@ontrails/testing 1.0.0-beta.15 → 1.0.0-beta.16

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/README.md +2 -2
  3. package/package.json +15 -7
  4. package/src/all.ts +7 -7
  5. package/src/context.ts +3 -0
  6. package/src/contracts.ts +1 -16
  7. package/src/examples.ts +19 -11
  8. package/src/index.ts +1 -1
  9. package/src/logger.ts +3 -1
  10. package/src/scenario.ts +3 -5
  11. package/src/signals.ts +221 -0
  12. package/src/types.ts +1 -1
  13. package/.turbo/turbo-build.log +0 -1
  14. package/.turbo/turbo-lint.log +0 -3
  15. package/.turbo/turbo-typecheck.log +0 -1
  16. package/dist/all.d.ts +0 -36
  17. package/dist/all.d.ts.map +0 -1
  18. package/dist/all.js +0 -99
  19. package/dist/all.js.map +0 -1
  20. package/dist/assertions.d.ts +0 -72
  21. package/dist/assertions.d.ts.map +0 -1
  22. package/dist/assertions.js +0 -238
  23. package/dist/assertions.js.map +0 -1
  24. package/dist/context.d.ts +0 -76
  25. package/dist/context.d.ts.map +0 -1
  26. package/dist/context.js +0 -118
  27. package/dist/context.js.map +0 -1
  28. package/dist/contracts.d.ts +0 -17
  29. package/dist/contracts.d.ts.map +0 -1
  30. package/dist/contracts.js +0 -75
  31. package/dist/contracts.js.map +0 -1
  32. package/dist/crosses.d.ts +0 -38
  33. package/dist/crosses.d.ts.map +0 -1
  34. package/dist/crosses.js +0 -223
  35. package/dist/crosses.js.map +0 -1
  36. package/dist/detours.d.ts +0 -13
  37. package/dist/detours.d.ts.map +0 -1
  38. package/dist/detours.js +0 -109
  39. package/dist/detours.js.map +0 -1
  40. package/dist/effective-examples.d.ts +0 -30
  41. package/dist/effective-examples.d.ts.map +0 -1
  42. package/dist/effective-examples.js +0 -227
  43. package/dist/effective-examples.js.map +0 -1
  44. package/dist/examples.d.ts +0 -23
  45. package/dist/examples.d.ts.map +0 -1
  46. package/dist/examples.js +0 -240
  47. package/dist/examples.js.map +0 -1
  48. package/dist/harness-cli.d.ts +0 -21
  49. package/dist/harness-cli.d.ts.map +0 -1
  50. package/dist/harness-cli.js +0 -205
  51. package/dist/harness-cli.js.map +0 -1
  52. package/dist/harness-mcp.d.ts +0 -21
  53. package/dist/harness-mcp.d.ts.map +0 -1
  54. package/dist/harness-mcp.js +0 -54
  55. package/dist/harness-mcp.js.map +0 -1
  56. package/dist/index.d.ts +0 -18
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js +0 -19
  59. package/dist/index.js.map +0 -1
  60. package/dist/logger.d.ts +0 -15
  61. package/dist/logger.d.ts.map +0 -1
  62. package/dist/logger.js +0 -87
  63. package/dist/logger.js.map +0 -1
  64. package/dist/scenario.d.ts +0 -37
  65. package/dist/scenario.d.ts.map +0 -1
  66. package/dist/scenario.js +0 -235
  67. package/dist/scenario.js.map +0 -1
  68. package/dist/trail.d.ts +0 -20
  69. package/dist/trail.d.ts.map +0 -1
  70. package/dist/trail.js +0 -80
  71. package/dist/trail.js.map +0 -1
  72. package/dist/types.d.ts +0 -113
  73. package/dist/types.d.ts.map +0 -1
  74. package/dist/types.js +0 -5
  75. package/dist/types.js.map +0 -1
  76. package/src/__tests__/all.test.ts +0 -254
  77. package/src/__tests__/context.test.ts +0 -170
  78. package/src/__tests__/contracts.test.ts +0 -239
  79. package/src/__tests__/crosses.test.ts +0 -587
  80. package/src/__tests__/detours.test.ts +0 -212
  81. package/src/__tests__/effective-examples.test.ts +0 -203
  82. package/src/__tests__/examples.test.ts +0 -636
  83. package/src/__tests__/harness-cli.test.ts +0 -90
  84. package/src/__tests__/harness-mcp.test.ts +0 -37
  85. package/src/__tests__/logger.test.ts +0 -136
  86. package/src/__tests__/partial-match.test.ts +0 -126
  87. package/src/__tests__/scenario.test.ts +0 -381
  88. package/src/__tests__/trail.test.ts +0 -99
  89. package/tsconfig.json +0 -9
  90. package/tsconfig.tests.json +0 -10
  91. package/tsconfig.tsbuildinfo +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAuB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAW9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAsCzD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,IAAI,EACT,eACI,OAAO,CAAC,YAAY,CAAC,GACrB,oBAAoB,GACpB,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,oBAAoB,CAAC,KACvD,IAkDF,CAAC"}
package/dist/contracts.js DELETED
@@ -1,75 +0,0 @@
1
- /**
2
- * testContracts — output schema verification.
3
- *
4
- * For every trail that has both examples and an output schema,
5
- * run each example and validate the implementation output against
6
- * the declared schema.
7
- */
8
- import { describe, test } from 'bun:test';
9
- import { executeTrail, formatZodIssues, validateInput } from '@ontrails/core';
10
- import { expectOk } from './assertions.js';
11
- import { mergeResourceOverrides, mergeTestContext, normalizeTestExecutionOptions, createMockResources, } from './context.js';
12
- import { deriveTrailExamples } from './effective-examples.js';
13
- // ---------------------------------------------------------------------------
14
- // Helpers
15
- // ---------------------------------------------------------------------------
16
- /** Check if a trail requires cross() but the context doesn't provide it. */
17
- const needsCrossContext = (t, resolveCtx) => {
18
- const spec = t;
19
- if (!spec.crosses || spec.crosses.length === 0) {
20
- return false;
21
- }
22
- return !normalizeTestExecutionOptions(resolveCtx()).ctx?.cross;
23
- };
24
- const validateOutputSchema = (outputSchema, value, trailId, exampleName) => {
25
- const parsed = outputSchema.safeParse(value);
26
- if (!parsed.success) {
27
- const issues = formatZodIssues(parsed.error.issues);
28
- throw new Error(`Output schema violation for trail "${trailId}", example "${exampleName}":\n${issues.map((i) => ` - ${i}`).join('\n')}\n\nActual output: ${JSON.stringify(value, null, 2)}`);
29
- }
30
- };
31
- // ---------------------------------------------------------------------------
32
- // testContracts
33
- // ---------------------------------------------------------------------------
34
- /**
35
- * Verify that every trail implementation output matches its declared
36
- * output schema. Catches implementation-schema drift.
37
- *
38
- * Trails without output schemas or examples are skipped.
39
- */
40
- export const testContracts = (app, ctxOrFactory) => {
41
- const resolveInput = typeof ctxOrFactory === 'function' ? ctxOrFactory : () => ctxOrFactory;
42
- const allEntries = app.list().map((trailDef) => ({
43
- ...trailDef,
44
- examples: deriveTrailExamples(trailDef),
45
- }));
46
- describe('contracts', () => {
47
- describe.each(allEntries)('$id', (t) => {
48
- if (t.output === undefined) {
49
- return;
50
- }
51
- if (t.examples.length === 0) {
52
- return;
53
- }
54
- if (needsCrossContext(t, resolveInput)) {
55
- return;
56
- }
57
- const { examples, output: outputSchema } = t;
58
- const successExamples = examples.filter((e) => e.error === undefined);
59
- test.each(successExamples)('contract: $name', async (example) => {
60
- const resolved = normalizeTestExecutionOptions(resolveInput());
61
- const resources = mergeResourceOverrides(await createMockResources(app), resolved.ctx, resolved.resources);
62
- const testCtx = mergeTestContext(resolved.ctx);
63
- const validated = validateInput(t.input, example.input);
64
- expectOk(validated);
65
- const result = await executeTrail(t, example.input, {
66
- ctx: testCtx,
67
- resources,
68
- });
69
- const resultValue = expectOk(result);
70
- validateOutputSchema(outputSchema, resultValue, t.id, example.name);
71
- });
72
- });
73
- });
74
- };
75
- //# sourceMappingURL=contracts.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
package/dist/crosses.d.ts DELETED
@@ -1,38 +0,0 @@
1
- /**
2
- * testCrosses — crossing-aware scenario testing for trails with crossings.
3
- *
4
- * Tests the crossing graph: which trails were crossed, in what order,
5
- * and supports failure injection from crossed trail examples.
6
- */
7
- import type { AnyTrail, ResourceOverrideMap, TrailContext } from '@ontrails/core';
8
- import type { CrossScenario } from './types.js';
9
- /** Options for testCrosses that provide trail definitions for injection. */
10
- export interface TestCrossOptions {
11
- /** Partial context overrides. */
12
- readonly ctx?: Partial<TrailContext> | undefined;
13
- /**
14
- * Explicit resource overrides merged on top of auto-resolved mocks for every
15
- * scenario. Values are passed by reference — provide immutable objects, or
16
- * use `mock()` on the resource definition to get a fresh instance per run.
17
- */
18
- readonly resources?: ResourceOverrideMap | undefined;
19
- /** Map of trail ID to trail definition, used for injectFromExample. */
20
- readonly trails?: ReadonlyMap<string, AnyTrail> | undefined;
21
- }
22
- /**
23
- * Generate a describe block for a trail with crossings with one test per scenario.
24
- *
25
- * @example
26
- * ```ts
27
- * testCrosses(onboardTrail, [
28
- * {
29
- * description: "crosses add then relate",
30
- * input: { name: "Alpha" },
31
- * expectOk: true,
32
- * expectCrossed: ["entity.add", "entity.relate"],
33
- * },
34
- * ]);
35
- * ```
36
- */
37
- export declare const testCrosses: (trailDef: AnyTrail, scenarios: readonly CrossScenario[], options?: TestCrossOptions) => void;
38
- //# sourceMappingURL=crosses.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crosses.d.ts","sourceRoot":"","sources":["../src/crosses.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,QAAQ,EAER,mBAAmB,EACnB,YAAY,EACb,MAAM,gBAAgB,CAAC;AAgBxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAmVhD,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;IACjD;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACrD,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC;CAC7D;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,GACtB,UAAU,QAAQ,EAClB,WAAW,SAAS,aAAa,EAAE,EACnC,UAAU,gBAAgB,KACzB,IAsBF,CAAC"}
package/dist/crosses.js DELETED
@@ -1,223 +0,0 @@
1
- /**
2
- * testCrosses — crossing-aware scenario testing for trails with crossings.
3
- *
4
- * Tests the crossing graph: which trails were crossed, in what order,
5
- * and supports failure injection from crossed trail examples.
6
- */
7
- import { describe, expect, test } from 'bun:test';
8
- import { buildCrossValidationSchema, executeTrail, InternalError, Result, ValidationError, validateInput, } from '@ontrails/core';
9
- import { assertErrorMatch, assertFullMatch, assertSchemaMatch, } from './assertions.js';
10
- import { mergeResourceOverrides, mergeTestContext } from './context.js';
11
- const collectDeclaredResources = (trailDef, trailsMap) => {
12
- const seenResourceIds = new Set();
13
- const seenTrailIds = new Set();
14
- const resources = [];
15
- const collect = (candidate) => {
16
- for (const declaredResource of candidate.resources) {
17
- if (seenResourceIds.has(declaredResource.id)) {
18
- continue;
19
- }
20
- seenResourceIds.add(declaredResource.id);
21
- resources.push(declaredResource);
22
- }
23
- };
24
- const visit = (candidate) => {
25
- if (seenTrailIds.has(candidate.id)) {
26
- return;
27
- }
28
- seenTrailIds.add(candidate.id);
29
- collect(candidate);
30
- for (const crossedId of candidate.crosses) {
31
- const crossedTrail = trailsMap?.get(crossedId);
32
- if (crossedTrail) {
33
- visit(crossedTrail);
34
- }
35
- }
36
- };
37
- visit(trailDef);
38
- return resources;
39
- };
40
- const resolveCrossMockResources = async (trailDef, trailsMap) => {
41
- const resources = {};
42
- for (const declaredResource of collectDeclaredResources(trailDef, trailsMap)) {
43
- if (!declaredResource.mock) {
44
- continue;
45
- }
46
- resources[declaredResource.id] = await declaredResource.mock();
47
- }
48
- return resources;
49
- };
50
- // ---------------------------------------------------------------------------
51
- // Injection helpers
52
- // ---------------------------------------------------------------------------
53
- /**
54
- * Find an error example on a trail by name or description substring.
55
- */
56
- const findErrorExample = (trailDef, description) => {
57
- const example = trailDef.examples?.find((ex) => ex.error !== undefined &&
58
- (ex.description?.includes(description) || ex.name.includes(description)));
59
- return example?.error;
60
- };
61
- /**
62
- * Try to inject an error from a crossed trail's example.
63
- * Returns undefined when no injection is configured for this trail ID.
64
- */
65
- const tryInjectError = (id, scenario, trailsMap) => {
66
- const injection = scenario.injectFromExample?.[id];
67
- if (injection === undefined) {
68
- return undefined;
69
- }
70
- const trailDef = trailsMap?.get(id);
71
- if (trailDef === undefined) {
72
- return Result.err(new InternalError(`Cannot inject: trail "${id}" not in topo`));
73
- }
74
- const errorName = findErrorExample(trailDef, injection);
75
- if (errorName === undefined) {
76
- return Result.err(new InternalError(`No error example matching "${injection}" on trail "${id}"`));
77
- }
78
- return Result.err(new Error(errorName));
79
- };
80
- const executeFromMap = (id, input, trailsMap, ctx, resources, cross) => {
81
- const trailDef = trailsMap?.get(id);
82
- if (trailDef === undefined) {
83
- return undefined;
84
- }
85
- const nestedCtx = cross ? { ...ctx, cross } : ctx;
86
- return executeTrail(trailDef, input, {
87
- ctx: nestedCtx,
88
- resources,
89
- validationSchema: buildCrossValidationSchema(trailDef),
90
- });
91
- };
92
- /** Extract trail ID from either a trail object or a string. */
93
- const resolveCrossId = (idOrTrail) => typeof idOrTrail === 'string' ? idOrTrail : idOrTrail.id;
94
- // ---------------------------------------------------------------------------
95
- // Cross factory
96
- // ---------------------------------------------------------------------------
97
- /** Delegate to baseCross, executeFromMap, or fall back to Result.ok(). */
98
- const delegateCross = (id, input, baseCross, trailsMap, ctx, resources, self) => {
99
- if (baseCross !== undefined) {
100
- return baseCross(id, input);
101
- }
102
- const executed = executeFromMap(id, input, trailsMap, ctx, resources, self);
103
- return Promise.resolve(executed ?? Result.ok());
104
- };
105
- /**
106
- * Build a recording cross function that optionally injects errors.
107
- */
108
- const createRecordingCross = (trace, scenario, trailsMap, baseCross, ctx, resources) => {
109
- // The generic O on CrossFn is erased at runtime; the cast is safe
110
- // because callers narrow via isOk/isErr before accessing the value.
111
- const invokeCross = async (idOrTrail, input, self) => {
112
- const id = resolveCrossId(idOrTrail);
113
- trace.push({ id, input });
114
- const injected = tryInjectError(id, scenario, trailsMap);
115
- if (injected !== undefined) {
116
- return injected;
117
- }
118
- return await delegateCross(id, input, baseCross, trailsMap, ctx, resources, self);
119
- };
120
- // Accepts either a trail object (typed cross), a string id (untyped),
121
- // or a batch of `[target, input]` tuples.
122
- const cross = async function cross(idOrTrail, input) {
123
- if (Array.isArray(idOrTrail)) {
124
- return await Promise.all(idOrTrail.map(([target, batchInput]) => invokeCross(target, batchInput, cross)));
125
- }
126
- return await invokeCross(idOrTrail, input, cross);
127
- };
128
- return cross;
129
- };
130
- // ---------------------------------------------------------------------------
131
- // Scenario assertions
132
- // ---------------------------------------------------------------------------
133
- const assertScenarioResult = (result, scenario, trailDef) => {
134
- if (scenario.expectValue !== undefined) {
135
- assertFullMatch(result, scenario.expectValue);
136
- }
137
- else if (scenario.expectErr !== undefined) {
138
- assertErrorMatch(result, scenario.expectErr, scenario.expectErrMessage);
139
- }
140
- else if (scenario.expectErrMessage !== undefined) {
141
- expect(result.isErr()).toBe(true);
142
- if (result.isErr()) {
143
- expect(result.error.message).toContain(scenario.expectErrMessage);
144
- }
145
- }
146
- else if (scenario.expectOk === true) {
147
- expect(result.isOk()).toBe(true);
148
- assertSchemaMatch(result, trailDef.output);
149
- }
150
- };
151
- const assertCrossTrace = (trace, scenario) => {
152
- if (scenario.expectCrossed !== undefined) {
153
- const crossedIds = trace.map((r) => r.id);
154
- expect(crossedIds).toEqual([...scenario.expectCrossed]);
155
- }
156
- if (scenario.expectCrossedCount !== undefined) {
157
- const counts = {};
158
- for (const record of trace) {
159
- counts[record.id] = (counts[record.id] ?? 0) + 1;
160
- }
161
- expect(counts).toEqual({ ...scenario.expectCrossedCount });
162
- }
163
- };
164
- const handleValidationError = (validated, scenario) => {
165
- if (!validated.isErr()) {
166
- return false;
167
- }
168
- if (scenario.expectErr === ValidationError) {
169
- expect(validated.error).toBeInstanceOf(ValidationError);
170
- if (scenario.expectErrMessage !== undefined) {
171
- expect(validated.error.message).toContain(scenario.expectErrMessage);
172
- }
173
- return true;
174
- }
175
- throw new Error(`Input validation failed unexpectedly: ${validated.error.message}`);
176
- };
177
- // ---------------------------------------------------------------------------
178
- // Scenario runner
179
- // ---------------------------------------------------------------------------
180
- const buildTestContext = (scenario, ctx, trailsMap, resources) => {
181
- const trace = [];
182
- const baseCtx = mergeTestContext(ctx);
183
- const cross = createRecordingCross(trace, scenario, trailsMap, baseCtx.cross, baseCtx, resources);
184
- return { testCtx: { ...baseCtx, cross }, trace };
185
- };
186
- const runScenario = async (trailDef, scenario, ctx, trailsMap, resources) => {
187
- const validated = validateInput(trailDef.input, scenario.input);
188
- if (handleValidationError(validated, scenario)) {
189
- return;
190
- }
191
- const { trace, testCtx } = buildTestContext(scenario, ctx, trailsMap, resources);
192
- const result = await executeTrail(trailDef, scenario.input, {
193
- ctx: testCtx,
194
- resources,
195
- });
196
- assertCrossTrace(trace, scenario);
197
- assertScenarioResult(result, scenario, trailDef);
198
- };
199
- /**
200
- * Generate a describe block for a trail with crossings with one test per scenario.
201
- *
202
- * @example
203
- * ```ts
204
- * testCrosses(onboardTrail, [
205
- * {
206
- * description: "crosses add then relate",
207
- * input: { name: "Alpha" },
208
- * expectOk: true,
209
- * expectCrossed: ["entity.add", "entity.relate"],
210
- * },
211
- * ]);
212
- * ```
213
- */
214
- export const testCrosses = (trailDef, scenarios, options) => {
215
- const explicitResources = options?.resources;
216
- describe(trailDef.id, () => {
217
- test.each([...scenarios])('$description', async (scenario) => {
218
- const resources = mergeResourceOverrides(await resolveCrossMockResources(trailDef, options?.trails), options?.ctx, explicitResources);
219
- await runScenario(trailDef, scenario, options?.ctx, options?.trails, resources);
220
- });
221
- });
222
- };
223
- //# sourceMappingURL=crosses.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crosses.js","sourceRoot":"","sources":["../src/crosses.ts"],"names":[],"mappings":"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"}
package/dist/detours.d.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * testDetours — validate the live detour contract for every trail.
3
- *
4
- * Pure structural validation. No blaze or detour recovery execution needed.
5
- */
6
- import type { Topo } from '@ontrails/core';
7
- /**
8
- * Verify that every trail's detours match the live runtime contract:
9
- * `on` must be an error constructor, `recover` must be callable, and
10
- * later detours must not be shadowed by earlier broader `on` types.
11
- */
12
- export declare const testDetours: (app: Topo) => void;
13
- //# sourceMappingURL=detours.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"detours.d.ts","sourceRoot":"","sources":["../src/detours.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,IAAI,EAAS,MAAM,gBAAgB,CAAC;AA+HlD;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,IAAI,KAAG,IAuCvC,CAAC"}
package/dist/detours.js DELETED
@@ -1,109 +0,0 @@
1
- /**
2
- * testDetours — validate the live detour contract for every trail.
3
- *
4
- * Pure structural validation. No blaze or detour recovery execution needed.
5
- */
6
- import { describe, test } from 'bun:test';
7
- import { TrailsError } from '@ontrails/core';
8
- const isErrorConstructor = (value) => {
9
- if (typeof value !== 'function') {
10
- return false;
11
- }
12
- const { prototype } = value;
13
- return prototype instanceof Error;
14
- };
15
- const detourLabel = (trailId, index, detour) => {
16
- if (typeof detour.on === 'function') {
17
- const { name } = detour.on;
18
- if (typeof name === 'string' && name.length > 0) {
19
- return `${trailId} detour[${index}] on ${name}`;
20
- }
21
- }
22
- return `${trailId} detour[${index}]`;
23
- };
24
- const assertValidOn = (trailId, index, detour) => {
25
- if (isErrorConstructor(detour.on)) {
26
- return;
27
- }
28
- throw new Error(`${detourLabel(trailId, index, detour)} must declare a real error constructor in on:`);
29
- };
30
- const assertCallableRecover = (trailId, index, detour) => {
31
- if (typeof detour.recover === 'function') {
32
- return;
33
- }
34
- throw new Error(`${detourLabel(trailId, index, detour)} must declare a callable recover function`);
35
- };
36
- const sameOrSubtype = (candidate, ancestor) => {
37
- if (candidate === ancestor) {
38
- return true;
39
- }
40
- let current = Object.getPrototypeOf(candidate.prototype);
41
- while (current && typeof current === 'object') {
42
- const ctor = current.constructor;
43
- if (ctor === ancestor) {
44
- return true;
45
- }
46
- current = Object.getPrototypeOf(current);
47
- }
48
- return false;
49
- };
50
- const getShadowingDetour = (detours, index) => {
51
- const detour = detours[index];
52
- if (!detour || !isErrorConstructor(detour.on)) {
53
- return undefined;
54
- }
55
- for (let previousIndex = 0; previousIndex < index; previousIndex += 1) {
56
- const previous = detours[previousIndex];
57
- if (!previous || !isErrorConstructor(previous.on)) {
58
- continue;
59
- }
60
- if (sameOrSubtype(detour.on, previous.on)) {
61
- return { index: previousIndex, on: previous.on };
62
- }
63
- }
64
- return undefined;
65
- };
66
- const assertNotShadowed = (trailId, detours, index) => {
67
- const detour = detours[index];
68
- if (!detour || !isErrorConstructor(detour.on)) {
69
- return;
70
- }
71
- const shadowing = getShadowingDetour(detours, index);
72
- if (!shadowing) {
73
- return;
74
- }
75
- const previousName = shadowing.on.name || TrailsError.name;
76
- const currentName = detour.on.name || TrailsError.name;
77
- throw new Error(`${trailId} detour[${index}] on ${currentName} is shadowed by earlier detour[${shadowing.index}] on ${previousName}`);
78
- };
79
- /**
80
- * Verify that every trail's detours match the live runtime contract:
81
- * `on` must be an error constructor, `recover` must be callable, and
82
- * later detours must not be shadowed by earlier broader `on` types.
83
- */
84
- export const testDetours = (app) => {
85
- const trailEntries = [...app.trails];
86
- describe('detours', () => {
87
- describe.each(trailEntries)('%s', (_id, trailDef) => {
88
- const trail = trailDef;
89
- if (trail.detours.length === 0) {
90
- return;
91
- }
92
- const detourCases = trail.detours.map((detour, index) => ({
93
- detour,
94
- index,
95
- trailId: trail.id,
96
- }));
97
- test.each(detourCases)('$trailId detour[$index] uses an error constructor', ({ detour, index, trailId }) => {
98
- assertValidOn(trailId, index, detour);
99
- });
100
- test.each(detourCases)('$trailId detour[$index] provides a callable recover', ({ detour, index, trailId }) => {
101
- assertCallableRecover(trailId, index, detour);
102
- });
103
- test.each(detourCases)('$trailId detour[$index] is not shadowed by an earlier detour', ({ index, trailId }) => {
104
- assertNotShadowed(trailId, trail.detours, index);
105
- });
106
- });
107
- });
108
- };
109
- //# sourceMappingURL=detours.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"detours.js","sourceRoot":"","sources":["../src/detours.ts"],"names":[],"mappings":"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"}
@@ -1,30 +0,0 @@
1
- import type { Trail, TrailExample } from '@ontrails/core';
2
- /**
3
- * Returns `true` if the given example was synthesized from contour fixtures
4
- * by `deriveTrailExamples`, `false` if it was authored on the trail.
5
- */
6
- export declare const isDerivedExample: (example: TrailExample<unknown, unknown>) => boolean;
7
- /**
8
- * Prefer authored trail examples and fall back to contour-derived fixtures.
9
- *
10
- * Examples returned by this helper come from one of two provenances:
11
- * - **Authored.** When `trail.examples` is non-empty, its entries are
12
- * returned verbatim. These are the developer's stated intent and carry
13
- * full invariants — including crossing-coverage assertions in
14
- * `testExamples`.
15
- * - **Derived.** When there are no authored examples but the trail has
16
- * contours with examples, candidate inputs are synthesized from contour
17
- * fixtures and validated against `trail.input`. These are opportunistic
18
- * coverage that exists to let `testAll(app)` exercise contour-backed
19
- * trails without per-test setup; they are not guaranteed to exercise
20
- * every composition branch, so consumers should relax invariants that
21
- * only make sense for authored inputs (see `isDerivedExample`).
22
- *
23
- * Contour examples stay as the raw input payload so Trails validation /
24
- * transforms still happen exactly once inside the normal test execution
25
- * path. Derived examples are additionally tagged via a module-level
26
- * `WeakSet` so consumers can detect them without widening the public
27
- * `TrailExample` shape.
28
- */
29
- export declare const deriveTrailExamples: (trail: Trail<unknown, unknown, unknown>) => readonly TrailExample<unknown, unknown>[];
30
- //# sourceMappingURL=effective-examples.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"effective-examples.d.ts","sourceRoot":"","sources":["../src/effective-examples.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAiBtE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,KACtC,OAAuC,CAAC;AAqQ3C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,KACtC,SAAS,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,EAyCzC,CAAC"}