@ontrails/testing 1.0.0-beta.1 → 1.0.0-beta.11

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 (55) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +166 -0
  3. package/README.md +25 -7
  4. package/dist/all.d.ts +2 -1
  5. package/dist/all.d.ts.map +1 -1
  6. package/dist/all.js.map +1 -1
  7. package/dist/context.d.ts +28 -2
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/context.js +70 -11
  10. package/dist/context.js.map +1 -1
  11. package/dist/contracts.d.ts +3 -2
  12. package/dist/contracts.d.ts.map +1 -1
  13. package/dist/contracts.js +25 -10
  14. package/dist/contracts.js.map +1 -1
  15. package/dist/examples.d.ts +4 -3
  16. package/dist/examples.d.ts.map +1 -1
  17. package/dist/examples.js +63 -68
  18. package/dist/examples.js.map +1 -1
  19. package/dist/follows.d.ts +38 -0
  20. package/dist/follows.d.ts.map +1 -0
  21. package/dist/{hike.js → follows.js} +71 -28
  22. package/dist/follows.js.map +1 -0
  23. package/dist/harness-mcp.d.ts.map +1 -1
  24. package/dist/harness-mcp.js +5 -2
  25. package/dist/harness-mcp.js.map +1 -1
  26. package/dist/index.d.ts +6 -4
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2 -2
  29. package/dist/index.js.map +1 -1
  30. package/dist/trail.js +1 -1
  31. package/dist/trail.js.map +1 -1
  32. package/dist/types.d.ts +2 -2
  33. package/dist/types.d.ts.map +1 -1
  34. package/package.json +6 -6
  35. package/src/__tests__/all.test.ts +64 -0
  36. package/src/__tests__/context.test.ts +92 -2
  37. package/src/__tests__/contracts.test.ts +122 -5
  38. package/src/__tests__/detours.test.ts +3 -3
  39. package/src/__tests__/examples.test.ts +285 -22
  40. package/src/__tests__/follows.test.ts +589 -0
  41. package/src/__tests__/trail.test.ts +4 -4
  42. package/src/all.ts +5 -1
  43. package/src/context.ts +121 -13
  44. package/src/contracts.ts +43 -12
  45. package/src/examples.ts +111 -105
  46. package/src/{hike.ts → follows.ts} +138 -43
  47. package/src/harness-mcp.ts +5 -2
  48. package/src/index.ts +6 -4
  49. package/src/trail.ts +1 -1
  50. package/src/types.ts +3 -3
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/dist/hike.d.ts +0 -32
  53. package/dist/hike.d.ts.map +0 -1
  54. package/dist/hike.js.map +0 -1
  55. package/src/__tests__/hike.test.ts +0 -164
@@ -1,3 +1,3 @@
1
1
  $ oxlint ./src
2
2
  Found 0 warnings and 0 errors.
3
- Finished in 27ms on 20 files with 93 rules using 24 threads.
3
+ Finished in 71ms on 21 files with 93 rules using 24 threads.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,171 @@
1
1
  # @ontrails/testing
2
2
 
3
+ ## 1.0.0-beta.11
4
+
5
+ ### Minor Changes
6
+
7
+ - Add services as a first-class primitive.
8
+
9
+ Services make infrastructure dependencies declarative, injectable, and governable. Define a service with `service()`, declare it on a trail with `services: [db]`, and access it with `db.from(ctx)` or `ctx.service()`.
10
+
11
+ **Core:** `service()` factory, `ServiceSpec<T>`, `ServiceContext`, singleton resolution in `executeTrail`, in-flight creation dedup, `isService` guard, `findDuplicateServiceId`, topo service discovery and validation, `services` field on trail specs.
12
+
13
+ **Testing:** Auto-resolution of `mock` factories in `testAll`, `testExamples`, `testContracts`, and `testFollows`. Explicit `services` overrides with correct precedence (`explicit > ctx.extensions > auto-mock`). Service mock propagation through follow graphs.
14
+
15
+ **Warden:** `service-declarations` rule validates `db.from(ctx)` and `ctx.service()` usage matches declared `services: [...]`. `service-exists` rule validates declared service IDs resolve in project context. Scope-aware AST walking skips nested function boundaries.
16
+
17
+ **Surfaces:** Service overrides thread through `dispatch` and `blaze` on CLI, MCP, and HTTP.
18
+
19
+ **Introspection:** Survey and surface map outputs include service graph. Topo exposes `.services`, `.getService()`, `.hasService()`, `.listServices()`, `.serviceIds()`, `.serviceCount`.
20
+
21
+ **Docs:** ADR-009 accepted. Unified services guide, updated vocabulary, getting-started, architecture, and package READMEs.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+ - @ontrails/core@1.0.0-beta.11
27
+ - @ontrails/cli@1.0.0-beta.11
28
+ - @ontrails/mcp@1.0.0-beta.11
29
+ - @ontrails/logging@1.0.0-beta.11
30
+
31
+ ## 1.0.0-beta.10
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies
36
+ - @ontrails/core@1.0.0-beta.10
37
+ - @ontrails/cli@1.0.0-beta.10
38
+ - @ontrails/mcp@1.0.0-beta.10
39
+ - @ontrails/logging@1.0.0-beta.10
40
+
41
+ ## 1.0.0-beta.9
42
+
43
+ ### Minor Changes
44
+
45
+ - Consolidated improvements across all surface packages.
46
+
47
+ **core**: Add `TrailResult<T>` utility type, `topo.ids()` and `topo.count` accessors, `dispatch()` for headless trail execution, and extract shared `executeTrail` pipeline used by CLI/MCP/HTTP.
48
+
49
+ **http**: Detect route path collisions and return `Result` from `buildHttpRoutes()`, wire request `AbortSignal` through to trail context, and make write → POST mapping explicit in intent-to-method lookup.
50
+
51
+ **mcp**: Return `Result` from `buildMcpTools()` on collision instead of throwing.
52
+
53
+ **cli**: Verify exception catching via centralized `executeTrail`.
54
+
55
+ **testing**: Follow context awareness improvements.
56
+
57
+ **warden**: Refactor rules as composable trails with examples.
58
+
59
+ **schema**: Error code and empty body fixes.
60
+
61
+ ### Patch Changes
62
+
63
+ - Updated dependencies
64
+ - @ontrails/core@1.0.0-beta.9
65
+ - @ontrails/cli@1.0.0-beta.9
66
+ - @ontrails/mcp@1.0.0-beta.9
67
+ - @ontrails/logging@1.0.0-beta.9
68
+
69
+ ## 1.0.0-beta.8
70
+
71
+ ### Patch Changes
72
+
73
+ - @ontrails/cli@1.0.0-beta.8
74
+ - @ontrails/core@1.0.0-beta.8
75
+ - @ontrails/logging@1.0.0-beta.8
76
+ - @ontrails/mcp@1.0.0-beta.8
77
+
78
+ ## 1.0.0-beta.7
79
+
80
+ ### Patch Changes
81
+
82
+ - @ontrails/cli@1.0.0-beta.7
83
+ - @ontrails/core@1.0.0-beta.7
84
+ - @ontrails/logging@1.0.0-beta.7
85
+ - @ontrails/mcp@1.0.0-beta.7
86
+
87
+ ## 1.0.0-beta.6
88
+
89
+ ### Patch Changes
90
+
91
+ - Updated dependencies
92
+ - @ontrails/core@1.0.0-beta.6
93
+ - @ontrails/cli@1.0.0-beta.6
94
+ - @ontrails/logging@1.0.0-beta.6
95
+ - @ontrails/mcp@1.0.0-beta.6
96
+
97
+ ## 1.0.0-beta.5
98
+
99
+ ### Patch Changes
100
+
101
+ - Updated dependencies
102
+ - @ontrails/core@1.0.0-beta.5
103
+ - @ontrails/logging@1.0.0-beta.5
104
+ - @ontrails/cli@1.0.0-beta.5
105
+ - @ontrails/mcp@1.0.0-beta.5
106
+
107
+ ## 1.0.0-beta.4
108
+
109
+ ### Major Changes
110
+
111
+ - API simplification: unified trail model, intent enum, run, metadata.
112
+
113
+ **BREAKING CHANGES:**
114
+
115
+ - `hike()` removed — use `trail()` with optional `follow: [...]` field
116
+ - `follows` renamed to `follow` (singular, matching `ctx.follow()`)
117
+ - `topo.hikes` removed — single `topo.trails` map
118
+ - `kind: 'hike'` removed — everything is `kind: 'trail'`
119
+ - `readOnly`/`destructive` booleans replaced by `intent: 'read' | 'write' | 'destroy'`
120
+ - `implementation` field renamed to `run`
121
+ - `markers` field renamed to `metadata`
122
+ - `testHike` renamed to `testFollows`, `HikeScenario` to `FollowScenario`
123
+ - `blaze()` now returns the surface handle (`Command` for CLI, `Server` for MCP)
124
+
125
+ ### Patch Changes
126
+
127
+ - Updated dependencies
128
+ - @ontrails/core@1.0.0-beta.4
129
+ - @ontrails/cli@1.0.0-beta.4
130
+ - @ontrails/mcp@1.0.0-beta.4
131
+ - @ontrails/logging@1.0.0-beta.4
132
+
133
+ ## 1.0.0-beta.3
134
+
135
+ ### Minor Changes
136
+
137
+ - Bug fixes across all surface packages found via parallel Codex review.
138
+
139
+ **core**: Fix Result.toJson false circular detection on DAGs, deserializeError subclass round-trip, topo cross-kind ID collisions, validateTopo multi-node cycle detection, error example input validation bypass, and deriveFields array type collapse.
140
+
141
+ **cli**: Switch blaze to parseAsync for proper async error handling, add boolean flag negation (--no-flag), and strict number parsing that rejects partial input.
142
+
143
+ **mcp**: Align BlobRef with core (including ReadableStream support) and detect tool-name collisions after normalization.
144
+
145
+ **testing**: Include hikes in testContracts validation, with follow-context awareness.
146
+
147
+ **warden**: Collect hike detour targets, validate detour refs in hike specs, and stop implementation-returns-result from walking into nested function bodies.
148
+
149
+ ### Patch Changes
150
+
151
+ - Updated dependencies
152
+ - @ontrails/core@1.0.0-beta.3
153
+ - @ontrails/cli@1.0.0-beta.3
154
+ - @ontrails/mcp@1.0.0-beta.3
155
+ - @ontrails/logging@1.0.0-beta.3
156
+
157
+ ## 1.0.0-beta.2
158
+
159
+ ### Patch Changes
160
+
161
+ - Fix workspace dependency resolution in published packages. Now using bun publish
162
+ which correctly replaces workspace:^ with actual version numbers.
163
+ - Updated dependencies
164
+ - @ontrails/core@1.0.0-beta.2
165
+ - @ontrails/cli@1.0.0-beta.2
166
+ - @ontrails/mcp@1.0.0-beta.2
167
+ - @ontrails/logging@1.0.0-beta.2
168
+
3
169
  ## 1.0.0-beta.1
4
170
 
5
171
  ### Patch Changes
package/README.md CHANGED
@@ -29,10 +29,10 @@ testDetours(app); // Verify detour targets exist
29
29
  | --- | --- |
30
30
  | `testAll(topo, ctx?)` | Single-line governance suite: validation + examples + contracts + detours |
31
31
  | `testExamples(topo, ctx?)` | Run trail examples as `describe`/`test` blocks |
32
- | `testTrail(trail, scenarios)` | Custom scenarios for edge cases and error paths |
33
- | `testHike(hike, scenarios)` | Test composition graphs -- follow chains, failure injection |
34
- | `testContracts(topo, ctx?)` | Validate implementation output against declared schemas |
32
+ | `testTrail(trail, scenarios)` | Custom scenarios for edge cases, error paths, and follow chains |
33
+ | `testContracts(topo, ctx?)` | Validate output against declared schemas |
35
34
  | `testDetours(topo)` | Verify every detour target exists in the topo |
35
+ | `createFollowContext(options?)` | Mock `FollowFn` for testing composite trails; returns preconfigured `Result` values keyed by trail ID |
36
36
  | `createTestContext(options?)` | `TrailContext` with sensible test defaults |
37
37
  | `createTestLogger()` | Logger that captures entries in memory for assertions |
38
38
  | `createCliHarness(options)` | Execute CLI commands in-process, capture stdout/stderr |
@@ -55,19 +55,37 @@ testTrail(showTrail, [
55
55
  ]);
56
56
  ```
57
57
 
58
- ## testHike
58
+ ## Testing composition (trails with follow)
59
59
 
60
- Where `testTrail` exercises a single trail, `testHike` exercises the follow graph:
60
+ `testTrail` works the same for trails with `follow` -- it exercises the follow graph:
61
61
 
62
62
  ```typescript
63
- import { testHike } from '@ontrails/testing';
63
+ import { testTrail } from '@ontrails/testing';
64
64
 
65
- testHike(onboardHike, [
65
+ testTrail(onboardTrail, [
66
66
  { description: 'happy path', input: { name: 'Delta', type: 'tool' }, expectOk: true },
67
67
  { description: 'add fails', input: { name: 'Alpha' }, expectErr: AlreadyExistsError },
68
68
  ]);
69
69
  ```
70
70
 
71
+ When you need to isolate a composite trail and stub out its dependencies, use `createFollowContext`:
72
+
73
+ ```typescript
74
+ import { createFollowContext, createTestContext } from '@ontrails/testing';
75
+ import { Result } from '@ontrails/core';
76
+
77
+ const follow = createFollowContext({
78
+ responses: {
79
+ 'entity.add': Result.ok({ id: '1', name: 'Delta', type: 'tool' }),
80
+ 'search': Result.ok({ results: [] }),
81
+ },
82
+ });
83
+ const ctx = { ...createTestContext(), follow };
84
+ const result = await onboardTrail.run({ name: 'Delta', type: 'tool' }, ctx);
85
+ ```
86
+
87
+ Calls to unregistered trail IDs return `Result.err` with a descriptive message, so missing stubs fail loudly.
88
+
71
89
  ## Surface harnesses
72
90
 
73
91
  ```typescript
package/dist/all.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * verification into one describe block.
6
6
  */
7
7
  import type { Topo, TrailContext } from '@ontrails/core';
8
+ import type { TestExecutionOptions } from './context.js';
8
9
  /**
9
10
  * Run the full governance test suite for a Topo.
10
11
  *
@@ -26,5 +27,5 @@ import type { Topo, TrailContext } from '@ontrails/core';
26
27
  * testAll(app);
27
28
  * ```
28
29
  */
29
- export declare const testAll: (topo: Topo, ctxOrFactory?: Partial<TrailContext> | (() => Partial<TrailContext>)) => void;
30
+ export declare const testAll: (topo: Topo, ctxOrFactory?: Partial<TrailContext> | TestExecutionOptions | (() => Partial<TrailContext> | TestExecutionOptions)) => void;
30
31
  //# sourceMappingURL=all.d.ts.map
package/dist/all.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAOzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,OAAO,GAClB,MAAM,IAAI,EACV,eAAe,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,KACnE,IAcF,CAAC"}
1
+ {"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAIzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,OAAO,GAClB,MAAM,IAAI,EACV,eACI,OAAO,CAAC,YAAY,CAAC,GACrB,oBAAoB,GACpB,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,oBAAoB,CAAC,KACvD,IAcF,CAAC"}
package/dist/all.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"all.js","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGlD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,IAAU,EACV,YAAoE,EAC9D,EAAE;IACR,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE;YAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,oGAAoG;QACpG,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACjC,oGAAoG;QACpG,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAClC,oGAAoG;QACpG,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"all.js","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGlD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,IAAU,EACV,YAGwD,EAClD,EAAE;IACR,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE;YAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,oGAAoG;QACpG,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACjC,oGAAoG;QACpG,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAClC,oGAAoG;QACpG,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
package/dist/context.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Test context factory for creating TrailContext instances suitable for testing.
3
3
  */
4
- import type { TrailContext } from '@ontrails/core';
4
+ import type { FollowFn, ServiceOverrideMap, Topo, TrailContext } from '@ontrails/core';
5
+ import { Result } from '@ontrails/core';
5
6
  import type { TestTrailContextOptions } from './types.js';
6
7
  /**
7
8
  * Create a TrailContext with deterministic, test-friendly defaults.
@@ -11,9 +12,34 @@ import type { TestTrailContextOptions } from './types.js';
11
12
  * - `signal`: a non-aborted AbortController signal
12
13
  */
13
14
  export declare const createTestContext: (overrides?: TestTrailContextOptions) => TrailContext;
15
+ export interface CreateFollowContextOptions {
16
+ readonly responses?: Record<string, Result<unknown, Error>> | undefined;
17
+ }
18
+ export interface TestExecutionOptions {
19
+ readonly ctx?: Partial<TrailContext> | undefined;
20
+ readonly services?: ServiceOverrideMap | undefined;
21
+ }
22
+ /**
23
+ * Create a mock `FollowFn` for testing composite trails.
24
+ *
25
+ * Returns preconfigured `Result` values keyed by trail ID. Calls to
26
+ * unregistered IDs return `Result.err` with a descriptive message.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const follow = createFollowContext({
31
+ * responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
32
+ * });
33
+ * const ctx = { ...createTestContext(), follow };
34
+ * ```
35
+ */
36
+ export declare const createFollowContext: (options?: CreateFollowContextOptions) => FollowFn;
37
+ export declare const normalizeTestExecutionOptions: (input?: Partial<TrailContext> | TestExecutionOptions) => TestExecutionOptions;
38
+ export declare const mergeServiceOverrides: (autoResolved: ServiceOverrideMap, ctx: Partial<TrailContext> | undefined, explicit: ServiceOverrideMap | undefined) => ServiceOverrideMap;
39
+ export declare const resolveMockServices: (app: Topo) => Promise<ServiceOverrideMap>;
14
40
  /**
15
41
  * Merge a Partial<TrailContext> into a test context.
16
42
  * Used internally when the public API accepts Partial<TrailContext>.
17
43
  */
18
- export declare const mergeTestContext: (ctx?: Partial<TrailContext>) => TrailContext;
44
+ export declare const mergeTestContext: (ctx?: Partial<TrailContext>, services?: ServiceOverrideMap) => TrailContext;
19
45
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAM1D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC5B,YAAY,uBAAuB,KAClC,YAMD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,OAAO,CAAC,YAAY,CAAC,KAAG,YAO9D,CAAC"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,IAAI,EACJ,YAAY,EACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAuB,MAAM,gBAAgB,CAAC;AAG7D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAU1D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC5B,YAAY,uBAAuB,KAClC,YAaF,CAAC;AAMF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;CACzE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACpD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAU,0BAA0B,KACnC,QAcF,CAAC;AAQF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,OAAO,CAAC,YAAY,CAAC,GAAG,oBAAoB,KACnD,oBACqD,CAAC;AAEzD,eAAO,MAAM,qBAAqB,GAChC,cAAc,kBAAkB,EAChC,KAAK,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,EACtC,UAAU,kBAAkB,GAAG,SAAS,KACvC,kBAID,CAAC;AAaH,eAAO,MAAM,mBAAmB,GAC9B,KAAK,IAAI,KACR,OAAO,CAAC,kBAAkB,CAAiC,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,OAAO,CAAC,YAAY,CAAC,EAC3B,WAAW,kBAAkB,KAC5B,YAcF,CAAC"}
package/dist/context.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Test context factory for creating TrailContext instances suitable for testing.
3
3
  */
4
+ import { Result, createServiceLookup } from '@ontrails/core';
4
5
  import { createTestLogger } from './logger.js';
5
6
  // ---------------------------------------------------------------------------
6
7
  // createTestContext
@@ -12,22 +13,80 @@ import { createTestLogger } from './logger.js';
12
13
  * - `logger`: a `TestLogger` that captures entries
13
14
  * - `signal`: a non-aborted AbortController signal
14
15
  */
15
- export const createTestContext = (overrides) => ({
16
- env: overrides?.env ?? { TRAILS_ENV: 'test' },
17
- logger: overrides?.logger ?? createTestLogger(),
18
- requestId: overrides?.requestId ?? 'test-request-001',
19
- signal: overrides?.signal ?? new AbortController().signal,
20
- workspaceRoot: overrides?.cwd ?? process.cwd(),
16
+ export const createTestContext = (overrides) => {
17
+ const cwd = overrides?.cwd ?? process.cwd();
18
+ const ctx = {
19
+ cwd,
20
+ env: overrides?.env ?? { TRAILS_ENV: 'test' },
21
+ extensions: undefined,
22
+ logger: overrides?.logger ?? createTestLogger(),
23
+ requestId: overrides?.requestId ?? 'test-request-001',
24
+ signal: overrides?.signal ?? new AbortController().signal,
25
+ workspaceRoot: cwd,
26
+ };
27
+ ctx.service = createServiceLookup(() => ctx);
28
+ return ctx;
29
+ };
30
+ /**
31
+ * Create a mock `FollowFn` for testing composite trails.
32
+ *
33
+ * Returns preconfigured `Result` values keyed by trail ID. Calls to
34
+ * unregistered IDs return `Result.err` with a descriptive message.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const follow = createFollowContext({
39
+ * responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
40
+ * });
41
+ * const ctx = { ...createTestContext(), follow };
42
+ * ```
43
+ */
44
+ export const createFollowContext = (options) => {
45
+ const responses = options?.responses ?? {};
46
+ return (id, _input) => {
47
+ const response = responses[id];
48
+ if (response === undefined) {
49
+ return Promise.resolve(Result.err(new Error(`No mock response for follow("${id}")`)));
50
+ }
51
+ return Promise.resolve(response);
52
+ };
53
+ };
54
+ const isTestExecutionOptions = (input) => input !== undefined &&
55
+ (Object.hasOwn(input, 'ctx') || Object.hasOwn(input, 'services'));
56
+ export const normalizeTestExecutionOptions = (input) => isTestExecutionOptions(input) ? input : { ctx: input };
57
+ export const mergeServiceOverrides = (autoResolved, ctx, explicit) => ({
58
+ ...autoResolved,
59
+ ...ctx?.extensions,
60
+ ...explicit,
21
61
  });
62
+ const buildMockServices = async (app) => {
63
+ const services = {};
64
+ for (const declaredService of app.listServices()) {
65
+ if (!declaredService.mock) {
66
+ continue;
67
+ }
68
+ services[declaredService.id] = await declaredService.mock();
69
+ }
70
+ return services;
71
+ };
72
+ export const resolveMockServices = async (app) => await buildMockServices(app);
22
73
  /**
23
74
  * Merge a Partial<TrailContext> into a test context.
24
75
  * Used internally when the public API accepts Partial<TrailContext>.
25
76
  */
26
- export const mergeTestContext = (ctx) => {
27
- if (ctx === undefined) {
28
- return createTestContext();
29
- }
77
+ export const mergeTestContext = (ctx, services) => {
30
78
  const base = createTestContext();
31
- return { ...base, ...ctx };
79
+ const extensions = {
80
+ ...base.extensions,
81
+ ...ctx?.extensions,
82
+ ...services,
83
+ };
84
+ const merged = {
85
+ ...base,
86
+ ...ctx,
87
+ extensions: Object.keys(extensions).length === 0 ? undefined : extensions,
88
+ };
89
+ merged.service = createServiceLookup(() => merged);
90
+ return merged;
32
91
  };
33
92
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,SAAmC,EACrB,EAAE,CAAC,CAAC;IAClB,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE;IAC7C,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,gBAAgB,EAAE;IAC/C,SAAS,EAAE,SAAS,EAAE,SAAS,IAAI,kBAAkB;IACrD,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC,MAAM;IACzD,aAAa,EAAE,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;CAC/C,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAA2B,EAAgB,EAAE;IAC5E,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,iBAAiB,EAAE,CAAC;IAC7B,CAAC;IAED,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,EAAE,CAAC;AAC7B,CAAC,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -6,11 +6,12 @@
6
6
  * the declared schema.
7
7
  */
8
8
  import type { Topo, TrailContext } from '@ontrails/core';
9
+ import type { TestExecutionOptions } from './context.js';
9
10
  /**
10
- * Verify that every trail's implementation output matches its declared
11
+ * Verify that every trail implementation output matches its declared
11
12
  * output schema. Catches implementation-schema drift.
12
13
  *
13
14
  * Trails without output schemas or examples are skipped.
14
15
  */
15
- export declare const testContracts: (app: Topo, ctxOrFactory?: Partial<TrailContext> | (() => Partial<TrailContext>)) => void;
16
+ export declare const testContracts: (app: Topo, ctxOrFactory?: Partial<TrailContext> | TestExecutionOptions | (() => Partial<TrailContext> | TestExecutionOptions)) => void;
16
17
  //# sourceMappingURL=contracts.d.ts.map
@@ -1 +1 @@
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;AA8B9E;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,IAAI,EACT,eAAe,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,KACnE,IAmCF,CAAC"}
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;AAqCzD;;;;;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,IA6CF,CAAC"}
package/dist/contracts.js CHANGED
@@ -6,12 +6,20 @@
6
6
  * the declared schema.
7
7
  */
8
8
  import { describe, test } from 'bun:test';
9
- import { formatZodIssues, validateInput } from '@ontrails/core';
9
+ import { executeTrail, formatZodIssues, validateInput } from '@ontrails/core';
10
10
  import { expectOk } from './assertions.js';
11
- import { mergeTestContext } from './context.js';
11
+ import { mergeServiceOverrides, mergeTestContext, normalizeTestExecutionOptions, resolveMockServices, } from './context.js';
12
12
  // ---------------------------------------------------------------------------
13
13
  // Helpers
14
14
  // ---------------------------------------------------------------------------
15
+ /** Check if a trail requires follow() but the context doesn't provide it. */
16
+ const needsFollowContext = (t, resolveCtx) => {
17
+ const spec = t;
18
+ if (!spec.follow || spec.follow.length === 0) {
19
+ return false;
20
+ }
21
+ return !normalizeTestExecutionOptions(resolveCtx()).ctx?.follow;
22
+ };
15
23
  const validateOutputSchema = (outputSchema, value, trailId, exampleName) => {
16
24
  const parsed = outputSchema.safeParse(value);
17
25
  if (!parsed.success) {
@@ -23,30 +31,37 @@ const validateOutputSchema = (outputSchema, value, trailId, exampleName) => {
23
31
  // testContracts
24
32
  // ---------------------------------------------------------------------------
25
33
  /**
26
- * Verify that every trail's implementation output matches its declared
34
+ * Verify that every trail implementation output matches its declared
27
35
  * output schema. Catches implementation-schema drift.
28
36
  *
29
37
  * Trails without output schemas or examples are skipped.
30
38
  */
31
39
  export const testContracts = (app, ctxOrFactory) => {
32
- const resolveCtx = typeof ctxOrFactory === 'function' ? ctxOrFactory : () => ctxOrFactory;
33
- const trailEntries = [...app.trails];
40
+ const resolveInput = typeof ctxOrFactory === 'function' ? ctxOrFactory : () => ctxOrFactory;
41
+ const allEntries = app.list();
34
42
  describe('contracts', () => {
35
- describe.each(trailEntries)('%s', (_id, trailDef) => {
36
- const t = trailDef;
43
+ describe.each(allEntries)('$id', (t) => {
37
44
  if (t.output === undefined) {
38
45
  return;
39
46
  }
40
47
  if (t.examples === undefined || t.examples.length === 0) {
41
48
  return;
42
49
  }
50
+ if (needsFollowContext(t, resolveInput)) {
51
+ return;
52
+ }
43
53
  const { examples, output: outputSchema } = t;
44
54
  const successExamples = examples.filter((e) => e.error === undefined);
45
55
  test.each(successExamples)('contract: $name', async (example) => {
46
- const testCtx = mergeTestContext(resolveCtx());
56
+ const resolved = normalizeTestExecutionOptions(resolveInput());
57
+ const services = mergeServiceOverrides(await resolveMockServices(app), resolved.ctx, resolved.services);
58
+ const testCtx = mergeTestContext(resolved.ctx);
47
59
  const validated = validateInput(t.input, example.input);
48
- const validatedInput = expectOk(validated);
49
- const result = await t.implementation(validatedInput, testCtx);
60
+ expectOk(validated);
61
+ const result = await executeTrail(t, example.input, {
62
+ ctx: testCtx,
63
+ services,
64
+ });
50
65
  const resultValue = expectOk(result);
51
66
  validateOutputSchema(outputSchema, resultValue, t.id, example.name);
52
67
  });
@@ -1 +1 @@
1
- {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGhE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,oBAAoB,GAAG,CAC3B,YAAuB,EACvB,KAAc,EACd,OAAe,EACf,WAAmB,EACb,EAAE;IACR,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,sCAAsC,OAAO,eAAe,WAAW,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAC7K,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,GAAS,EACT,YAAoE,EAC9D,EAAE;IACR,MAAM,UAAU,GACd,OAAO,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;IACzE,MAAM,YAAY,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IAErC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;QACzB,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;YAClD,MAAM,CAAC,GAAG,QAAmC,CAAC;YAE9C,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;YAC7C,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;YAEtE,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CACxB,iBAAiB,EACjB,KAAK,EAAE,OAAuC,EAAE,EAAE;gBAChD,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,EAAE,CAAC,CAAC;gBAE/C,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;gBACxD,MAAM,cAAc,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;gBAE3C,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,cAAc,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;gBAC/D,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAErC,oBAAoB,CAAC,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YACtE,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
@@ -3,14 +3,15 @@
3
3
  *
4
4
  * Iterates every trail in the app's topo. For each trail with examples,
5
5
  * generates describe/test blocks using bun:test. Progressive assertion
6
- * determines which check to run per example. For hikes with `follows`
6
+ * determines which check to run per example. For trails with `follow`
7
7
  * declarations, checks that every declared follow was called at least once.
8
8
  */
9
9
  import type { Topo, TrailContext } from '@ontrails/core';
10
+ import type { TestExecutionOptions } from './context.js';
10
11
  /**
11
12
  * Generate describe/test blocks for every trail example in the app.
12
13
  *
13
- * For hikes with `follows` declarations and examples, also verifies that
14
+ * For trails with `follow` declarations and examples, also verifies that
14
15
  * every declared follow ID was called at least once across all examples.
15
16
  *
16
17
  * One line in your test file:
@@ -18,5 +19,5 @@ import type { Topo, TrailContext } from '@ontrails/core';
18
19
  * testExamples(app);
19
20
  * ```
20
21
  */
21
- export declare const testExamples: (app: Topo, ctxOrFactory?: Partial<TrailContext> | (() => Partial<TrailContext>)) => void;
22
+ export declare const testExamples: (app: Topo, ctxOrFactory?: Partial<TrailContext> | TestExecutionOptions | (() => Partial<TrailContext> | TestExecutionOptions)) => void;
22
23
  //# sourceMappingURL=examples.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"examples.d.ts","sourceRoot":"","sources":["../src/examples.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAGV,IAAI,EAGJ,YAAY,EACb,MAAM,gBAAgB,CAAC;AAkQxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,IAAI,EACT,eAAe,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,KACnE,IAuBF,CAAC"}
1
+ {"version":3,"file":"examples.d.ts","sourceRoot":"","sources":["../src/examples.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAGV,IAAI,EAGJ,YAAY,EACb,MAAM,gBAAgB,CAAC;AAkCxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAiLzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,IAAI,EACT,eACI,OAAO,CAAC,YAAY,CAAC,GACrB,oBAAoB,GACpB,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,oBAAoB,CAAC,KACvD,IAyEF,CAAC"}