@ontrails/testing 1.0.0-beta.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/.turbo/turbo-build.log +1 -0
- package/.turbo/turbo-lint.log +3 -0
- package/.turbo/turbo-typecheck.log +1 -0
- package/CHANGELOG.md +23 -0
- package/README.md +221 -0
- package/dist/all.d.ts +30 -0
- package/dist/all.d.ts.map +1 -0
- package/dist/all.js +47 -0
- package/dist/all.js.map +1 -0
- package/dist/assertions.d.ts +49 -0
- package/dist/assertions.d.ts.map +1 -0
- package/dist/assertions.js +84 -0
- package/dist/assertions.js.map +1 -0
- package/dist/context.d.ts +19 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +33 -0
- package/dist/context.js.map +1 -0
- package/dist/contracts.d.ts +16 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +56 -0
- package/dist/contracts.js.map +1 -0
- package/dist/detours.d.ts +12 -0
- package/dist/detours.d.ts.map +1 -0
- package/dist/detours.js +30 -0
- package/dist/detours.js.map +1 -0
- package/dist/examples.d.ts +22 -0
- package/dist/examples.d.ts.map +1 -0
- package/dist/examples.js +187 -0
- package/dist/examples.js.map +1 -0
- package/dist/harness-cli.d.ts +21 -0
- package/dist/harness-cli.d.ts.map +1 -0
- package/dist/harness-cli.js +213 -0
- package/dist/harness-cli.js.map +1 -0
- package/dist/harness-mcp.d.ts +21 -0
- package/dist/harness-mcp.d.ts.map +1 -0
- package/dist/harness-mcp.js +50 -0
- package/dist/harness-mcp.js.map +1 -0
- package/dist/hike.d.ts +32 -0
- package/dist/hike.d.ts.map +1 -0
- package/dist/hike.js +169 -0
- package/dist/hike.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +15 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +87 -0
- package/dist/logger.js.map +1 -0
- package/dist/trail.d.ts +20 -0
- package/dist/trail.d.ts.map +1 -0
- package/dist/trail.js +80 -0
- package/dist/trail.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +23 -0
- package/src/__tests__/context.test.ts +60 -0
- package/src/__tests__/contracts.test.ts +68 -0
- package/src/__tests__/detours.test.ts +55 -0
- package/src/__tests__/examples.test.ts +176 -0
- package/src/__tests__/hike.test.ts +164 -0
- package/src/__tests__/logger.test.ts +136 -0
- package/src/__tests__/trail.test.ts +99 -0
- package/src/all.ts +55 -0
- package/src/assertions.ts +108 -0
- package/src/context.ts +42 -0
- package/src/contracts.ts +85 -0
- package/src/detours.ts +44 -0
- package/src/examples.ts +314 -0
- package/src/harness-cli.ts +310 -0
- package/src/harness-mcp.ts +65 -0
- package/src/hike.ts +283 -0
- package/src/index.ts +40 -0
- package/src/logger.ts +125 -0
- package/src/trail.ts +116 -0
- package/src/types.ts +117 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ tsc -b
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ tsc --noEmit
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @ontrails/testing
|
|
2
|
+
|
|
3
|
+
## 1.0.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Initial v1 beta release of the Trails framework.
|
|
8
|
+
|
|
9
|
+
- **@ontrails/core** — Result type, error taxonomy, trail/hike/event/topo, validateTopo, validateInput/Output, deriveFields, patterns, redaction, branded types, resilience
|
|
10
|
+
- **@ontrails/cli** — CLI surface adapter, Commander integration, flag derivation, layers
|
|
11
|
+
- **@ontrails/mcp** — MCP surface adapter, tool generation, annotations, progress bridge
|
|
12
|
+
- **@ontrails/logging** — Structured logging, sinks, formatters, LogTape adapter
|
|
13
|
+
- **@ontrails/testing** — testAll, testExamples, testTrail, testHike, testContracts, testDetours, surface harnesses
|
|
14
|
+
- **@ontrails/warden** — AST-based code convention rules via oxc-parser, drift detection, CI formatters
|
|
15
|
+
- **@ontrails/schema** — Surface map generation, hashing, semantic diffing
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @ontrails/core@1.0.0-beta.0
|
|
21
|
+
- @ontrails/cli@1.0.0-beta.0
|
|
22
|
+
- @ontrails/mcp@1.0.0-beta.0
|
|
23
|
+
- @ontrails/logging@1.0.0-beta.0
|
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# @ontrails/testing
|
|
2
|
+
|
|
3
|
+
Contract-driven testing utilities for Trails. Write examples for agent fluency -- get test cases for free.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add -d @ontrails/testing
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `@ontrails/core`, `@ontrails/cli`, `@ontrails/mcp`, `@ontrails/logging`, `zod`.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { testExamples } from '@ontrails/testing';
|
|
17
|
+
import { app } from '../app';
|
|
18
|
+
|
|
19
|
+
testExamples(app);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
One line. The entire topo is tested. Every trail, every example: input validation, implementation execution, output verification.
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
PASS src/__tests__/app.test.ts
|
|
26
|
+
greet
|
|
27
|
+
example: Basic greeting
|
|
28
|
+
example: Loud greeting
|
|
29
|
+
entity.show
|
|
30
|
+
example: Show entity by name
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API Overview
|
|
34
|
+
|
|
35
|
+
### `testAll(topo, ctx?)`
|
|
36
|
+
|
|
37
|
+
Single-line governance suite. Wraps topo validation, example execution, contract checks, and detour verification into one `governance` describe block:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { testAll } from '@ontrails/testing';
|
|
41
|
+
import { app } from '../app';
|
|
42
|
+
|
|
43
|
+
testAll(app);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For most apps, `testAll` is the only test call you need.
|
|
47
|
+
|
|
48
|
+
### `testExamples(app, ctx?)`
|
|
49
|
+
|
|
50
|
+
For each trail with `examples`, generates `describe`/`test` blocks using the Bun test runner.
|
|
51
|
+
|
|
52
|
+
Per example:
|
|
53
|
+
|
|
54
|
+
1. Validates `example.input` against the trail's Zod schema
|
|
55
|
+
2. Calls the implementation with validated input
|
|
56
|
+
3. Applies progressive assertion (see below)
|
|
57
|
+
4. Validates output against the trail's output schema (if present)
|
|
58
|
+
|
|
59
|
+
Trails with no examples produce no tests.
|
|
60
|
+
|
|
61
|
+
The runtime implementation is always awaited, so `testExamples()` behaves the same for sync-authored and async-authored trails.
|
|
62
|
+
|
|
63
|
+
### `testTrail(trail, scenarios, ctx?)`
|
|
64
|
+
|
|
65
|
+
Custom scenarios for edge cases, boundary values, and regressions that do not belong in agent-facing examples:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { testTrail } from '@ontrails/testing';
|
|
69
|
+
import { ValidationError, NotFoundError } from '@ontrails/core';
|
|
70
|
+
|
|
71
|
+
testTrail(showTrail, [
|
|
72
|
+
{ description: 'empty name', input: { name: '' }, expectOk: true },
|
|
73
|
+
{ description: 'missing name', input: {}, expectErr: ValidationError },
|
|
74
|
+
{
|
|
75
|
+
description: 'exact match',
|
|
76
|
+
input: { name: 'Alpha' },
|
|
77
|
+
expectValue: { name: 'Alpha', type: 'concept' },
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
description: 'not found',
|
|
81
|
+
input: { name: 'missing' },
|
|
82
|
+
expectErr: NotFoundError,
|
|
83
|
+
expectErrMessage: 'not found',
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `testHike(hike, scenarios, ctx?)`
|
|
89
|
+
|
|
90
|
+
Tests a hike's composition graph -- follow chains, failure injection, and multi-trail interactions:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { testHike } from '@ontrails/testing';
|
|
94
|
+
|
|
95
|
+
testHike(onboardHike, [
|
|
96
|
+
{ description: 'successful onboard', input: { name: 'Delta', type: 'tool' }, expectOk: true },
|
|
97
|
+
{ description: 'fails when add fails', input: { name: 'Alpha' }, expectErr: AlreadyExistsError },
|
|
98
|
+
]);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Where `testTrail` exercises a single trail in isolation, `testHike` exercises the follow graph and verifies that upstream failures propagate correctly.
|
|
102
|
+
|
|
103
|
+
### `testContracts(app, ctx?)`
|
|
104
|
+
|
|
105
|
+
Catches implementation-schema drift. Runs every example through the implementation, then validates the result against the trail's `output` schema. Reports detailed Zod errors on mismatch.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { testContracts } from '@ontrails/testing';
|
|
109
|
+
|
|
110
|
+
testContracts(app);
|
|
111
|
+
// Fails if any implementation returns data that doesn't match its declared output schema
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `testDetours(app)`
|
|
115
|
+
|
|
116
|
+
Structural validation of detour declarations. Verifies every detour target trail exists in the topo. No implementation execution needed.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { testDetours } from '@ontrails/testing';
|
|
120
|
+
|
|
121
|
+
testDetours(app);
|
|
122
|
+
// Fails: Trail "entity.show" has detour target "entity.search" which does not exist in the topo
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Progressive Assertion
|
|
126
|
+
|
|
127
|
+
What `testExamples` checks depends on what the example declares:
|
|
128
|
+
|
|
129
|
+
**Full match** -- example has `expected`:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
{
|
|
133
|
+
name: 'Found',
|
|
134
|
+
input: { name: 'Alpha' },
|
|
135
|
+
expected: { name: 'Alpha', type: 'concept' },
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Asserts `result.isOk()` and `result.value` deep-equals `expected`.
|
|
140
|
+
|
|
141
|
+
**Schema-only match** -- example has no `expected` and no `error`:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
{ name: 'Returns something valid', input: { name: 'Alpha' } }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Asserts `result.isOk()` and validates against the trail's output schema.
|
|
148
|
+
|
|
149
|
+
**Error match** -- example has `error`:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
{ name: 'Not found', input: { name: 'missing' }, error: 'NotFoundError' }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Asserts `result.isErr()` and `instanceof` check.
|
|
156
|
+
|
|
157
|
+
### Test Context and Mocks
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { createTestContext, createTestLogger } from '@ontrails/testing';
|
|
161
|
+
|
|
162
|
+
// TrailContext with sensible test defaults
|
|
163
|
+
const ctx = createTestContext({
|
|
164
|
+
requestId: 'test-001',
|
|
165
|
+
env: { TRAILS_ENV: 'test' },
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Logger that captures entries in memory
|
|
169
|
+
const logger = createTestLogger();
|
|
170
|
+
logger.info('hello');
|
|
171
|
+
logger.entries; // All captured records
|
|
172
|
+
logger.assertLogged('info', 'hello'); // Passes if any entry matches
|
|
173
|
+
logger.clear(); // Reset
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Surface Harnesses
|
|
177
|
+
|
|
178
|
+
**CLI harness** -- execute commands in-process and capture stdout/stderr:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { createCliHarness } from '@ontrails/testing';
|
|
182
|
+
|
|
183
|
+
const harness = createCliHarness({ app });
|
|
184
|
+
const result = await harness.run('entity show --name Alpha --output json');
|
|
185
|
+
|
|
186
|
+
expect(result.exitCode).toBe(0);
|
|
187
|
+
expect(result.json).toMatchObject({ name: 'Alpha' });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**MCP harness** -- invoke tools directly without transport:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { createMcpHarness } from '@ontrails/testing';
|
|
194
|
+
|
|
195
|
+
const harness = createMcpHarness({ app });
|
|
196
|
+
const result = await harness.callTool('myapp_entity_show', { name: 'Alpha' });
|
|
197
|
+
|
|
198
|
+
expect(result.isError).toBe(false);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Exports
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import {
|
|
205
|
+
testAll,
|
|
206
|
+
testExamples,
|
|
207
|
+
testTrail,
|
|
208
|
+
testHike,
|
|
209
|
+
testContracts,
|
|
210
|
+
testDetours,
|
|
211
|
+
createTestContext,
|
|
212
|
+
createTestLogger,
|
|
213
|
+
createCliHarness,
|
|
214
|
+
createMcpHarness,
|
|
215
|
+
} from '@ontrails/testing';
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Further Reading
|
|
219
|
+
|
|
220
|
+
- [Testing Guide](../../docs/testing.md)
|
|
221
|
+
- [Getting Started](../../docs/getting-started.md)
|
package/dist/all.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testAll — single-line governance suite for any Topo.
|
|
3
|
+
*
|
|
4
|
+
* Wraps topo validation, example execution, contract checks, and detour
|
|
5
|
+
* verification into one describe block.
|
|
6
|
+
*/
|
|
7
|
+
import type { Topo, TrailContext } from '@ontrails/core';
|
|
8
|
+
/**
|
|
9
|
+
* Run the full governance test suite for a Topo.
|
|
10
|
+
*
|
|
11
|
+
* Generates a `governance` describe block containing:
|
|
12
|
+
* - Structural validation via `validateTopo`
|
|
13
|
+
* - Example execution via `testExamples`
|
|
14
|
+
* - Output contract checks via `testContracts`
|
|
15
|
+
* - Detour target verification via `testDetours`
|
|
16
|
+
*
|
|
17
|
+
* Accepts either a static context or a factory function that produces a
|
|
18
|
+
* fresh context per test (useful when the context contains mutable state
|
|
19
|
+
* like an in-memory store).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { testAll } from '@ontrails/testing';
|
|
24
|
+
* import { app } from '../src/app.js';
|
|
25
|
+
*
|
|
26
|
+
* testAll(app);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const testAll: (topo: Topo, ctxOrFactory?: Partial<TrailContext> | (() => Partial<TrailContext>)) => void;
|
|
30
|
+
//# sourceMappingURL=all.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/all.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testAll — single-line governance suite for any Topo.
|
|
3
|
+
*
|
|
4
|
+
* Wraps topo validation, example execution, contract checks, and detour
|
|
5
|
+
* verification into one describe block.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, test } from 'bun:test';
|
|
8
|
+
import { validateTopo } from '@ontrails/core';
|
|
9
|
+
import { testContracts } from './contracts.js';
|
|
10
|
+
import { testDetours } from './detours.js';
|
|
11
|
+
import { testExamples } from './examples.js';
|
|
12
|
+
/**
|
|
13
|
+
* Run the full governance test suite for a Topo.
|
|
14
|
+
*
|
|
15
|
+
* Generates a `governance` describe block containing:
|
|
16
|
+
* - Structural validation via `validateTopo`
|
|
17
|
+
* - Example execution via `testExamples`
|
|
18
|
+
* - Output contract checks via `testContracts`
|
|
19
|
+
* - Detour target verification via `testDetours`
|
|
20
|
+
*
|
|
21
|
+
* Accepts either a static context or a factory function that produces a
|
|
22
|
+
* fresh context per test (useful when the context contains mutable state
|
|
23
|
+
* like an in-memory store).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { testAll } from '@ontrails/testing';
|
|
28
|
+
* import { app } from '../src/app.js';
|
|
29
|
+
*
|
|
30
|
+
* testAll(app);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export const testAll = (topo, ctxOrFactory) => {
|
|
34
|
+
describe('governance', () => {
|
|
35
|
+
test('topo validates', () => {
|
|
36
|
+
const result = validateTopo(topo);
|
|
37
|
+
expect(result.isOk()).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
// oxlint-disable-next-line jest/require-hook -- these generate describe/test blocks, not setup code
|
|
40
|
+
testExamples(topo, ctxOrFactory);
|
|
41
|
+
// oxlint-disable-next-line jest/require-hook -- these generate describe/test blocks, not setup code
|
|
42
|
+
testContracts(topo, ctxOrFactory);
|
|
43
|
+
// oxlint-disable-next-line jest/require-hook -- these generate describe/test blocks, not setup code
|
|
44
|
+
testDetours(topo);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=all.js.map
|
package/dist/all.js.map
ADDED
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive assertion logic for example-driven testing.
|
|
3
|
+
*
|
|
4
|
+
* Three tiers:
|
|
5
|
+
* 1. Full match — example has `expected` output
|
|
6
|
+
* 2. Schema-only — no expected output, no error
|
|
7
|
+
* 3. Error match — example declares an error class name
|
|
8
|
+
*/
|
|
9
|
+
import type { Result } from '@ontrails/core';
|
|
10
|
+
import type { z } from 'zod';
|
|
11
|
+
/**
|
|
12
|
+
* Assert that a Result is Ok and return its value.
|
|
13
|
+
*
|
|
14
|
+
* Eliminates the `if (result.isOk())` / `as unknown as` dance in tests.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const value = expectOk(result);
|
|
19
|
+
* expect(value.name).toBe('Alice');
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare const expectOk: <T, E>(result: Result<T, E>) => T;
|
|
23
|
+
/**
|
|
24
|
+
* Assert that a Result is Err and return its error.
|
|
25
|
+
*
|
|
26
|
+
* Eliminates the `if (result.isErr())` / `as unknown as` dance in tests.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const error = expectErr(result);
|
|
31
|
+
* expect(error).toBeInstanceOf(ValidationError);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare const expectErr: <T, E>(result: Result<T, E>) => E;
|
|
35
|
+
/**
|
|
36
|
+
* Assert that the result is ok and its value deep-equals the expected output.
|
|
37
|
+
*/
|
|
38
|
+
export declare const assertFullMatch: (result: Result<unknown, Error>, expected: unknown) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Assert that the result is ok and, if an output schema is provided,
|
|
41
|
+
* the value parses against it.
|
|
42
|
+
*/
|
|
43
|
+
export declare const assertSchemaMatch: (result: Result<unknown, Error>, outputSchema: z.ZodType | undefined) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Assert that the result is an error of the specified type, with optional
|
|
46
|
+
* message substring matching.
|
|
47
|
+
*/
|
|
48
|
+
export declare const assertErrorMatch: (result: Result<unknown, Error>, expectedError: new (...args: never[]) => Error, expectedMessage?: string) => void;
|
|
49
|
+
//# sourceMappingURL=assertions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAM7B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAG,CAGrD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAG,CAGtD,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,EAC9B,UAAU,OAAO,KAChB,IAGF,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,EAC9B,cAAc,CAAC,CAAC,OAAO,GAAG,SAAS,KAClC,IAUF,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,EAC9B,eAAe,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,EAC9C,kBAAkB,MAAM,KACvB,IAMF,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive assertion logic for example-driven testing.
|
|
3
|
+
*
|
|
4
|
+
* Three tiers:
|
|
5
|
+
* 1. Full match — example has `expected` output
|
|
6
|
+
* 2. Schema-only — no expected output, no error
|
|
7
|
+
* 3. Error match — example declares an error class name
|
|
8
|
+
*/
|
|
9
|
+
import { expect } from 'bun:test';
|
|
10
|
+
import { formatZodIssues } from '@ontrails/core';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Result narrowing helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Assert that a Result is Ok and return its value.
|
|
16
|
+
*
|
|
17
|
+
* Eliminates the `if (result.isOk())` / `as unknown as` dance in tests.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const value = expectOk(result);
|
|
22
|
+
* expect(value.name).toBe('Alice');
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const expectOk = (result) => {
|
|
26
|
+
expect(result.isOk()).toBe(true);
|
|
27
|
+
return result.value;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Assert that a Result is Err and return its error.
|
|
31
|
+
*
|
|
32
|
+
* Eliminates the `if (result.isErr())` / `as unknown as` dance in tests.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const error = expectErr(result);
|
|
37
|
+
* expect(error).toBeInstanceOf(ValidationError);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export const expectErr = (result) => {
|
|
41
|
+
expect(result.isErr()).toBe(true);
|
|
42
|
+
return result.error;
|
|
43
|
+
};
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Full Match
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* Assert that the result is ok and its value deep-equals the expected output.
|
|
49
|
+
*/
|
|
50
|
+
export const assertFullMatch = (result, expected) => {
|
|
51
|
+
const value = expectOk(result);
|
|
52
|
+
expect(value).toEqual(expected);
|
|
53
|
+
};
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Schema-Only Match
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Assert that the result is ok and, if an output schema is provided,
|
|
59
|
+
* the value parses against it.
|
|
60
|
+
*/
|
|
61
|
+
export const assertSchemaMatch = (result, outputSchema) => {
|
|
62
|
+
const value = expectOk(result);
|
|
63
|
+
if (outputSchema !== undefined) {
|
|
64
|
+
const parsed = outputSchema.safeParse(value);
|
|
65
|
+
if (!parsed.success) {
|
|
66
|
+
throw new Error(`Output does not match schema: ${formatZodIssues(parsed.error.issues).join('; ')}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Error Match
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Assert that the result is an error of the specified type, with optional
|
|
75
|
+
* message substring matching.
|
|
76
|
+
*/
|
|
77
|
+
export const assertErrorMatch = (result, expectedError, expectedMessage) => {
|
|
78
|
+
const error = expectErr(result);
|
|
79
|
+
expect(error).toBeInstanceOf(expectedError);
|
|
80
|
+
if (expectedMessage !== undefined) {
|
|
81
|
+
expect(error.message).toContain(expectedMessage);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAO,MAAoB,EAAK,EAAE;IACxD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,OAAQ,MAAkC,CAAC,KAAK,CAAC;AACnD,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAO,MAAoB,EAAK,EAAE;IACzD,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,OAAQ,MAAkC,CAAC,KAAK,CAAC;AACnD,CAAC,CAAC;AAEF,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,MAA8B,EAC9B,QAAiB,EACX,EAAE;IACR,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,MAA8B,EAC9B,YAAmC,EAC7B,EAAE;IACR,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,iCAAiC,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,MAA8B,EAC9B,aAA8C,EAC9C,eAAwB,EAClB,EAAE;IACR,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IAC5C,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test context factory for creating TrailContext instances suitable for testing.
|
|
3
|
+
*/
|
|
4
|
+
import type { TrailContext } from '@ontrails/core';
|
|
5
|
+
import type { TestTrailContextOptions } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Create a TrailContext with deterministic, test-friendly defaults.
|
|
8
|
+
*
|
|
9
|
+
* - `requestId`: `"test-request-001"` (deterministic)
|
|
10
|
+
* - `logger`: a `TestLogger` that captures entries
|
|
11
|
+
* - `signal`: a non-aborted AbortController signal
|
|
12
|
+
*/
|
|
13
|
+
export declare const createTestContext: (overrides?: TestTrailContextOptions) => TrailContext;
|
|
14
|
+
/**
|
|
15
|
+
* Merge a Partial<TrailContext> into a test context.
|
|
16
|
+
* Used internally when the public API accepts Partial<TrailContext>.
|
|
17
|
+
*/
|
|
18
|
+
export declare const mergeTestContext: (ctx?: Partial<TrailContext>) => TrailContext;
|
|
19
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test context factory for creating TrailContext instances suitable for testing.
|
|
3
|
+
*/
|
|
4
|
+
import { createTestLogger } from './logger.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// createTestContext
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
/**
|
|
9
|
+
* Create a TrailContext with deterministic, test-friendly defaults.
|
|
10
|
+
*
|
|
11
|
+
* - `requestId`: `"test-request-001"` (deterministic)
|
|
12
|
+
* - `logger`: a `TestLogger` that captures entries
|
|
13
|
+
* - `signal`: a non-aborted AbortController signal
|
|
14
|
+
*/
|
|
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(),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Merge a Partial<TrailContext> into a test context.
|
|
24
|
+
* Used internally when the public API accepts Partial<TrailContext>.
|
|
25
|
+
*/
|
|
26
|
+
export const mergeTestContext = (ctx) => {
|
|
27
|
+
if (ctx === undefined) {
|
|
28
|
+
return createTestContext();
|
|
29
|
+
}
|
|
30
|
+
const base = createTestContext();
|
|
31
|
+
return { ...base, ...ctx };
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
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 type { Topo, TrailContext } from '@ontrails/core';
|
|
9
|
+
/**
|
|
10
|
+
* Verify that every trail's implementation output matches its declared
|
|
11
|
+
* output schema. Catches implementation-schema drift.
|
|
12
|
+
*
|
|
13
|
+
* Trails without output schemas or examples are skipped.
|
|
14
|
+
*/
|
|
15
|
+
export declare const testContracts: (app: Topo, ctxOrFactory?: Partial<TrailContext> | (() => Partial<TrailContext>)) => void;
|
|
16
|
+
//# sourceMappingURL=contracts.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
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 { formatZodIssues, validateInput } from '@ontrails/core';
|
|
10
|
+
import { expectOk } from './assertions.js';
|
|
11
|
+
import { mergeTestContext } from './context.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Helpers
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const validateOutputSchema = (outputSchema, value, trailId, exampleName) => {
|
|
16
|
+
const parsed = outputSchema.safeParse(value);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
const issues = formatZodIssues(parsed.error.issues);
|
|
19
|
+
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)}`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// testContracts
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/**
|
|
26
|
+
* Verify that every trail's implementation output matches its declared
|
|
27
|
+
* output schema. Catches implementation-schema drift.
|
|
28
|
+
*
|
|
29
|
+
* Trails without output schemas or examples are skipped.
|
|
30
|
+
*/
|
|
31
|
+
export const testContracts = (app, ctxOrFactory) => {
|
|
32
|
+
const resolveCtx = typeof ctxOrFactory === 'function' ? ctxOrFactory : () => ctxOrFactory;
|
|
33
|
+
const trailEntries = [...app.trails];
|
|
34
|
+
describe('contracts', () => {
|
|
35
|
+
describe.each(trailEntries)('%s', (_id, trailDef) => {
|
|
36
|
+
const t = trailDef;
|
|
37
|
+
if (t.output === undefined) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (t.examples === undefined || t.examples.length === 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const { examples, output: outputSchema } = t;
|
|
44
|
+
const successExamples = examples.filter((e) => e.error === undefined);
|
|
45
|
+
test.each(successExamples)('contract: $name', async (example) => {
|
|
46
|
+
const testCtx = mergeTestContext(resolveCtx());
|
|
47
|
+
const validated = validateInput(t.input, example.input);
|
|
48
|
+
const validatedInput = expectOk(validated);
|
|
49
|
+
const result = await t.implementation(validatedInput, testCtx);
|
|
50
|
+
const resultValue = expectOk(result);
|
|
51
|
+
validateOutputSchema(outputSchema, resultValue, t.id, example.name);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testDetours — verify that all detour targets exist in the topo.
|
|
3
|
+
*
|
|
4
|
+
* Pure structural validation. No implementation execution needed.
|
|
5
|
+
*/
|
|
6
|
+
import type { Topo } from '@ontrails/core';
|
|
7
|
+
/**
|
|
8
|
+
* Verify that every trail's detour targets reference trails that
|
|
9
|
+
* actually exist in the app's topo.
|
|
10
|
+
*/
|
|
11
|
+
export declare const testDetours: (app: Topo) => void;
|
|
12
|
+
//# sourceMappingURL=detours.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detours.d.ts","sourceRoot":"","sources":["../src/detours.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAS,MAAM,gBAAgB,CAAC;AAMlD;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,IAAI,KAAG,IAyBvC,CAAC"}
|