Skip to content

Commit fbbdb4b

Browse files
committed
Support Long results returned as valueString in FHIR R4
CQL Long expected values (e.g. 100L) parse to BigInt, but FHIR R4 has no integer64 type, so servers return Long results as valueString. The result comparison had no bigint handling, so many CqlArithmeticFunctionsTest tests failed despite correct server output. Compare an expected BigInt numerically against string, integer, and bigint actuals, rejecting non-integer strings so empty or malformed values don't match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M6fK2KhnNsn28SJfe83Htw
1 parent 39d289e commit fbbdb4b

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

src/shared/results-utils.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/**
2+
* Compares an expected CQL Long (parsed to a BigInt by cvl) against the actual value.
3+
* FHIR R4 has no integer64 type, so servers return Long results as valueString
4+
* (or valueInteger when the value fits), and the extracted actual is a string or number.
5+
*/
6+
function longEquals(expected: bigint, actual: any): boolean {
7+
if (typeof actual === 'bigint') {
8+
return expected === actual;
9+
}
10+
if (typeof actual === 'string') {
11+
// BigInt('') is 0n and BigInt trims whitespace, so require an integer literal.
12+
if (!/^[+-]?\d+$/.test(actual)) {
13+
return false;
14+
}
15+
return BigInt(actual) === expected;
16+
}
17+
if (typeof actual === 'number' && Number.isInteger(actual)) {
18+
return BigInt(actual) === expected;
19+
}
20+
return false;
21+
}
22+
123
/**
224
* Compares two results for equality, handling nested objects and numbers
325
*/
@@ -14,6 +36,10 @@ export function resultsEqual(expected: any, actual: any): boolean {
1436
return Math.abs(actual - expected) < 0.00000001;
1537
}
1638

39+
if (typeof expected === 'bigint') {
40+
return longEquals(expected, actual);
41+
}
42+
1743
if (expected === actual) {
1844
return true;
1945
}

test/results-utils.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,33 @@ test('equal lists (order-insensitive)', () => {
1717
test('nested structures compared key-wise', () => {
1818
expect(resultsEqual({ x: 1 }, { x: 1 })).toBe(true);
1919
});
20+
21+
test('expected Long matches FHIR R4 valueString encoding', () => {
22+
expect(resultsEqual(1n, '1')).toBe(true);
23+
expect(resultsEqual(-1n, '-1')).toBe(true);
24+
expect(resultsEqual(0n, '0')).toBe(true);
25+
expect(resultsEqual(9223372036854775807n, '9223372036854775807')).toBe(true);
26+
expect(resultsEqual(1n, '2')).toBe(false);
27+
});
28+
29+
test('expected Long matches integer and bigint actuals', () => {
30+
expect(resultsEqual(1n, 1)).toBe(true);
31+
expect(resultsEqual(1n, 1n)).toBe(true);
32+
expect(resultsEqual(1n, 2)).toBe(false);
33+
expect(resultsEqual(1n, 1.5)).toBe(false);
34+
});
35+
36+
test('expected Long rejects non-integer strings', () => {
37+
expect(resultsEqual(1n, 'abc')).toBe(false);
38+
expect(resultsEqual(1n, '1.5')).toBe(false);
39+
expect(resultsEqual(0n, '')).toBe(false);
40+
expect(resultsEqual(0n, ' 0 ')).toBe(false);
41+
expect(resultsEqual(1n, null)).toBe(false);
42+
expect(resultsEqual(1n, undefined)).toBe(false);
43+
});
44+
45+
test('Long values compare inside lists and structures', () => {
46+
expect(resultsEqual([1n, 2n], ['1', '2'])).toBe(true);
47+
expect(resultsEqual({ x: 1n }, { x: '1' })).toBe(true);
48+
expect(resultsEqual([1n, 2n], ['1', '3'])).toBe(false);
49+
});

0 commit comments

Comments
 (0)