Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 176 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ Configuration settings are set in a JSON configuration file. The file `conf/loca
"CqlOperation": "$cql"
},
"Build": {
"CqlFileVersion": "1.0.000",
"CqlOutputPath": "./cql",
"testsRunDescription": '',
"testsRunDescription": "Local host test run",
"cqlTranslator": "Java CQFramework Translator",
"cqlTranslatorVersion": "Unknown",
"cqlEngine": "Java CQFramework Engine",
"cqlEngineVersion": "4.1.0"
},
"CqlFileVersion": "1.0.000",
"CqlOutputPath": "./cql",
"CqlVersion": "1.5",
"testsRunDescription": "Local host test run",
"cqlTranslator": "Java CQFramework Translator",
"cqlTranslatorVersion": "Unknown",
"cqlEngine": "Java CQFramework Engine",
"cqlEngineVersion": "4.1.0",
"SERVER_OFFSET_ISO": "-06:00",
"TimeZoneOffsetPolicy": "timezone-offset-policy.default-server-offset"
},
"Tests": {
"ResultsPath": "./results",
"SkipList": []
Expand All @@ -56,6 +58,171 @@ Configuration settings are set in a JSON configuration file. The file `conf/loca

Create your own configuration file and reference it when running the commands. You can use `conf/localhost.json` as a template for a new configuration with your own settings.

### Time Zone Configuration

The CQL Tests Runner uses two settings to control how **DateTime** values are evaluated:

- `SERVER_OFFSET_ISO`
- `TimeZoneOffsetPolicy`

These settings are required because CQL allows **DateTime values without a timezone offset**, and different engines interpret those values differently. Without explicitly setting these, tests involving DateTime comparison and extraction may produce inconsistent results (pass, fail, or null).

Reference: http://cql.hl7.org/CodeSystem/cql-language-capabilities

---

#### TimeZoneOffsetPolicy (what it means)

# 🔄 Timezone Offset Policy

## Background

CQL allows `DateTime` literals to be specified **with or without a timezone offset**.

For example:

```
@2012-04-01T00:00
```

This value does **not include an explicit timezone offset**.

---

## What the CQL Specification Says

> If no timezone offset is specified, the timezone offset of the evaluation request timestamp is used.

This means:

- A `DateTime` literal may omit an offset in its **source representation**
- But at **evaluation time**, the engine must apply an offset
- That offset comes from the **evaluation request timestamp**

👉 In other words, under CQL semantics:

- DateTimes are **always evaluated with an effective timezone offset**
- The only question is **which offset is applied**

---

## Why This Capability Exists

Although the specification is clear, implementations have historically differed in how they handle `DateTime` values without explicit offsets.

Observed variations include:

- Applying the evaluation request timestamp offset (spec-compliant behavior)
- Treating the value as offset-less in some operations
- Returning `null` for operations like `timezoneoffset(...)`
- Applying server-local or implicit defaults inconsistently

This capability exists to explicitly test and document how an engine behaves in these scenarios.

---

## What Is Being Tested

This capability evaluates how an engine handles `DateTime` values that omit a timezone offset, including:

- Whether the engine applies the evaluation request timestamp offset
- How functions like `timezoneoffset(...)` behave
- Whether comparisons and arithmetic treat the value consistently

---

## Clarification

This capability does **not** test whether a `DateTime` “has a timezone or not.”

Per the CQL specification:

- A `DateTime` without an explicit offset is still evaluated **as if it has one**
- The offset is derived from the evaluation context (evaluation request timestamp)

👉 The purpose of this capability is to verify that engines implement this behavior **correctly and consistently**.

---

## Suggested Terminology

- ❌ “Does the DateTime have a timezone?”
- ✅ “How does the engine determine the effective timezone offset for DateTimes without an explicit offset?”

---

## Key Clarification

> This capability is testing **implementation consistency**, not specification ambiguity.


---

# 🔗 CapabilityTests Alignment & Mapping

## Capability Definition

- Capability: `timezone-offset-policy`
- Focus: Handling of DateTime values without explicit timezone offsets

---

## Test Mapping

### 1. Default Offset Application
- Tests: `timezone-offset-default`
- Verifies:
- Missing offset uses evaluation request timestamp
- No null offset produced

---

### 2. timezoneoffset(...) Behavior
- Tests: `timezoneoffset-from-datetime`
- Verifies:
- Returns evaluation offset when not explicitly provided

---

### 3. Equality / Comparison Consistency
- Tests: `datetime-equality-offset`
- Verifies:
- `@2012-04-01T00:00` equals equivalent explicit-offset value

---

### 4. Arithmetic Behavior
- Tests: `datetime-arithmetic-offset`
- Verifies:
- Duration calculations respect derived offset

---

## Reviewer Traceability

Spec → Behavior → Test

- Spec: Offset defaults to evaluation timestamp
- Behavior: Engine applies offset consistently
- Tests: Confirm consistency across functions and operators

---

## Summary

This capability ensures engines:

- Correctly apply implicit timezone offsets
- Do not treat DateTimes as offset-less at runtime
- Maintain consistency across operations


#### Notes

- These settings do not change server behavior; they only control how the runner evaluates tests
- If the server declares a policy in metadata, it overrides configuration
- `SERVER_OFFSET_ISO` is only used where explicitly referenced in test expressions

### Running the tests

The CLI now requires a configuration file path as an argument. Run the tests with the following commands:
Expand Down
78 changes: 43 additions & 35 deletions assets/schema/cql-test-configuration.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,41 +31,49 @@
"type": "object",
"description": "Build configuration settings",
"properties": {
"CqlFileVersion": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$",
"description": "CQL file version in semantic versioning format"
},
"CqlOutputPath": {
"type": "string",
"description": "Path where CQL files are output"
},
"CqlVersion": {
"type": "string",
"pattern": "^\\d+\\.\\d+(\\.\\d+)?$",
"description": "CQL engine version for version-based test filtering (optional)"
},
"testsRunDescription":{
"type": "string",
"description": "Information about this test run"
},
"cqlTranslator": {
"type": "string",
"description": "Which CQL Translator is used in this test run."
},
"cqlTranslatorVersion": {
"type": "string",
"description": "Which CQL translator version is used in this test run"
},
"cqlEngine": {
"type": "string",
"description": "Which CQL engine is used in this test run"
},
"cqlEngineVersion": {
"type": "string",
"description": "Which version of the CQL engine is used in this test run"
}
},
"CqlFileVersion": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$",
"description": "CQL file version in semantic versioning format"
},
"CqlOutputPath": {
"type": "string",
"description": "Path where CQL files are output"
},
"CqlVersion": {
"type": "string",
"pattern": "^\\d+\\.\\d+(\\.\\d+)?$",
"description": "CQL engine version for version-based test filtering (optional)"
},
"testsRunDescription": {
"type": "string",
"description": "Information about this test run"
},
"cqlTranslator": {
"type": "string",
"description": "Which CQL Translator is used in this test run."
},
"cqlTranslatorVersion": {
"type": "string",
"description": "Which CQL translator version is used in this test run"
},
"cqlEngine": {
"type": "string",
"description": "Which CQL engine is used in this test run"
},
"cqlEngineVersion": {
"type": "string",
"description": "Which version of the CQL engine is used in this test run"
},
"SERVER_OFFSET_ISO": {
"type": "string",
"description": "ISO 8601 formatted date string to offset the server start time"
},
"TimeZoneOffsetPolicy": {
"type": "string",
"description": "Which timezone offset policy is used in this test run."
}
},
"required": ["CqlFileVersion", "CqlOutputPath"],
"additionalProperties": false
},
Expand Down
18 changes: 10 additions & 8 deletions conf/localhost.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
"CqlOperation": "$cql"
},
"Build": {
"CqlFileVersion": "1.0.000",
"CqlOutputPath": "./cql",
"CqlVersion": "1.5",
"testsRunDescription": "Local host test run",
"cqlTranslator": "Java CQFramework Translator",
"cqlTranslatorVersion": "Unknown",
"cqlEngine": "Java CQFramework Engine",
"cqlEngineVersion": "4.1.0"
"CqlFileVersion": "1.0.000",
"CqlOutputPath": "./cql",
"CqlVersion": "1.5",
"testsRunDescription": "Local host test run",
"cqlTranslator": "Java CQFramework Translator",
"cqlTranslatorVersion": "Unknown",
"cqlEngine": "Java CQFramework Engine",
"cqlEngineVersion": "4.1.0",
"SERVER_OFFSET_ISO": "-06:00",
"TimeZoneOffsetPolicy": "timezone-offset-policy.default-server-offset"
},
"Debug": {
"QuickTest": false
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"dev": "tsc --watch",
"test": "vitest run test/",
"test:cql": "node --import tsx src/bin/cql-tests.ts run-tests conf/localhost.json ./results",
"cql-run-tests:dev": "tsx src/bin/cql-tests.ts run-tests conf/development.json ./results",
"cql-run-tests:local": "tsx src/bin/cql-tests.ts run-tests conf/localhost.json ./local_results",
"build-libs": "npm run build && node dist/bin/cql-tests.js build-cql conf/localhost.json ./cql",
"unit-tests": "vitest run test/",
"clean": "rm -rf dist",
Expand Down Expand Up @@ -51,4 +53,4 @@
"typescript": "^5.9.3",
"vitest": "^4.0.18"
}
}
}
11 changes: 8 additions & 3 deletions src/bin/cql-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ program
);
process.exit(1);
}
throw error;
console.error(error);
process.exit(1);
}
});

Expand Down Expand Up @@ -73,7 +74,8 @@ program
);
process.exit(1);
}
throw error;
console.error(error);
process.exit(1);
}
});

Expand All @@ -92,4 +94,7 @@ program
await serverCommand.start();
});

program.parse();
program.parseAsync().catch((error: any) => {
console.error(error);
process.exit(1);
});
6 changes: 4 additions & 2 deletions src/commands/run-tests-command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TestRunner } from '../services/test-runner.js';
import { TestRunner } from '../services/test-runner';
import { ConfigLoader } from '../conf/config-loader.js';

// Type declaration for CVL loader
Expand Down Expand Up @@ -54,7 +54,9 @@ export class RunCommand {
cqlTranslator: config.Build?.cqlTranslator,
cqlTranslatorVersion: config.Build?.cqlTranslatorVersion,
cqlEngine: config.Build?.cqlEngine,
cqlEngineVersion: config.Build?.cqlEngineVersion
cqlEngineVersion: config.Build?.cqlEngineVersion,
SERVER_OFFSET_ISO: config.Build?.SERVER_OFFSET_ISO,
TimeZoneOffsetPolicy: config.Build?.TimeZoneOffsetPolicy,
},
Tests: {
ResultsPath: config.Tests.ResultsPath,
Expand Down
Loading