@ontrails/testing 1.0.0-beta.1 → 1.0.0-beta.3
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-lint.log +1 -1
- package/CHANGELOG.md +36 -0
- package/dist/contracts.d.ts +3 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +16 -6
- package/dist/contracts.js.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/contracts.test.ts +26 -1
- package/src/contracts.ts +20 -7
package/.turbo/turbo-lint.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @ontrails/testing
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.3
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Bug fixes across all surface packages found via parallel Codex review.
|
|
8
|
+
|
|
9
|
+
**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.
|
|
10
|
+
|
|
11
|
+
**cli**: Switch blaze to parseAsync for proper async error handling, add boolean flag negation (--no-flag), and strict number parsing that rejects partial input.
|
|
12
|
+
|
|
13
|
+
**mcp**: Align BlobRef with core (including ReadableStream support) and detect tool-name collisions after normalization.
|
|
14
|
+
|
|
15
|
+
**testing**: Include hikes in testContracts validation, with follow-context awareness.
|
|
16
|
+
|
|
17
|
+
**warden**: Collect hike detour targets, validate detour refs in hike specs, and stop implementation-returns-result from walking into nested function bodies.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- @ontrails/core@1.0.0-beta.3
|
|
23
|
+
- @ontrails/cli@1.0.0-beta.3
|
|
24
|
+
- @ontrails/mcp@1.0.0-beta.3
|
|
25
|
+
- @ontrails/logging@1.0.0-beta.3
|
|
26
|
+
|
|
27
|
+
## 1.0.0-beta.2
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Fix workspace dependency resolution in published packages. Now using bun publish
|
|
32
|
+
which correctly replaces workspace:^ with actual version numbers.
|
|
33
|
+
- Updated dependencies
|
|
34
|
+
- @ontrails/core@1.0.0-beta.2
|
|
35
|
+
- @ontrails/cli@1.0.0-beta.2
|
|
36
|
+
- @ontrails/mcp@1.0.0-beta.2
|
|
37
|
+
- @ontrails/logging@1.0.0-beta.2
|
|
38
|
+
|
|
3
39
|
## 1.0.0-beta.1
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/dist/contracts.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* testContracts — output schema verification.
|
|
3
3
|
*
|
|
4
|
-
* For every trail that has both examples and an output schema,
|
|
4
|
+
* For every trail and hike that has both examples and an output schema,
|
|
5
5
|
* run each example and validate the implementation output against
|
|
6
6
|
* the declared schema.
|
|
7
7
|
*/
|
|
8
8
|
import type { Topo, TrailContext } from '@ontrails/core';
|
|
9
9
|
/**
|
|
10
|
-
* Verify that every trail
|
|
10
|
+
* Verify that every trail and hike implementation output matches its declared
|
|
11
11
|
* output schema. Catches implementation-schema drift.
|
|
12
12
|
*
|
|
13
|
-
* Trails without output schemas or examples are skipped.
|
|
13
|
+
* Trails and hikes without output schemas or examples are skipped.
|
|
14
14
|
*/
|
|
15
15
|
export declare const testContracts: (app: Topo, ctxOrFactory?: Partial<TrailContext> | (() => Partial<TrailContext>)) => void;
|
|
16
16
|
//# sourceMappingURL=contracts.d.ts.map
|
package/dist/contracts.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AA0C9E;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,IAAI,EACT,eAAe,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,KACnE,IAoCF,CAAC"}
|
package/dist/contracts.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* testContracts — output schema verification.
|
|
3
3
|
*
|
|
4
|
-
* For every trail that has both examples and an output schema,
|
|
4
|
+
* For every trail and hike that has both examples and an output schema,
|
|
5
5
|
* run each example and validate the implementation output against
|
|
6
6
|
* the declared schema.
|
|
7
7
|
*/
|
|
@@ -12,6 +12,14 @@ import { mergeTestContext } from './context.js';
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// Helpers
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
|
+
/** Check if a trail/hike requires follow() but the context doesn't provide it. */
|
|
16
|
+
const needsFollowContext = (t, resolveCtx) => {
|
|
17
|
+
const spec = t;
|
|
18
|
+
if (!spec.follows || spec.follows.length === 0) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return !resolveCtx()?.follow;
|
|
22
|
+
};
|
|
15
23
|
const validateOutputSchema = (outputSchema, value, trailId, exampleName) => {
|
|
16
24
|
const parsed = outputSchema.safeParse(value);
|
|
17
25
|
if (!parsed.success) {
|
|
@@ -23,23 +31,25 @@ const validateOutputSchema = (outputSchema, value, trailId, exampleName) => {
|
|
|
23
31
|
// testContracts
|
|
24
32
|
// ---------------------------------------------------------------------------
|
|
25
33
|
/**
|
|
26
|
-
* Verify that every trail
|
|
34
|
+
* Verify that every trail and hike implementation output matches its declared
|
|
27
35
|
* output schema. Catches implementation-schema drift.
|
|
28
36
|
*
|
|
29
|
-
* Trails without output schemas or examples are skipped.
|
|
37
|
+
* Trails and hikes without output schemas or examples are skipped.
|
|
30
38
|
*/
|
|
31
39
|
export const testContracts = (app, ctxOrFactory) => {
|
|
32
40
|
const resolveCtx = typeof ctxOrFactory === 'function' ? ctxOrFactory : () => ctxOrFactory;
|
|
33
|
-
const
|
|
41
|
+
const allEntries = app.list();
|
|
34
42
|
describe('contracts', () => {
|
|
35
|
-
describe.each(
|
|
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, resolveCtx)) {
|
|
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) => {
|
package/dist/contracts.js.map
CHANGED
|
@@ -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,
|
|
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,kFAAkF;AAClF,MAAM,kBAAkB,GAAG,CACzB,CAAU,EACV,UAAmD,EAC1C,EAAE;IACX,MAAM,IAAI,GAAG,CAAoC,CAAC;IAClD,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC;AAC/B,CAAC,CAAC;AAEF,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,UAAU,GAAG,GAAG,CAAC,IAAI,EAA+B,CAAC;IAE3D,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;QACzB,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE;YACrC,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;YACD,IAAI,kBAAkB,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;gBACtC,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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/testing",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts",
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@ontrails/cli": "
|
|
18
|
-
"@ontrails/core": "
|
|
19
|
-
"@ontrails/logging": "
|
|
20
|
-
"@ontrails/mcp": "
|
|
21
|
-
"zod": "
|
|
17
|
+
"@ontrails/cli": "^1.0.0-beta.0",
|
|
18
|
+
"@ontrails/core": "^1.0.0-beta.0",
|
|
19
|
+
"@ontrails/logging": "^1.0.0-beta.0",
|
|
20
|
+
"@ontrails/mcp": "^1.0.0-beta.0",
|
|
21
|
+
"zod": "^4.3.5"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import { Result, trail, topo } from '@ontrails/core';
|
|
3
|
+
import { Result, hike, trail, topo } from '@ontrails/core';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
6
6
|
import { testContracts } from '../contracts.js';
|
|
@@ -38,6 +38,26 @@ const noExamplesTrail = trail('noexamples', {
|
|
|
38
38
|
output: z.object({ value: z.number() }),
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Test hikes
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/** Hike whose implementation matches the output schema. */
|
|
46
|
+
const validHike = hike('hike.valid', {
|
|
47
|
+
examples: [
|
|
48
|
+
{
|
|
49
|
+
expected: { total: 3 },
|
|
50
|
+
input: { a: 1, b: 2 },
|
|
51
|
+
name: 'Valid hike output',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
follows: ['valid'],
|
|
55
|
+
implementation: (input: { a: number; b: number }) =>
|
|
56
|
+
Result.ok({ total: input.a + input.b }),
|
|
57
|
+
input: z.object({ a: z.number(), b: z.number() }),
|
|
58
|
+
output: z.object({ total: z.number() }),
|
|
59
|
+
});
|
|
60
|
+
|
|
41
61
|
// ---------------------------------------------------------------------------
|
|
42
62
|
// Tests
|
|
43
63
|
// ---------------------------------------------------------------------------
|
|
@@ -66,3 +86,8 @@ describe('testContracts: skips trails without examples', () => {
|
|
|
66
86
|
// Trail without examples is skipped -- no contract tests generated
|
|
67
87
|
});
|
|
68
88
|
});
|
|
89
|
+
|
|
90
|
+
describe('testContracts: validates hike output schemas', () => {
|
|
91
|
+
// eslint-disable-next-line jest/require-hook
|
|
92
|
+
testContracts(topo('test-app', { validHike } as Record<string, unknown>));
|
|
93
|
+
});
|
package/src/contracts.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* testContracts — output schema verification.
|
|
3
3
|
*
|
|
4
|
-
* For every trail that has both examples and an output schema,
|
|
4
|
+
* For every trail and hike that has both examples and an output schema,
|
|
5
5
|
* run each example and validate the implementation output against
|
|
6
6
|
* the declared schema.
|
|
7
7
|
*/
|
|
@@ -19,6 +19,18 @@ import { mergeTestContext } from './context.js';
|
|
|
19
19
|
// Helpers
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
|
+
/** Check if a trail/hike requires follow() but the context doesn't provide it. */
|
|
23
|
+
const needsFollowContext = (
|
|
24
|
+
t: unknown,
|
|
25
|
+
resolveCtx: () => Partial<TrailContext> | undefined
|
|
26
|
+
): boolean => {
|
|
27
|
+
const spec = t as { follows?: readonly string[] };
|
|
28
|
+
if (!spec.follows || spec.follows.length === 0) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return !resolveCtx()?.follow;
|
|
32
|
+
};
|
|
33
|
+
|
|
22
34
|
const validateOutputSchema = (
|
|
23
35
|
outputSchema: z.ZodType,
|
|
24
36
|
value: unknown,
|
|
@@ -39,10 +51,10 @@ const validateOutputSchema = (
|
|
|
39
51
|
// ---------------------------------------------------------------------------
|
|
40
52
|
|
|
41
53
|
/**
|
|
42
|
-
* Verify that every trail
|
|
54
|
+
* Verify that every trail and hike implementation output matches its declared
|
|
43
55
|
* output schema. Catches implementation-schema drift.
|
|
44
56
|
*
|
|
45
|
-
* Trails without output schemas or examples are skipped.
|
|
57
|
+
* Trails and hikes without output schemas or examples are skipped.
|
|
46
58
|
*/
|
|
47
59
|
export const testContracts = (
|
|
48
60
|
app: Topo,
|
|
@@ -50,18 +62,19 @@ export const testContracts = (
|
|
|
50
62
|
): void => {
|
|
51
63
|
const resolveCtx =
|
|
52
64
|
typeof ctxOrFactory === 'function' ? ctxOrFactory : () => ctxOrFactory;
|
|
53
|
-
const
|
|
65
|
+
const allEntries = app.list() as Trail<unknown, unknown>[];
|
|
54
66
|
|
|
55
67
|
describe('contracts', () => {
|
|
56
|
-
describe.each(
|
|
57
|
-
const t = trailDef as Trail<unknown, unknown>;
|
|
58
|
-
|
|
68
|
+
describe.each(allEntries)('$id', (t) => {
|
|
59
69
|
if (t.output === undefined) {
|
|
60
70
|
return;
|
|
61
71
|
}
|
|
62
72
|
if (t.examples === undefined || t.examples.length === 0) {
|
|
63
73
|
return;
|
|
64
74
|
}
|
|
75
|
+
if (needsFollowContext(t, resolveCtx)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
65
78
|
|
|
66
79
|
const { examples, output: outputSchema } = t;
|
|
67
80
|
const successExamples = examples.filter((e) => e.error === undefined);
|