diff --git a/Dockerfile b/Dockerfile index f813854..9d3fdf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# Use Node.js v24 as the base image +# Use Node.js v25 as the base image FROM node:25-alpine # Set working directory diff --git a/README.md b/README.md index 0db30d6..b2668d7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # cql-tests-runner +[![Website](https://shields.foundry.hl7.org/website?url=https%3A%2F%2Fcql-tests-runner.quality.hl7.org&logo=fireship&label=try%20it%20now)](https://cql-tests-runner.quality.hl7.org) +[![GitHub contributors](https://shields.foundry.hl7.org/github/contributors/cqframework/cql-tests-runner?logo=github)](https://github.com/cqframework/cql-tests-runner/graphs/contributors) +[![GitHub last commit](https://shields.foundry.hl7.org/github/last-commit/cqframework/cql-tests-runner?logo=github)](https://github.com/cqframework/cql-tests-runner/graphs/commit-activity) +[![GitHub top language](https://shields.foundry.hl7.org/github/languages/top/cqframework/cql-tests-runner?logo=github)](https://github.com/cqframework/cql-tests-runner) +[![Docker automated build](https://shields.foundry.hl7.org/docker/automated/hlseven/quality-cql-tests-runner?logo=docker)](https://hub.docker.com/r/hlseven/quality-cql-tests-runner) +[![Docker pulls](https://shields.foundry.hl7.org/docker/pulls/hlseven/quality-cql-tests-runner?logo=docker)](https://hub.docker.com/r/hlseven/quality-cql-tests-runner) +[![Docker image size](https://shields.foundry.hl7.org/docker/image-size/hlseven/quality-cql-tests-runner?logo=docker)](https://hub.docker.com/r/hlseven/quality-cql-tests-runner) + + Test Runner for the [CQL Tests](https://github.com/cqframework/cql-tests) repository. This node application allows you to run the tests in the CQL Tests repository against a server of your choice using the [$cql](https://hl7.org/fhir/uv/cql/OperationDefinition-cql-cql.html) operation. The runner in its current state uses only this operation, and there is no expectation of any other FHIR server capability made by this runner. Additional capabilities may be required in the future as we expand the runner to support full Library/$evaluate as well. None of the tests in the repository have any expectation of being able to access data (i.e. the tests have no retrieve expressions). The application runs all the tests in the repository and outputs the results as a JSON file in the `results` directory. If the output directory does not exist, it will be created. @@ -8,11 +17,11 @@ Results output from running these tests can be posted to the [CQL Tests Results] ## Setting up the Environment -This application requires Node v24 and makes use of the [Axios](https://axios-http.com/docs/intro) framework for HTTP request/response processing. [Node Download](https://nodejs.org/en/download) +This application requires Node v25 and makes use of the [Axios](https://axios-http.com/docs/intro) framework for HTTP request/response processing. [Node Download](https://nodejs.org/en/download) Install application dependencies using -``` +```sh npm install ``` diff --git a/package-lock.json b/package-lock.json index b9f5af9..bd606ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,25 @@ { "name": "cql-tests-runner", - "version": "1.4.0", + "version": "1.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cql-tests-runner", - "version": "1.4.0", + "version": "1.6.0", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.25.3", "@types/uuid": "^11.0.0", "antlr4": "4.13.2", - "axios": "^1.13.3", + "axios": "^1.13.4", "colors": "^1.4.0", "commander": "^14.0.2", "config": "^4.2.0", "cors": "^2.8.6", "date-fns": "^4.1.0", "express": "^5.2.1", - "fast-xml-parser": "^5.3.3", + "fast-xml-parser": "^5.3.4", "uuid": "^13.0.0", "zod": "^4.3.6", "zod-from-json-schema": "^0.5.2" @@ -31,29 +31,15 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.6", "@types/fhir": "^0.0.41", - "@types/node": "^25.0.10", + "@types/node": "^25.1.0", "@types/supertest": "^6.0.3", "prettier": "^3.8.1", "supertest": "^7.2.2", - "ts-node": "^10.9.2", "tsx": "^4.21.0", "typescript": "^5.9.3", "vitest": "^4.0.18" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -508,16 +494,6 @@ "hono": "^4" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -525,17 +501,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.25.3", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz", @@ -955,34 +920,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -1100,9 +1037,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", - "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", + "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", "dev": true, "license": "MIT", "dependencies": { @@ -1324,32 +1261,6 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -1390,13 +1301,6 @@ "node": ">=16" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -1421,9 +1325,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.3.tgz", - "integrity": "sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -1617,13 +1521,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1694,16 +1591,6 @@ "wrappy": "1" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1980,9 +1867,9 @@ "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, "node_modules/fast-xml-parser": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz", - "integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", + "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", "funding": [ { "type": "github", @@ -2342,13 +2229,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3076,50 +2956,6 @@ "node": ">=0.6" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/tsx": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", @@ -3222,13 +3058,6 @@ "uuid": "dist-node/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3428,16 +3257,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", diff --git a/package.json b/package.json index 334f53c..1fcf2a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cql-tests-runner", - "version": "1.5.0", + "version": "1.6.0", "description": "Server API and command line tools for running CQL tests", "type": "module", "main": "dist/bin/cql-tests.js", @@ -26,14 +26,14 @@ "@modelcontextprotocol/sdk": "^1.25.3", "@types/uuid": "^11.0.0", "antlr4": "4.13.2", - "axios": "^1.13.3", + "axios": "^1.13.4", "colors": "^1.4.0", "commander": "^14.0.2", "config": "^4.2.0", "cors": "^2.8.6", "date-fns": "^4.1.0", "express": "^5.2.1", - "fast-xml-parser": "^5.3.3", + "fast-xml-parser": "^5.3.4", "uuid": "^13.0.0", "zod": "^4.3.6", "zod-from-json-schema": "^0.5.2" @@ -42,11 +42,10 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.6", "@types/fhir": "^0.0.41", - "@types/node": "^25.0.10", + "@types/node": "^25.1.0", "@types/supertest": "^6.0.3", "prettier": "^3.8.1", "supertest": "^7.2.2", - "ts-node": "^10.9.2", "tsx": "^4.21.0", "typescript": "^5.9.3", "vitest": "^4.0.18" diff --git a/src/cql-engine/cql-engine.ts b/src/cql-engine/cql-engine.ts index 7754784..27c6787 100644 --- a/src/cql-engine/cql-engine.ts +++ b/src/cql-engine/cql-engine.ts @@ -44,13 +44,21 @@ export class CQLEngine { * Creates an instance of CQLEngine. * @param baseURL - The base URL for the CQL engine. * @param cqlPath - The path for the CQL engine (optional). + * @param cqlTranslator - CQL translator name (optional, from config Build). + * @param cqlTranslatorVersion - CQL translator version (optional). + * @param cqlEngine - CQL engine name (optional). + * @param cqlEngineVersion - CQL engine version (optional). */ - constructor(baseURL: string, cqlPath: string | null = null, - cqlTranslator: string, cqlTranslatorVersion: string, - cqlEngine: string, cqlEngineVersion: string) { + constructor( + baseURL: string, + cqlPath: string | null = null, + cqlTranslator: string = '', + cqlTranslatorVersion: string = '', + cqlEngine: string = '', + cqlEngineVersion: string = '' + ) { this._prepareBaseURL(baseURL, cqlPath); - this._setInformationFields(cqlTranslator, cqlTranslatorVersion, - cqlEngine, cqlEngineVersion); + this._setInformationFields(cqlTranslator, cqlTranslatorVersion, cqlEngine, cqlEngineVersion); } /** diff --git a/src/models/config-types.ts b/src/models/config-types.ts index 6cf9b3c..0a62be7 100644 --- a/src/models/config-types.ts +++ b/src/models/config-types.ts @@ -16,7 +16,11 @@ export interface Config { CqlFileVersion: string; CqlOutputPath: string; CqlVersion?: string; - testsRunDescription?: string; // Note: schema has this misplaced but it's used in code + testsRunDescription?: string; + cqlTranslator?: string; + cqlTranslatorVersion?: string; + cqlEngine?: string; + cqlEngineVersion?: string; }; Tests: { ResultsPath: string; diff --git a/src/server/test-execution-service.ts b/src/server/test-execution-service.ts index 5733322..bd0b195 100644 --- a/src/server/test-execution-service.ts +++ b/src/server/test-execution-service.ts @@ -1,3 +1,5 @@ +// Author: Preston Lee + import { ConfigLoader } from '../conf/config-loader.js'; import { CQLEngine } from '../cql-engine/cql-engine.js'; import { CQLTestResults } from '../test-results/cql-test-results.js'; @@ -7,17 +9,55 @@ import { generateParametersResource, Result, } from '../shared/results-shared.js'; -import { InternalTestResult } from '../models/test-types.js'; +import { InternalTestResult, Tests } from '../models/test-types.js'; import { ServerConnectivity } from '../shared/server-connectivity.js'; import { ResultExtractor } from '../extractors/result-extractor.js'; import { buildExtractor } from './extractor-builder.js'; import { createConfigFromData } from './config-utils.js'; import { resultsEqual } from '../shared/results-utils.js'; -// Type declaration for CVL loader -declare const cvlLoader: () => Promise<[{ default: any }]>; +interface ExecutionContext { + config: ConfigLoader; + cqlEngine: CQLEngine; + cvl: any; + tests: Tests[]; + resultExtractor: ResultExtractor; + skipMap: Map; +} export class TestExecutionService { + /** + * Builds shared execution context from config data (engine, CVL, tests, extractor, skip map). + */ + private async createExecutionContext(configData: any): Promise { + const config = createConfigFromData(configData); + const serverBaseUrl = config.FhirServer.BaseUrl; + const cqlEndpoint = config.CqlEndpoint; + + await ServerConnectivity.verifyServerConnectivity(serverBaseUrl); + + const build = config.Build; + const cqlEngine = new CQLEngine( + serverBaseUrl, + cqlEndpoint, + build?.cqlTranslator ?? '', + build?.cqlTranslatorVersion ?? '', + build?.cqlEngine ?? '', + build?.cqlEngineVersion ?? '' + ); + cqlEngine.cqlVersion = config.Build?.CqlVersion || '1.5'; + + // @ts-expect-error - cvl.mjs has no declaration file + const cvlModule = await import('../../cvl/cvl.mjs'); + const cvl = cvlModule.default; + + const tests = TestLoader.load(); + const resultExtractor = buildExtractor(); + const skipMap = config.skipListMap(); + + return { config, cqlEngine, cvl, tests, resultExtractor, skipMap }; + } + /** * Runs a single test */ @@ -86,28 +126,11 @@ export class TestExecutionService { * Runs all tests based on configuration */ async runTests(configData: any): Promise { - // Create a temporary config loader from the provided data - const config = createConfigFromData(configData); - const serverBaseUrl = config.FhirServer.BaseUrl; - const cqlEndpoint = config.CqlEndpoint; + const ctx = await this.createExecutionContext(configData); + const { config, cqlEngine, cvl, tests, resultExtractor, skipMap } = ctx; - // Verify server connectivity before proceeding - await ServerConnectivity.verifyServerConnectivity(serverBaseUrl); - - const cqlEngine = new CQLEngine(serverBaseUrl, cqlEndpoint); - cqlEngine.cqlVersion = config.Build?.CqlVersion || '1.5'; - - // Load CVL using dynamic import - // @ts-ignore - const cvlModule = await import('../../cvl/cvl.mjs'); - const cvl = cvlModule.default; - - const tests = TestLoader.load(); const quickTest = config.Debug?.QuickTest || false; - const resultExtractor = buildExtractor(); const emptyResults = await generateEmptyResults(tests, quickTest); - const skipMap = config.skipListMap(); - const results = new CQLTestResults(cqlEngine); for (const testFile of emptyResults) { @@ -117,7 +140,6 @@ export class TestExecutionService { } } - // Return the results data that would normally be written to file return results.toJSON(); } @@ -130,50 +152,22 @@ export class TestExecutionService { testName: string, configData: any ): Promise { - const config = createConfigFromData(configData); - const serverBaseUrl = config.FhirServer.BaseUrl; - const cqlEndpoint = config.CqlEndpoint; - - await ServerConnectivity.verifyServerConnectivity(serverBaseUrl); + const ctx = await this.createExecutionContext(configData); + const { config, cqlEngine, cvl, tests, resultExtractor, skipMap } = ctx; - const cqlEngine = new CQLEngine(serverBaseUrl, cqlEndpoint); - cqlEngine.cqlVersion = config.Build?.CqlVersion || '1.5'; - - // @ts-ignore - const cvlModule = await import('../../cvl/cvl.mjs'); - const cvl = cvlModule.default; - - const tests = TestLoader.load(); - const resultExtractor = buildExtractor(); - const skipMap = config.skipListMap(); - - // Find the specific test for (const testSuite of tests) { - if (testSuite.name === testsName) { - for (const group of testSuite.group) { - if (group.name === groupName && group.test) { - for (const test of group.test) { - if (test.name === testName) { - const result = new Result(testsName, groupName, test); - await this.runTest( - result, - cqlEngine.apiUrl!, - cvl, - resultExtractor, - skipMap, - config - ); - - // Convert to schema-compliant format - const testResults = new CQLTestResults(cqlEngine); - testResults.add(result); - const jsonResults = testResults.toJSON(); - - // Return just the single test result - return jsonResults.results[0] || null; - } - } - } + if (testSuite.name !== testsName) continue; + for (const group of testSuite.group) { + if (group.name !== groupName || !group.test) continue; + for (const test of group.test) { + if (test.name !== testName) continue; + + const result = new Result(testsName, groupName, test); + await this.runTest(result, cqlEngine.apiUrl!, cvl, resultExtractor, skipMap, config); + + const testResults = new CQLTestResults(cqlEngine); + testResults.add(result); + return testResults.toJSON().results[0] ?? null; } } } @@ -189,50 +183,24 @@ export class TestExecutionService { groupName: string, configData: any ): Promise { - const config = createConfigFromData(configData); - const serverBaseUrl = config.FhirServer.BaseUrl; - const cqlEndpoint = config.CqlEndpoint; - - await ServerConnectivity.verifyServerConnectivity(serverBaseUrl); - - const cqlEngine = new CQLEngine(serverBaseUrl, cqlEndpoint); - cqlEngine.cqlVersion = config.Build?.CqlVersion || '1.5'; - - // @ts-ignore - const cvlModule = await import('../../cvl/cvl.mjs'); - const cvl = cvlModule.default; - - const tests = TestLoader.load(); - const resultExtractor = buildExtractor(); - const skipMap = config.skipListMap(); + const ctx = await this.createExecutionContext(configData); + const { config, cqlEngine, cvl, tests, resultExtractor, skipMap } = ctx; const results = new CQLTestResults(cqlEngine); - // Find and run tests in the specified group for (const testSuite of tests) { - if (testSuite.name === testsName) { - for (const group of testSuite.group) { - if (group.name === groupName && group.test) { - for (const test of group.test) { - const result = new Result(testsName, groupName, test); - await this.runTest( - result, - cqlEngine.apiUrl!, - cvl, - resultExtractor, - skipMap, - config - ); - results.add(result); - } - break; - } + if (testSuite.name !== testsName) continue; + for (const group of testSuite.group) { + if (group.name !== groupName || !group.test) continue; + for (const test of group.test) { + const result = new Result(testsName, groupName, test); + await this.runTest(result, cqlEngine.apiUrl!, cvl, resultExtractor, skipMap, config); + results.add(result); } - break; + return results.toJSON().results; } } - const jsonResults = results.toJSON(); - return jsonResults.results; + return results.toJSON().results; } } diff --git a/src/services/test-runner.ts b/src/services/test-runner.ts index 2d6727a..056fa5a 100644 --- a/src/services/test-runner.ts +++ b/src/services/test-runner.ts @@ -28,14 +28,15 @@ export class TestRunner { // Verify server connectivity before proceeding await ServerConnectivity.verifyServerConnectivity(serverBaseUrl); + const build = config.Build; const cqlEngine = new CQLEngine( - serverBaseUrl, - cqlEndpoint, - configData.Build.cqlTranslator, - configData.Build.cqlTranslatorVersion, - configData.Build.cqlEngine, - configData.Build.cqlEngineVersion - ); + serverBaseUrl, + cqlEndpoint, + build.cqlTranslator ?? '', + build.cqlTranslatorVersion ?? '', + build.cqlEngine ?? '', + build.cqlEngineVersion ?? '' + ); cqlEngine.cqlVersion = '1.5'; //default value const cqlVersion = config.Build?.CqlVersion; if (typeof cqlVersion === 'string' && cqlVersion.trim() !== '') {