@holokai/holo-test 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +217 -80
- package/dist/conformance/audit-tests.d.ts +4 -0
- package/dist/conformance/audit-tests.d.ts.map +1 -0
- package/dist/conformance/audit-tests.js +55 -0
- package/dist/conformance/audit-tests.js.map +1 -0
- package/dist/conformance/datastore-tests.d.ts +13 -0
- package/dist/conformance/datastore-tests.d.ts.map +1 -0
- package/dist/conformance/datastore-tests.js +121 -0
- package/dist/conformance/datastore-tests.js.map +1 -0
- package/dist/conformance/holo-fixture-loader.d.ts +4 -0
- package/dist/conformance/holo-fixture-loader.d.ts.map +1 -0
- package/dist/conformance/holo-fixture-loader.js +79 -0
- package/dist/conformance/holo-fixture-loader.js.map +1 -0
- package/dist/conformance/holo-fixture-tests.d.ts +4 -0
- package/dist/conformance/holo-fixture-tests.d.ts.map +1 -0
- package/dist/conformance/holo-fixture-tests.js +273 -0
- package/dist/conformance/holo-fixture-tests.js.map +1 -0
- package/dist/conformance/holo-fixture-types.d.ts +93 -0
- package/dist/conformance/holo-fixture-types.d.ts.map +1 -0
- package/dist/conformance/holo-fixture-types.js +2 -0
- package/dist/conformance/holo-fixture-types.js.map +1 -0
- package/dist/conformance/loader.d.ts +7 -0
- package/dist/conformance/loader.d.ts.map +1 -0
- package/dist/conformance/loader.js +71 -0
- package/dist/conformance/loader.js.map +1 -0
- package/dist/conformance/pipeline-tests.d.ts +4 -0
- package/dist/conformance/pipeline-tests.d.ts.map +1 -0
- package/dist/conformance/pipeline-tests.js +73 -0
- package/dist/conformance/pipeline-tests.js.map +1 -0
- package/dist/conformance/runner.d.ts +2 -0
- package/dist/conformance/runner.d.ts.map +1 -0
- package/dist/conformance/runner.js +102 -0
- package/dist/conformance/runner.js.map +1 -0
- package/dist/conformance/sdk-tests.d.ts +12 -0
- package/dist/conformance/sdk-tests.d.ts.map +1 -0
- package/dist/{services/http-fixture-server.js → conformance/sdk-tests.js} +56 -17
- package/dist/conformance/sdk-tests.js.map +1 -0
- package/dist/conformance/translator-tests.d.ts +4 -0
- package/dist/conformance/translator-tests.d.ts.map +1 -0
- package/dist/conformance/translator-tests.js +49 -0
- package/dist/conformance/translator-tests.js.map +1 -0
- package/dist/conformance/types.d.ts +81 -0
- package/dist/conformance/types.d.ts.map +1 -0
- package/dist/conformance/types.js +2 -0
- package/dist/conformance/types.js.map +1 -0
- package/dist/conformance/wire-tests.d.ts +4 -0
- package/dist/conformance/wire-tests.d.ts.map +1 -0
- package/dist/conformance/wire-tests.js +52 -0
- package/dist/conformance/wire-tests.js.map +1 -0
- package/dist/fixtures/plugin.d.ts +27 -0
- package/dist/fixtures/plugin.d.ts.map +1 -0
- package/dist/fixtures/plugin.js +110 -0
- package/dist/fixtures/plugin.js.map +1 -0
- package/dist/helpers/env.d.ts +2 -0
- package/dist/helpers/env.d.ts.map +1 -1
- package/dist/helpers/env.js +5 -0
- package/dist/helpers/env.js.map +1 -1
- package/dist/helpers/ids.d.ts +2 -0
- package/dist/helpers/ids.d.ts.map +1 -0
- package/dist/helpers/ids.js +5 -0
- package/dist/helpers/ids.js.map +1 -0
- package/dist/helpers/matrix.d.ts +9 -1
- package/dist/helpers/matrix.d.ts.map +1 -1
- package/dist/helpers/matrix.js +8 -3
- package/dist/helpers/matrix.js.map +1 -1
- package/dist/helpers/mocks.d.ts +4 -0
- package/dist/helpers/mocks.d.ts.map +1 -0
- package/dist/helpers/mocks.js +37 -0
- package/dist/helpers/mocks.js.map +1 -0
- package/dist/helpers/polling.d.ts +3 -0
- package/dist/helpers/polling.d.ts.map +1 -0
- package/dist/helpers/polling.js +24 -0
- package/dist/helpers/polling.js.map +1 -0
- package/dist/helpers/vitest-helpers.d.ts +1 -1
- package/dist/helpers/vitest-helpers.d.ts.map +1 -1
- package/dist/index.d.ts +26 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -21
- package/dist/index.js.map +1 -1
- package/dist/live/assertions.d.ts.map +1 -1
- package/dist/live/assertions.js +54 -21
- package/dist/live/assertions.js.map +1 -1
- package/dist/live/error-payloads.d.ts +5 -0
- package/dist/live/error-payloads.d.ts.map +1 -0
- package/dist/live/error-payloads.js +47 -0
- package/dist/live/error-payloads.js.map +1 -0
- package/dist/live/error-tester.d.ts +3 -0
- package/dist/live/error-tester.d.ts.map +1 -0
- package/dist/live/error-tester.js +121 -0
- package/dist/live/error-tester.js.map +1 -0
- package/dist/live/index.d.ts +3 -1
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js +2 -0
- package/dist/live/index.js.map +1 -1
- package/dist/live/live-tester.d.ts.map +1 -1
- package/dist/live/live-tester.js +17 -3
- package/dist/live/live-tester.js.map +1 -1
- package/dist/live/types.d.ts +9 -0
- package/dist/live/types.d.ts.map +1 -1
- package/dist/services/plugin-loader.d.ts +4 -2
- package/dist/services/plugin-loader.d.ts.map +1 -1
- package/dist/services/plugin-loader.js +14 -2
- package/dist/services/plugin-loader.js.map +1 -1
- package/dist/types.d.ts +0 -37
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -6
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -83
- package/dist/cli.js.map +0 -1
- package/dist/contracts/audit-contract.d.ts +0 -3
- package/dist/contracts/audit-contract.d.ts.map +0 -1
- package/dist/contracts/audit-contract.js +0 -11
- package/dist/contracts/audit-contract.js.map +0 -1
- package/dist/contracts/pipeline-contract.d.ts +0 -3
- package/dist/contracts/pipeline-contract.d.ts.map +0 -1
- package/dist/contracts/pipeline-contract.js +0 -9
- package/dist/contracts/pipeline-contract.js.map +0 -1
- package/dist/contracts/roundtrip-contract.d.ts +0 -3
- package/dist/contracts/roundtrip-contract.d.ts.map +0 -1
- package/dist/contracts/roundtrip-contract.js +0 -9
- package/dist/contracts/roundtrip-contract.js.map +0 -1
- package/dist/contracts/wire-contract.d.ts +0 -3
- package/dist/contracts/wire-contract.d.ts.map +0 -1
- package/dist/contracts/wire-contract.js +0 -9
- package/dist/contracts/wire-contract.js.map +0 -1
- package/dist/dsl/assertions.d.ts +0 -6
- package/dist/dsl/assertions.d.ts.map +0 -1
- package/dist/dsl/assertions.js +0 -54
- package/dist/dsl/assertions.js.map +0 -1
- package/dist/dsl/suite-builder.d.ts +0 -25
- package/dist/dsl/suite-builder.d.ts.map +0 -1
- package/dist/dsl/suite-builder.js +0 -96
- package/dist/dsl/suite-builder.js.map +0 -1
- package/dist/factories/worker-request-factory.d.ts +0 -5
- package/dist/factories/worker-request-factory.d.ts.map +0 -1
- package/dist/factories/worker-request-factory.js +0 -19
- package/dist/factories/worker-request-factory.js.map +0 -1
- package/dist/fixtures/fixture-loader.d.ts +0 -6
- package/dist/fixtures/fixture-loader.d.ts.map +0 -1
- package/dist/fixtures/fixture-loader.js +0 -23
- package/dist/fixtures/fixture-loader.js.map +0 -1
- package/dist/runner/test-reporter.d.ts +0 -3
- package/dist/runner/test-reporter.d.ts.map +0 -1
- package/dist/runner/test-reporter.js +0 -43
- package/dist/runner/test-reporter.js.map +0 -1
- package/dist/runner/test-runner.d.ts +0 -12
- package/dist/runner/test-runner.d.ts.map +0 -1
- package/dist/runner/test-runner.js +0 -37
- package/dist/runner/test-runner.js.map +0 -1
- package/dist/services/http-fixture-server.d.ts +0 -14
- package/dist/services/http-fixture-server.d.ts.map +0 -1
- package/dist/services/http-fixture-server.js.map +0 -1
- package/dist/testers/audit-tester.d.ts +0 -4
- package/dist/testers/audit-tester.d.ts.map +0 -1
- package/dist/testers/audit-tester.js +0 -74
- package/dist/testers/audit-tester.js.map +0 -1
- package/dist/testers/pipeline-tester.d.ts +0 -4
- package/dist/testers/pipeline-tester.d.ts.map +0 -1
- package/dist/testers/pipeline-tester.js +0 -62
- package/dist/testers/pipeline-tester.js.map +0 -1
- package/dist/testers/roundtrip-tester.d.ts +0 -3
- package/dist/testers/roundtrip-tester.d.ts.map +0 -1
- package/dist/testers/roundtrip-tester.js +0 -77
- package/dist/testers/roundtrip-tester.js.map +0 -1
- package/dist/testers/wire-tester.d.ts +0 -4
- package/dist/testers/wire-tester.d.ts.map +0 -1
- package/dist/testers/wire-tester.js +0 -87
- package/dist/testers/wire-tester.js.map +0 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
function holoFixtureRoot() {
|
|
7
|
+
const pkgRoot = dirname(require.resolve('@holokai/holo-test/package.json'));
|
|
8
|
+
return join(pkgRoot, 'holo');
|
|
9
|
+
}
|
|
10
|
+
function pluginRoot(family) {
|
|
11
|
+
return dirname(require.resolve(`@holokai/holo-provider-${family}/package.json`));
|
|
12
|
+
}
|
|
13
|
+
const KIND_DIRS = ['request', 'response', 'streaming'];
|
|
14
|
+
export async function loadHoloFixtures() {
|
|
15
|
+
const root = holoFixtureRoot();
|
|
16
|
+
if (!(await exists(root)))
|
|
17
|
+
return [];
|
|
18
|
+
const fixtures = [];
|
|
19
|
+
for (const kind of KIND_DIRS) {
|
|
20
|
+
const kindPath = join(root, kind);
|
|
21
|
+
if (!(await exists(kindPath)))
|
|
22
|
+
continue;
|
|
23
|
+
const files = await readdir(kindPath);
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
if (!file.endsWith('.holo.ts') && !file.endsWith('.holo.js'))
|
|
26
|
+
continue;
|
|
27
|
+
const fullPath = join(kindPath, file);
|
|
28
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
29
|
+
const fixture = mod.default ?? mod.fixture;
|
|
30
|
+
if (!fixture) {
|
|
31
|
+
throw new Error(`Holo fixture ${fullPath} has no default export`);
|
|
32
|
+
}
|
|
33
|
+
const expectedId = file.replace(/\.holo\.(ts|js)$/, '');
|
|
34
|
+
const expectedFullId = `${kind}.${expectedId}`;
|
|
35
|
+
if (fixture.id !== expectedFullId) {
|
|
36
|
+
throw new Error(`Holo fixture ${fullPath} declares id "${fixture.id}" but filename implies "${expectedFullId}".`);
|
|
37
|
+
}
|
|
38
|
+
if (fixture.kind !== kind) {
|
|
39
|
+
throw new Error(`Holo fixture ${fullPath} declares kind "${fixture.kind}" but is filed under "${kind}/".`);
|
|
40
|
+
}
|
|
41
|
+
fixtures.push(fixture);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
fixtures.sort((a, b) => a.id.localeCompare(b.id));
|
|
45
|
+
return fixtures;
|
|
46
|
+
}
|
|
47
|
+
export async function loadPluginHoloScenarios(family) {
|
|
48
|
+
const dataRoot = join(pluginRoot(family), 'tests', 'data', 'holo');
|
|
49
|
+
if (!(await exists(dataRoot)))
|
|
50
|
+
return [];
|
|
51
|
+
const files = await readdir(dataRoot);
|
|
52
|
+
const scenarios = [];
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
if (!file.endsWith('.scenario.ts') && !file.endsWith('.scenario.js'))
|
|
55
|
+
continue;
|
|
56
|
+
const fullPath = join(dataRoot, file);
|
|
57
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
58
|
+
const scenario = mod.default ?? mod.scenario;
|
|
59
|
+
if (!scenario) {
|
|
60
|
+
throw new Error(`Plugin Holo scenario ${fullPath} has no default export`);
|
|
61
|
+
}
|
|
62
|
+
const expectedFilename = `${scenario.holoId}.scenario.ts`;
|
|
63
|
+
if (file !== expectedFilename && file !== expectedFilename.replace(/\.ts$/, '.js')) {
|
|
64
|
+
throw new Error(`Plugin Holo scenario ${fullPath} declares holoId "${scenario.holoId}" but filename is "${file}".`);
|
|
65
|
+
}
|
|
66
|
+
scenarios.push(scenario);
|
|
67
|
+
}
|
|
68
|
+
return scenarios;
|
|
69
|
+
}
|
|
70
|
+
async function exists(path) {
|
|
71
|
+
try {
|
|
72
|
+
await stat(path);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=holo-fixture-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holo-fixture-loader.js","sourceRoot":"","sources":["../../src/conformance/holo-fixture-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IProviderTranslator } from '@holokai/holo-types/provider';
|
|
2
|
+
import type { HoloFixture, PluginHoloScenario } from './holo-fixture-types';
|
|
3
|
+
export declare function registerHoloFixtureTests(fixture: HoloFixture, pluginScenarios: Map<string, PluginHoloScenario>, translatorsByFamily: Map<string, IProviderTranslator>): void;
|
|
4
|
+
//# sourceMappingURL=holo-fixture-tests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holo-fixture-tests.d.ts","sourceRoot":"","sources":["../../src/conformance/holo-fixture-tests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,8BAA8B,CAAC;AAGtE,OAAO,KAAK,EACR,WAAW,EAIX,kBAAkB,EAErB,MAAM,sBAAsB,CAAC;AAyT9B,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,WAAW,EACpB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAChD,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,GACtD,IAAI,CAmDN"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { collectAsync, toAsyncIter } from '@holokai/holo-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Strip per-translator pass-through metadata. Applied on every comparison
|
|
5
|
+
* (per-plugin round-trip and cross-provider) because `delta.provider` and
|
|
6
|
+
* `delta.provider_delta` are scratch fields the source translator uses to
|
|
7
|
+
* carry forward provider-native nuance for lossless same-provider round-trip;
|
|
8
|
+
* they are not part of the canonical Holo pivot.
|
|
9
|
+
*/
|
|
10
|
+
function stripProviderMetadata(chunks) {
|
|
11
|
+
return chunks.map(chunk => {
|
|
12
|
+
if (!chunk || typeof chunk !== 'object')
|
|
13
|
+
return chunk;
|
|
14
|
+
const rest = { ...chunk };
|
|
15
|
+
if ('delta' in rest && rest.delta && typeof rest.delta === 'object') {
|
|
16
|
+
const { provider: _p, provider_delta: _pd, ...restDelta } = rest.delta;
|
|
17
|
+
rest.delta = restDelta;
|
|
18
|
+
}
|
|
19
|
+
return rest;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Strip volatile fields that destination translators legitimately need to
|
|
24
|
+
* synthesize during a cross-provider stream pivot when the source chunks
|
|
25
|
+
* don't carry them: top-level `id` / `created` / `model` and message-level
|
|
26
|
+
* `id` / `model`. Applied ONLY to cross tests — per-plugin (same-provider in/out)
|
|
27
|
+
* round-trip stays strict on these fields, so a translator that drops or
|
|
28
|
+
* fabricates them surfaces immediately.
|
|
29
|
+
*/
|
|
30
|
+
function stripCrossVolatile(chunks) {
|
|
31
|
+
return chunks.map(chunk => {
|
|
32
|
+
if (!chunk || typeof chunk !== 'object')
|
|
33
|
+
return chunk;
|
|
34
|
+
const { id: _id, created: _created, model: _model, ...rest } = chunk;
|
|
35
|
+
if ('message' in rest && rest.message && typeof rest.message === 'object') {
|
|
36
|
+
const { id: _mid, model: _mmodel, ...restMsg } = rest.message;
|
|
37
|
+
rest.message = restMsg;
|
|
38
|
+
}
|
|
39
|
+
return rest;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function buildExpectedRequest(fixture, scenario) {
|
|
43
|
+
return {
|
|
44
|
+
...fixture.holo,
|
|
45
|
+
...(scenario.holoOverrides ?? {}),
|
|
46
|
+
model: scenario.modelOverride,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function buildExpectedResponse(fixture, scenario) {
|
|
50
|
+
return {
|
|
51
|
+
...fixture.holo,
|
|
52
|
+
...(scenario.holoOverrides ?? {}),
|
|
53
|
+
model: scenario.modelOverride,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function pickIt(failure, throwsExpected) {
|
|
57
|
+
if (throwsExpected)
|
|
58
|
+
return it;
|
|
59
|
+
return failure ? it.fails : it;
|
|
60
|
+
}
|
|
61
|
+
function failureReason(ef) {
|
|
62
|
+
return ef?.reason ? ` (expected fail: ${ef.reason})` : ' (expected fail)';
|
|
63
|
+
}
|
|
64
|
+
function computeRoundTripFailing(ef, toHoloFailing, fromHoloFailing) {
|
|
65
|
+
if (ef?.roundTrip === true)
|
|
66
|
+
return true;
|
|
67
|
+
if (ef?.roundTrip === false)
|
|
68
|
+
return false;
|
|
69
|
+
return toHoloFailing || fromHoloFailing;
|
|
70
|
+
}
|
|
71
|
+
function registerRequestPerPlugin(family, fixture, scenario, translator) {
|
|
72
|
+
const expected = buildExpectedRequest(fixture, scenario);
|
|
73
|
+
const ef = scenario.expectedFailure;
|
|
74
|
+
const toHoloFailing = ef?.toHolo === true;
|
|
75
|
+
const fromHoloFailing = ef?.fromHolo === true;
|
|
76
|
+
const roundTripFailing = computeRoundTripFailing(ef, toHoloFailing, fromHoloFailing);
|
|
77
|
+
const fromHoloThrows = scenario.throws?.fromHolo === true;
|
|
78
|
+
const toHoloThrows = scenario.throws?.toHolo === true;
|
|
79
|
+
const toHoloLabel = `${family}: toHolo${toHoloFailing ? failureReason(ef) : ''}`;
|
|
80
|
+
pickIt(toHoloFailing, toHoloThrows)(toHoloLabel, async () => {
|
|
81
|
+
if (toHoloThrows) {
|
|
82
|
+
await expect(translator.toHoloRequest(scenario.native)).rejects.toThrow();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const result = await translator.toHoloRequest(scenario.native);
|
|
86
|
+
expect(result).toEqual(expected);
|
|
87
|
+
});
|
|
88
|
+
const fromHoloLabel = `${family}: fromHolo${fromHoloFailing ? failureReason(ef) : ''}`;
|
|
89
|
+
pickIt(fromHoloFailing, fromHoloThrows)(fromHoloLabel, async () => {
|
|
90
|
+
if (fromHoloThrows) {
|
|
91
|
+
await expect(translator.fromHoloRequest(expected)).rejects.toThrow();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const result = await translator.fromHoloRequest(expected);
|
|
95
|
+
expect(result).toEqual(scenario.native);
|
|
96
|
+
});
|
|
97
|
+
const rtLabel = `${family}: round-trip${roundTripFailing ? failureReason(ef) : ''}`;
|
|
98
|
+
pickIt(roundTripFailing, toHoloThrows || fromHoloThrows)(rtLabel, async () => {
|
|
99
|
+
if (toHoloThrows || fromHoloThrows) {
|
|
100
|
+
// Round-trip starts from native; if toHolo throws, that's the throw point.
|
|
101
|
+
await expect((async () => {
|
|
102
|
+
const intermediate = await translator.toHoloRequest(scenario.native);
|
|
103
|
+
return translator.fromHoloRequest(intermediate);
|
|
104
|
+
})()).rejects.toThrow();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const intermediate = await translator.toHoloRequest(scenario.native);
|
|
108
|
+
const back = await translator.fromHoloRequest(intermediate);
|
|
109
|
+
expect(back).toEqual(scenario.native);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function registerResponsePerPlugin(family, fixture, scenario, translator) {
|
|
113
|
+
const expected = buildExpectedResponse(fixture, scenario);
|
|
114
|
+
const ef = scenario.expectedFailure;
|
|
115
|
+
const toHoloFailing = ef?.toHolo === true;
|
|
116
|
+
const fromHoloFailing = ef?.fromHolo === true;
|
|
117
|
+
const roundTripFailing = computeRoundTripFailing(ef, toHoloFailing, fromHoloFailing);
|
|
118
|
+
pickIt(toHoloFailing, false)(`${family}: toHolo${toHoloFailing ? failureReason(ef) : ''}`, async () => {
|
|
119
|
+
const result = await translator.toHoloResponse(scenario.native);
|
|
120
|
+
expect(result).toEqual(expected);
|
|
121
|
+
});
|
|
122
|
+
pickIt(fromHoloFailing, false)(`${family}: fromHolo${fromHoloFailing ? failureReason(ef) : ''}`, async () => {
|
|
123
|
+
const result = await translator.fromHoloResponse(expected);
|
|
124
|
+
expect(result).toEqual(scenario.native);
|
|
125
|
+
});
|
|
126
|
+
pickIt(roundTripFailing, false)(`${family}: round-trip${roundTripFailing ? failureReason(ef) : ''}`, async () => {
|
|
127
|
+
const intermediate = await translator.toHoloResponse(scenario.native);
|
|
128
|
+
const back = await translator.fromHoloResponse(intermediate);
|
|
129
|
+
expect(back).toEqual(scenario.native);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function registerStreamingPerPlugin(family, fixture, scenario, translator) {
|
|
133
|
+
const ef = scenario.expectedFailure;
|
|
134
|
+
const toHoloFailing = ef?.toHolo === true;
|
|
135
|
+
const fromHoloFailing = ef?.fromHolo === true;
|
|
136
|
+
const supportsToHolo = typeof translator.toHoloStream === 'function';
|
|
137
|
+
if (supportsToHolo) {
|
|
138
|
+
pickIt(toHoloFailing, false)(`${family}: toHoloStream${toHoloFailing ? failureReason(ef) : ''}`, async () => {
|
|
139
|
+
const native = scenario.native;
|
|
140
|
+
const result = await collectAsync(translator.toHoloStream(toAsyncIter(native)));
|
|
141
|
+
expect(stripProviderMetadata(result)).toEqual(stripProviderMetadata(fixture.holo));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Skip — plugin hasn't implemented toHoloStream. Cross tests where
|
|
146
|
+
// this plugin is the source will also skip via the same check.
|
|
147
|
+
}
|
|
148
|
+
pickIt(fromHoloFailing, false)(`${family}: fromHoloStream${fromHoloFailing ? failureReason(ef) : ''}`, async () => {
|
|
149
|
+
const result = await collectAsync(translator.fromHoloStream(toAsyncIter(fixture.holo)));
|
|
150
|
+
expect(stripProviderMetadata(result)).toEqual(stripProviderMetadata(scenario.native));
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function registerRequestCross(srcFamily, dstFamily, _fixture, srcScenario, dstScenario, srcTranslator, dstTranslator) {
|
|
154
|
+
// For request/response cross, inherit per-plugin expectedFailure flags —
|
|
155
|
+
// most translators are strict on the Holo shape they consume, so a lossy
|
|
156
|
+
// src.toHolo or dst.fromHolo will break the chain. Streaming cross does
|
|
157
|
+
// NOT inherit (translators are flexible about chunk-type nomenclature).
|
|
158
|
+
const inheritedFailure = srcScenario.expectedFailure?.toHolo === true ||
|
|
159
|
+
dstScenario.expectedFailure?.fromHolo === true;
|
|
160
|
+
const inheritedThrow = dstScenario.throws?.fromHolo === true;
|
|
161
|
+
const reasonParts = [];
|
|
162
|
+
if (srcScenario.expectedFailure?.toHolo)
|
|
163
|
+
reasonParts.push(`src.toHolo: ${srcScenario.expectedFailure.reason ?? 'lossy'}`);
|
|
164
|
+
if (dstScenario.expectedFailure?.fromHolo)
|
|
165
|
+
reasonParts.push(`dst.fromHolo: ${dstScenario.expectedFailure.reason ?? 'lossy'}`);
|
|
166
|
+
const reason = reasonParts.length ? ` (expected fail: ${reasonParts.join('; ')})` : '';
|
|
167
|
+
const label = `${srcFamily} → ${dstFamily}${inheritedFailure ? reason : ''}`;
|
|
168
|
+
pickIt(inheritedFailure, inheritedThrow)(label, async () => {
|
|
169
|
+
const rawHolo = await srcTranslator.toHoloRequest(srcScenario.native);
|
|
170
|
+
// Mimic gateway routing: rewrite model + apply destination's plugin-level
|
|
171
|
+
// overrides (e.g., max_tokens defaults) before handing off to dst.fromHolo.
|
|
172
|
+
const holo = {
|
|
173
|
+
...rawHolo,
|
|
174
|
+
...(dstScenario.holoOverrides ?? {}),
|
|
175
|
+
model: dstScenario.modelOverride,
|
|
176
|
+
};
|
|
177
|
+
if (inheritedThrow) {
|
|
178
|
+
await expect(dstTranslator.fromHoloRequest(holo)).rejects.toThrow();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const result = await dstTranslator.fromHoloRequest(holo);
|
|
182
|
+
expect(result).toEqual(dstScenario.native);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function registerResponseCross(srcFamily, dstFamily, _fixture, srcScenario, dstScenario, srcTranslator, dstTranslator) {
|
|
186
|
+
const srcCrossFailing = srcScenario.expectedFailure?.crossAsSrc !== undefined
|
|
187
|
+
? srcScenario.expectedFailure.crossAsSrc
|
|
188
|
+
: srcScenario.expectedFailure?.toHolo === true;
|
|
189
|
+
const inheritedFailure = srcCrossFailing ||
|
|
190
|
+
dstScenario.expectedFailure?.fromHolo === true;
|
|
191
|
+
const reasonParts = [];
|
|
192
|
+
if (srcCrossFailing)
|
|
193
|
+
reasonParts.push(`src.toHolo: ${srcScenario.expectedFailure?.reason ?? 'lossy'}`);
|
|
194
|
+
if (dstScenario.expectedFailure?.fromHolo)
|
|
195
|
+
reasonParts.push(`dst.fromHolo: ${dstScenario.expectedFailure.reason ?? 'lossy'}`);
|
|
196
|
+
const reason = reasonParts.length ? ` (expected fail: ${reasonParts.join('; ')})` : '';
|
|
197
|
+
const label = `${srcFamily} → ${dstFamily}${inheritedFailure ? reason : ''}`;
|
|
198
|
+
pickIt(inheritedFailure, false)(label, async () => {
|
|
199
|
+
const rawHolo = await srcTranslator.toHoloResponse(srcScenario.native);
|
|
200
|
+
const holo = {
|
|
201
|
+
...rawHolo,
|
|
202
|
+
...(dstScenario.holoOverrides ?? {}),
|
|
203
|
+
model: dstScenario.modelOverride,
|
|
204
|
+
};
|
|
205
|
+
const result = await dstTranslator.fromHoloResponse(holo);
|
|
206
|
+
expect(result).toEqual(dstScenario.native);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function registerStreamingCross(srcFamily, dstFamily, _fixture, srcScenario, dstScenario, srcTranslator, dstTranslator) {
|
|
210
|
+
const expectedFail = srcScenario.expectedFailure?.crossStreamingTo?.includes(dstFamily) === true;
|
|
211
|
+
const reason = expectedFail
|
|
212
|
+
? failureReason(srcScenario.expectedFailure)
|
|
213
|
+
: '';
|
|
214
|
+
const label = `${srcFamily} → ${dstFamily}${reason}`;
|
|
215
|
+
if (typeof srcTranslator.toHoloStream !== 'function') {
|
|
216
|
+
it.skip(`${label} (src missing toHoloStream)`, () => {
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
pickIt(expectedFail, false)(label, async () => {
|
|
221
|
+
const holo = await collectAsync(srcTranslator.toHoloStream(toAsyncIter(srcScenario.native)));
|
|
222
|
+
const result = await collectAsync(dstTranslator.fromHoloStream(toAsyncIter(holo)));
|
|
223
|
+
expect(stripCrossVolatile(stripProviderMetadata(result)))
|
|
224
|
+
.toEqual(stripCrossVolatile(stripProviderMetadata(dstScenario.native)));
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
export function registerHoloFixtureTests(fixture, pluginScenarios, translatorsByFamily) {
|
|
228
|
+
describe(`holo: ${fixture.id}`, () => {
|
|
229
|
+
// Per-plugin tests
|
|
230
|
+
for (const [family, scenario] of pluginScenarios) {
|
|
231
|
+
const translator = translatorsByFamily.get(family);
|
|
232
|
+
if (!translator)
|
|
233
|
+
continue;
|
|
234
|
+
describe(family, () => {
|
|
235
|
+
if (fixture.kind === 'request') {
|
|
236
|
+
registerRequestPerPlugin(family, fixture, scenario, translator);
|
|
237
|
+
}
|
|
238
|
+
else if (fixture.kind === 'response') {
|
|
239
|
+
registerResponsePerPlugin(family, fixture, scenario, translator);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
registerStreamingPerPlugin(family, fixture, scenario, translator);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
// Cross tests: every (src, dst) pair with both natives, src ≠ dst
|
|
247
|
+
const families = [...pluginScenarios.keys()];
|
|
248
|
+
if (families.length >= 2) {
|
|
249
|
+
describe('cross', () => {
|
|
250
|
+
for (const srcFamily of families) {
|
|
251
|
+
for (const dstFamily of families) {
|
|
252
|
+
if (srcFamily === dstFamily)
|
|
253
|
+
continue;
|
|
254
|
+
const srcScenario = pluginScenarios.get(srcFamily);
|
|
255
|
+
const dstScenario = pluginScenarios.get(dstFamily);
|
|
256
|
+
const srcTranslator = translatorsByFamily.get(srcFamily);
|
|
257
|
+
const dstTranslator = translatorsByFamily.get(dstFamily);
|
|
258
|
+
if (fixture.kind === 'request') {
|
|
259
|
+
registerRequestCross(srcFamily, dstFamily, fixture, srcScenario, dstScenario, srcTranslator, dstTranslator);
|
|
260
|
+
}
|
|
261
|
+
else if (fixture.kind === 'response') {
|
|
262
|
+
registerResponseCross(srcFamily, dstFamily, fixture, srcScenario, dstScenario, srcTranslator, dstTranslator);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
registerStreamingCross(srcFamily, dstFamily, fixture, srcScenario, dstScenario, srcTranslator, dstTranslator);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=holo-fixture-tests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holo-fixture-tests.js","sourceRoot":"","sources":["../../src/conformance/holo-fixture-tests.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { HoloRequest, HoloResponse, HoloStreamChunk } from '@holokai/holo-types/holo';
|
|
2
|
+
export type HoloFixtureKind = 'request' | 'response' | 'streaming';
|
|
3
|
+
export interface HoloFixtureBase {
|
|
4
|
+
/** Stable identifier. Filename must equal `${id}.holo.ts`. */
|
|
5
|
+
id: string;
|
|
6
|
+
kind: HoloFixtureKind;
|
|
7
|
+
/** One-sentence description of what the shape pins. */
|
|
8
|
+
description: string;
|
|
9
|
+
/** Tags for filtering/grouping tests (optional). */
|
|
10
|
+
tags?: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface HoloRequestFixture extends HoloFixtureBase {
|
|
13
|
+
kind: 'request';
|
|
14
|
+
/** The pivot HoloRequest. `model` is omitted; plugin scenarios provide it via modelOverride. */
|
|
15
|
+
holo: Omit<HoloRequest, 'model'>;
|
|
16
|
+
}
|
|
17
|
+
export interface HoloResponseFixture extends HoloFixtureBase {
|
|
18
|
+
kind: 'response';
|
|
19
|
+
/** The pivot HoloResponse. `id` and `model` are omitted; plugin scenarios provide them. */
|
|
20
|
+
holo: Omit<HoloResponse, 'id' | 'model'>;
|
|
21
|
+
}
|
|
22
|
+
export interface HoloStreamingFixture extends HoloFixtureBase {
|
|
23
|
+
kind: 'streaming';
|
|
24
|
+
/** The pivot HoloStreamChunk[] — the full stream the framework expects. */
|
|
25
|
+
holo: HoloStreamChunk[];
|
|
26
|
+
}
|
|
27
|
+
export type HoloFixture = HoloRequestFixture | HoloResponseFixture | HoloStreamingFixture;
|
|
28
|
+
export interface PluginHoloScenarioExpectedFailure {
|
|
29
|
+
/** Plugin's `toHolo(native)` doesn't reproduce the Holo fixture shape. */
|
|
30
|
+
toHolo?: boolean;
|
|
31
|
+
/** Plugin's `fromHolo(fixture)` doesn't reproduce the expected native shape. */
|
|
32
|
+
fromHolo?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Round-trip semantics:
|
|
35
|
+
* - `true` — explicitly pin the round-trip as expected-fail.
|
|
36
|
+
* - `false` — explicitly opt OUT of the auto-pin that would otherwise fire when
|
|
37
|
+
* `toHolo` or `fromHolo` is `true`. Use this when the per-side comparison
|
|
38
|
+
* against the canonical fixture loses fields the native shape never carried,
|
|
39
|
+
* but native → Holo → native still reproduces the native shape verbatim
|
|
40
|
+
* (e.g., Claude lacking `frequency_penalty`: fromHolo silently drops it,
|
|
41
|
+
* matching scenario.native; toHolo can't reconstruct it from a wire that
|
|
42
|
+
* never had it; round-trip from native is lossless).
|
|
43
|
+
* - `undefined` — auto-pin if either side is pinned.
|
|
44
|
+
*/
|
|
45
|
+
roundTrip?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Streaming cross-provider routing into named destination families is lossy.
|
|
48
|
+
* Listed when this scenario is the SOURCE: the cross test `thisFamily → dstFamily`
|
|
49
|
+
* is marked `it.fails`. Used when an asymmetry in how providers index content
|
|
50
|
+
* blocks vs tool calls leaks across the boundary (e.g., Claude → OpenAI for
|
|
51
|
+
* multi-block streams).
|
|
52
|
+
*/
|
|
53
|
+
crossStreamingTo?: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Response cross matrix override when this scenario is the SOURCE:
|
|
56
|
+
* - `true` — explicitly pin the cross-as-src direction as expected-fail.
|
|
57
|
+
* - `false` — explicitly opt OUT of the auto-pin that would otherwise fire
|
|
58
|
+
* when `toHolo` is `true`. Use when the per-plugin `toHolo` loss is
|
|
59
|
+
* structurally recovered by the cross framework (e.g., Bedrock's response
|
|
60
|
+
* wire has no `model` field, but the cross logic restamps
|
|
61
|
+
* `model: dstScenario.modelOverride` before handing off to dst.fromHolo).
|
|
62
|
+
* - `undefined` — inherit from `toHolo`.
|
|
63
|
+
*/
|
|
64
|
+
crossAsSrc?: boolean;
|
|
65
|
+
/** Human-readable explanation; surfaced in test names. */
|
|
66
|
+
reason?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface PluginHoloScenarioThrows {
|
|
69
|
+
/** fromHolo throws (e.g., Bedrock throws on URL image). */
|
|
70
|
+
fromHolo?: boolean;
|
|
71
|
+
/** toHolo throws (rare). */
|
|
72
|
+
toHolo?: boolean;
|
|
73
|
+
}
|
|
74
|
+
export interface PluginHoloScenario<TNative = unknown> {
|
|
75
|
+
/** Must match a Holo fixture's `id`. Filename must be `${holoId}.scenario.ts`. */
|
|
76
|
+
holoId: string;
|
|
77
|
+
/** Provider protocol (e.g., 'claude.messages'). */
|
|
78
|
+
protocol: string;
|
|
79
|
+
/** Model name to inject into the fixture Holo shape before comparison. */
|
|
80
|
+
modelOverride: string;
|
|
81
|
+
/**
|
|
82
|
+
* Extra Holo fields the plugin always emits (e.g., max_tokens defaults).
|
|
83
|
+
* Merged into the fixture Holo before comparison.
|
|
84
|
+
*/
|
|
85
|
+
holoOverrides?: Record<string, unknown>;
|
|
86
|
+
/** Native shape the plugin produces / consumes for this Holo fixture. */
|
|
87
|
+
native: TNative;
|
|
88
|
+
/** Pin known losses per-direction. The framework propagates these to cross tests. */
|
|
89
|
+
expectedFailure?: PluginHoloScenarioExpectedFailure;
|
|
90
|
+
/** Throws on a specific direction. Cross tests inherit destination's fromHolo throw. */
|
|
91
|
+
throws?: PluginHoloScenarioThrows;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=holo-fixture-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holo-fixture-types.d.ts","sourceRoot":"","sources":["../../src/conformance/holo-fixture-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,YAAY,EAAE,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAEzF,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC5B,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,eAAe,CAAC;IACtB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACvD,IAAI,EAAE,SAAS,CAAC;IAChB,gGAAgG;IAChG,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IACxD,IAAI,EAAE,UAAU,CAAC;IACjB,2FAA2F;IAC3F,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IACzD,IAAI,EAAE,WAAW,CAAC;IAClB,2EAA2E;IAC3E,IAAI,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,WAAW,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,oBAAoB,CAAC;AAE3B,MAAM,WAAW,iCAAiC;IAC9C,0EAA0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACrC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB,CAAC,OAAO,GAAG,OAAO;IACjD,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAC;IAChB,qFAAqF;IACrF,eAAe,CAAC,EAAE,iCAAiC,CAAC;IACpD,wFAAwF;IACxF,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holo-fixture-types.js","sourceRoot":"","sources":["../../src/conformance/holo-fixture-types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HoloScenario } from './types';
|
|
2
|
+
import type { ScenarioSdkAdapter } from './sdk-tests';
|
|
3
|
+
import type { DatastoreConfig } from './datastore-tests';
|
|
4
|
+
export declare function loadSdkAdapter(family: string): Promise<ScenarioSdkAdapter | undefined>;
|
|
5
|
+
export declare function loadDatastoreConfig(family: string): Promise<DatastoreConfig | undefined>;
|
|
6
|
+
export declare function loadScenarios(family: string): Promise<HoloScenario[]>;
|
|
7
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/conformance/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAYvD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAK5F;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAK9F;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAsC3E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
function pluginRoot(family) {
|
|
7
|
+
return dirname(require.resolve(`@holokai/holo-provider-${family}/package.json`));
|
|
8
|
+
}
|
|
9
|
+
function datastorePluginRoot(family) {
|
|
10
|
+
return dirname(require.resolve(`@holokai/holo-datastore-${family}/package.json`));
|
|
11
|
+
}
|
|
12
|
+
export async function loadSdkAdapter(family) {
|
|
13
|
+
const adapterPath = join(pluginRoot(family), 'tests', 'sdk-adapter.ts');
|
|
14
|
+
if (!(await exists(adapterPath)))
|
|
15
|
+
return undefined;
|
|
16
|
+
const mod = await import(pathToFileURL(adapterPath).href);
|
|
17
|
+
return mod.default ?? mod.adapter;
|
|
18
|
+
}
|
|
19
|
+
export async function loadDatastoreConfig(family) {
|
|
20
|
+
const configPath = join(datastorePluginRoot(family), 'tests', 'datastore-config.ts');
|
|
21
|
+
if (!(await exists(configPath)))
|
|
22
|
+
return undefined;
|
|
23
|
+
const mod = await import(pathToFileURL(configPath).href);
|
|
24
|
+
return mod.default ?? mod.config;
|
|
25
|
+
}
|
|
26
|
+
export async function loadScenarios(family) {
|
|
27
|
+
const dataRoot = join(pluginRoot(family), 'tests', 'data');
|
|
28
|
+
if (!(await exists(dataRoot)))
|
|
29
|
+
return [];
|
|
30
|
+
const protocolDirs = await readdir(dataRoot, { withFileTypes: true });
|
|
31
|
+
const scenarios = [];
|
|
32
|
+
for (const protoDir of protocolDirs) {
|
|
33
|
+
if (!protoDir.isDirectory())
|
|
34
|
+
continue;
|
|
35
|
+
// Skip the Holo-fixture bucket — it's handled by loadPluginHoloScenarios.
|
|
36
|
+
if (protoDir.name === 'holo')
|
|
37
|
+
continue;
|
|
38
|
+
const protocolPath = join(dataRoot, protoDir.name);
|
|
39
|
+
const files = await readdir(protocolPath);
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
if (!file.endsWith('.scenario.ts') && !file.endsWith('.scenario.js'))
|
|
42
|
+
continue;
|
|
43
|
+
const fullPath = join(protocolPath, file);
|
|
44
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
45
|
+
const scenario = mod.default ?? mod.scenario;
|
|
46
|
+
if (!scenario) {
|
|
47
|
+
throw new Error(`Scenario file ${fullPath} has no default export`);
|
|
48
|
+
}
|
|
49
|
+
if (scenario.protocol !== protoDir.name) {
|
|
50
|
+
throw new Error(`Scenario ${fullPath} declares protocol "${scenario.protocol}" but is filed under directory "${protoDir.name}"`);
|
|
51
|
+
}
|
|
52
|
+
scenarios.push(scenario);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
scenarios.sort((a, b) => {
|
|
56
|
+
if (a.protocol !== b.protocol)
|
|
57
|
+
return a.protocol.localeCompare(b.protocol);
|
|
58
|
+
return a.name.localeCompare(b.name);
|
|
59
|
+
});
|
|
60
|
+
return scenarios;
|
|
61
|
+
}
|
|
62
|
+
async function exists(path) {
|
|
63
|
+
try {
|
|
64
|
+
await stat(path);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/conformance/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IProviderPlugin } from '@holokai/holo-types/plugin';
|
|
2
|
+
import type { HoloScenario } from './types';
|
|
3
|
+
export declare function registerPipelineTests(family: string, plugin: IProviderPlugin, scenario: HoloScenario): void;
|
|
4
|
+
//# sourceMappingURL=pipeline-tests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-tests.d.ts","sourceRoot":"","sources":["../../src/conformance/pipeline-tests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,4BAA4B,CAAC;AAMhE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,SAAS,CAAC;AAE1C,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAmF3G"}
|