From 3fefbf6c406185d2b4e0477d6f3fa71a549cb4d4 Mon Sep 17 00:00:00 2001 From: Kevin L'Hereec Date: Wed, 3 Jun 2026 08:56:12 +0200 Subject: [PATCH] test: 16 tests for typed compliance comparison (numeric, boolean, string) --- .../services/compliance_comparison.spec.ts | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 tests/unit/services/compliance_comparison.spec.ts diff --git a/tests/unit/services/compliance_comparison.spec.ts b/tests/unit/services/compliance_comparison.spec.ts new file mode 100644 index 0000000..1a94afd --- /dev/null +++ b/tests/unit/services/compliance_comparison.spec.ts @@ -0,0 +1,111 @@ +import { describe, it, expect } from 'vitest' + +/** + * Tests for the typed compliance comparison logic used in configuration_engine.ts + * Extracted here for clarity — the logic is: + * - numeric types (int, unsignedInt): compare as Number, fallback to String if NaN + * - boolean types: compare true/false/'true'/'false'/'1'/'0' + * - everything else: compare as String + */ + +function compareValues( + currentValue: any, + desiredValue: any, + desiredType: string +): boolean { + const isNumeric = + desiredType === 'unsignedInt' || + desiredType === 'int' || + desiredType === 'xsd:unsignedInt' || + desiredType === 'xsd:int' + const isBool = desiredType === 'boolean' || desiredType === 'xsd:boolean' + + if (isNumeric) { + const numCurrent = Number(currentValue) + const numDesired = Number(desiredValue) + return !isNaN(numCurrent) && !isNaN(numDesired) + ? numCurrent === numDesired + : String(currentValue) === String(desiredValue) + } else if (isBool) { + const toBool = (v: any) => v === true || v === 'true' || v === '1' + return toBool(currentValue) === toBool(desiredValue) + } else { + return String(currentValue) === String(desiredValue) + } +} + +describe('Compliance value comparison', () => { + describe('numeric types (unsignedInt, int)', () => { + it('60 (number) equals "60" (string)', () => { + expect(compareValues(60, '60', 'unsignedInt')).toBe(true) + }) + + it('"30" equals 30', () => { + expect(compareValues('30', 30, 'xsd:unsignedInt')).toBe(true) + }) + + it('0 equals "0"', () => { + expect(compareValues(0, '0', 'int')).toBe(true) + }) + + it('60 does not equal "90"', () => { + expect(compareValues(60, '90', 'unsignedInt')).toBe(false) + }) + + it('NaN values fallback to string comparison', () => { + expect(compareValues('abc', 'abc', 'xsd:int')).toBe(true) + expect(compareValues('abc', 'def', 'xsd:int')).toBe(false) + }) + }) + + describe('boolean types', () => { + it('true equals "true"', () => { + expect(compareValues(true, 'true', 'xsd:boolean')).toBe(true) + }) + + it('"1" equals true', () => { + expect(compareValues('1', true, 'boolean')).toBe(true) + }) + + it('false equals "false"', () => { + expect(compareValues(false, 'false', 'xsd:boolean')).toBe(true) + }) + + it('"0" equals false', () => { + expect(compareValues('0', false, 'boolean')).toBe(true) + }) + + it('true does not equal false', () => { + expect(compareValues(true, false, 'xsd:boolean')).toBe(false) + }) + + it('"1" does not equal "0"', () => { + expect(compareValues('1', '0', 'boolean')).toBe(false) + }) + }) + + describe('string types (default)', () => { + it('same strings match', () => { + expect(compareValues('hello', 'hello', 'xsd:string')).toBe(true) + }) + + it('different strings do not match', () => { + expect(compareValues('hello', 'world', 'string')).toBe(false) + }) + + it('number is converted to string for comparison', () => { + expect(compareValues(42, '42', 'xsd:string')).toBe(true) + }) + + it('null is converted to "null"', () => { + expect(compareValues(null, 'null', 'xsd:string')).toBe(true) + }) + }) + + describe('unknown/empty type defaults to string', () => { + it('empty type uses string comparison', () => { + expect(compareValues('60', '60', '')).toBe(true) + expect(compareValues(60, '60', '')).toBe(true) + }) + }) +})