@ontrails/testing 1.0.0-beta.8 → 1.0.0-beta.9
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 +28 -0
- package/README.md +19 -0
- package/dist/context.d.ts +20 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +25 -0
- package/dist/context.js.map +1 -1
- package/dist/harness-mcp.d.ts.map +1 -1
- package/dist/harness-mcp.js +5 -2
- package/dist/harness-mcp.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/context.test.ts +36 -1
- package/src/context.ts +42 -1
- package/src/harness-mcp.ts +5 -2
- package/src/index.ts +2 -1
package/.turbo/turbo-lint.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @ontrails/testing
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.9
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Consolidated improvements across all surface packages.
|
|
8
|
+
|
|
9
|
+
**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.
|
|
10
|
+
|
|
11
|
+
**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.
|
|
12
|
+
|
|
13
|
+
**mcp**: Return `Result` from `buildMcpTools()` on collision instead of throwing.
|
|
14
|
+
|
|
15
|
+
**cli**: Verify exception catching via centralized `executeTrail`.
|
|
16
|
+
|
|
17
|
+
**testing**: Follow context awareness improvements.
|
|
18
|
+
|
|
19
|
+
**warden**: Refactor rules as composable trails with examples.
|
|
20
|
+
|
|
21
|
+
**schema**: Error code and empty body fixes.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies
|
|
26
|
+
- @ontrails/core@1.0.0-beta.9
|
|
27
|
+
- @ontrails/cli@1.0.0-beta.9
|
|
28
|
+
- @ontrails/mcp@1.0.0-beta.9
|
|
29
|
+
- @ontrails/logging@1.0.0-beta.9
|
|
30
|
+
|
|
3
31
|
## 1.0.0-beta.8
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ testDetours(app); // Verify detour targets exist
|
|
|
32
32
|
| `testTrail(trail, scenarios)` | Custom scenarios for edge cases, error paths, and follow chains |
|
|
33
33
|
| `testContracts(topo, ctx?)` | Validate output against declared schemas |
|
|
34
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 |
|
|
35
36
|
| `createTestContext(options?)` | `TrailContext` with sensible test defaults |
|
|
36
37
|
| `createTestLogger()` | Logger that captures entries in memory for assertions |
|
|
37
38
|
| `createCliHarness(options)` | Execute CLI commands in-process, capture stdout/stderr |
|
|
@@ -67,6 +68,24 @@ testTrail(onboardTrail, [
|
|
|
67
68
|
]);
|
|
68
69
|
```
|
|
69
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
|
+
|
|
70
89
|
## Surface harnesses
|
|
71
90
|
|
|
72
91
|
```typescript
|
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, 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,6 +12,24 @@ 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
|
+
/**
|
|
19
|
+
* Create a mock `FollowFn` for testing composite trails.
|
|
20
|
+
*
|
|
21
|
+
* Returns preconfigured `Result` values keyed by trail ID. Calls to
|
|
22
|
+
* unregistered IDs return `Result.err` with a descriptive message.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const follow = createFollowContext({
|
|
27
|
+
* responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
|
|
28
|
+
* });
|
|
29
|
+
* const ctx = { ...createTestContext(), follow };
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const createFollowContext: (options?: CreateFollowContextOptions) => FollowFn;
|
|
14
33
|
/**
|
|
15
34
|
* Merge a Partial<TrailContext> into a test context.
|
|
16
35
|
* Used internally when the public API accepts Partial<TrailContext>.
|
package/dist/context.d.ts.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAM1D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC5B,YAAY,uBAAuB,KAClC,YAMD,CAAC;AAMH,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;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAU,0BAA0B,KACnC,QAcF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,OAAO,CAAC,YAAY,CAAC,KAAG,YAO9D,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 } from '@ontrails/core';
|
|
4
5
|
import { createTestLogger } from './logger.js';
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// createTestContext
|
|
@@ -19,6 +20,30 @@ export const createTestContext = (overrides) => ({
|
|
|
19
20
|
signal: overrides?.signal ?? new AbortController().signal,
|
|
20
21
|
workspaceRoot: overrides?.cwd ?? process.cwd(),
|
|
21
22
|
});
|
|
23
|
+
/**
|
|
24
|
+
* Create a mock `FollowFn` for testing composite trails.
|
|
25
|
+
*
|
|
26
|
+
* Returns preconfigured `Result` values keyed by trail ID. Calls to
|
|
27
|
+
* unregistered IDs return `Result.err` with a descriptive message.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const follow = createFollowContext({
|
|
32
|
+
* responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
|
|
33
|
+
* });
|
|
34
|
+
* const ctx = { ...createTestContext(), follow };
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const createFollowContext = (options) => {
|
|
38
|
+
const responses = options?.responses ?? {};
|
|
39
|
+
return (id, _input) => {
|
|
40
|
+
const response = responses[id];
|
|
41
|
+
if (response === undefined) {
|
|
42
|
+
return Promise.resolve(Result.err(new Error(`No mock response for follow("${id}")`)));
|
|
43
|
+
}
|
|
44
|
+
return Promise.resolve(response);
|
|
45
|
+
};
|
|
46
|
+
};
|
|
22
47
|
/**
|
|
23
48
|
* Merge a Partial<TrailContext> into a test context.
|
|
24
49
|
* Used internally when the public API accepts Partial<TrailContext>.
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,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;AAUH;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAoC,EAC1B,EAAE;IACZ,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC;IAC3C,OAAO,CAAI,EAAU,EAAE,MAAe,EAA6B,EAAE;QACnE,MAAM,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,OAAO,CAAC,OAAO,CACpB,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,gCAAgC,EAAE,IAAI,CAAC,CAG3D,CACF,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC,QAA4B,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-mcp.d.ts","sourceRoot":"","sources":["../src/harness-mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,iBAAiB,KAAG,
|
|
1
|
+
{"version":3,"file":"harness-mcp.d.ts","sourceRoot":"","sources":["../src/harness-mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,iBAAiB,KAAG,UAmC7D,CAAC"}
|
package/dist/harness-mcp.js
CHANGED
|
@@ -21,9 +21,12 @@ import { buildMcpTools } from '@ontrails/mcp';
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
export const createMcpHarness = (options) => {
|
|
24
|
-
const
|
|
24
|
+
const toolsResult = buildMcpTools(options.app);
|
|
25
|
+
if (toolsResult.isErr()) {
|
|
26
|
+
throw toolsResult.error;
|
|
27
|
+
}
|
|
25
28
|
const toolMap = new Map();
|
|
26
|
-
for (const tool of
|
|
29
|
+
for (const tool of toolsResult.value) {
|
|
27
30
|
toolMap.set(tool.name, tool);
|
|
28
31
|
}
|
|
29
32
|
return {
|
package/dist/harness-mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-mcp.js","sourceRoot":"","sources":["../src/harness-mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAA0B,EAAc,EAAE;IACzE,MAAM,
|
|
1
|
+
{"version":3,"file":"harness-mcp.js","sourceRoot":"","sources":["../src/harness-mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAA0B,EAAc,EAAE;IACzE,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,WAAW,CAAC,KAAK,CAAC;IAC1B,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6B,CAAC;IACrD,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,KAAK,CAAC,QAAQ,CACZ,IAAY,EACZ,IAA6B;YAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oBAC1D,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;gBACtC,aAAa,EAAE,SAAS;gBACxB,YAAY,EAAE,SAAS;gBACvB,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;aACjC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,10 +5,11 @@ export { testTrail } from './trail.js';
|
|
|
5
5
|
export { testContracts } from './contracts.js';
|
|
6
6
|
export { testDetours } from './detours.js';
|
|
7
7
|
export { assertErrorMatch, assertFullMatch, assertSchemaMatch, expectErr, expectOk, } from './assertions.js';
|
|
8
|
-
export { createTestContext } from './context.js';
|
|
8
|
+
export { createFollowContext, createTestContext } from './context.js';
|
|
9
9
|
export { createTestLogger } from './logger.js';
|
|
10
10
|
export { createCliHarness } from './harness-cli.js';
|
|
11
11
|
export { createMcpHarness } from './harness-mcp.js';
|
|
12
|
+
export type { CreateFollowContextOptions } from './context.js';
|
|
12
13
|
export type { TestFollowOptions } from './follows.js';
|
|
13
14
|
export type { FollowScenario, TestScenario, TestLogger, TestTrailContextOptions, CliHarness, CliHarnessOptions, CliHarnessResult, McpHarness, McpHarnessOptions, McpHarnessResult, } from './types.js';
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,YAAY,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,YAAY,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,uBAAuB,EACvB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export { testDetours } from './detours.js';
|
|
|
8
8
|
// Assertions
|
|
9
9
|
export { assertErrorMatch, assertFullMatch, assertSchemaMatch, expectErr, expectOk, } from './assertions.js';
|
|
10
10
|
// Mock factories
|
|
11
|
-
export { createTestContext } from './context.js';
|
|
11
|
+
export { createFollowContext, createTestContext } from './context.js';
|
|
12
12
|
export { createTestLogger } from './logger.js';
|
|
13
13
|
// Surface harnesses
|
|
14
14
|
export { createCliHarness } from './harness-cli.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,aAAa;AACb,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAEzB,iBAAiB;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,aAAa;AACb,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAEzB,iBAAiB;AACjB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,oBAAoB;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,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.9",
|
|
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": "^1.0.0-beta.
|
|
18
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
19
|
-
"@ontrails/logging": "^1.0.0-beta.
|
|
20
|
-
"@ontrails/mcp": "^1.0.0-beta.
|
|
17
|
+
"@ontrails/cli": "^1.0.0-beta.8",
|
|
18
|
+
"@ontrails/core": "^1.0.0-beta.8",
|
|
19
|
+
"@ontrails/logging": "^1.0.0-beta.8",
|
|
20
|
+
"@ontrails/mcp": "^1.0.0-beta.8",
|
|
21
21
|
"zod": "^4.3.5"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Result } from '@ontrails/core';
|
|
4
|
+
|
|
5
|
+
import { createFollowContext, createTestContext } from '../context.js';
|
|
4
6
|
import type { TestLogger } from '../types.js';
|
|
5
7
|
|
|
6
8
|
// ---------------------------------------------------------------------------
|
|
@@ -58,3 +60,36 @@ describe('createTestContext', () => {
|
|
|
58
60
|
expect(env).toEqual({ CUSTOM: '1', NODE_ENV: 'test' });
|
|
59
61
|
});
|
|
60
62
|
});
|
|
63
|
+
|
|
64
|
+
describe('createFollowContext', () => {
|
|
65
|
+
test('returns configured ok response for registered id', async () => {
|
|
66
|
+
const follow = createFollowContext({
|
|
67
|
+
responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
|
|
68
|
+
});
|
|
69
|
+
const result = await follow('entity.add', { name: 'Alpha' });
|
|
70
|
+
expect(result.unwrap()).toEqual({ id: '1', name: 'Alpha' });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('returns configured err response for registered id', async () => {
|
|
74
|
+
const err = new Error('conflict');
|
|
75
|
+
const follow = createFollowContext({
|
|
76
|
+
responses: { 'entity.add': Result.err(err) },
|
|
77
|
+
});
|
|
78
|
+
const result = await follow('entity.add', {});
|
|
79
|
+
expect(result.isErr()).toBe(true);
|
|
80
|
+
expect(result.error?.message).toBe('conflict');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('returns err with descriptive message for unregistered id', async () => {
|
|
84
|
+
const follow = createFollowContext();
|
|
85
|
+
const result = await follow('unknown.trail', {});
|
|
86
|
+
expect(result.isErr()).toBe(true);
|
|
87
|
+
expect(result.error?.message).toContain('unknown.trail');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('no options defaults to empty responses', async () => {
|
|
91
|
+
const follow = createFollowContext();
|
|
92
|
+
const result = await follow('any.id', {});
|
|
93
|
+
expect(result.isErr()).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
});
|
package/src/context.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Test context factory for creating TrailContext instances suitable for testing.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { TrailContext } from '@ontrails/core';
|
|
5
|
+
import type { FollowFn, TrailContext } from '@ontrails/core';
|
|
6
|
+
import { Result } from '@ontrails/core';
|
|
6
7
|
|
|
7
8
|
import { createTestLogger } from './logger.js';
|
|
8
9
|
import type { TestTrailContextOptions } from './types.js';
|
|
@@ -28,6 +29,46 @@ export const createTestContext = (
|
|
|
28
29
|
workspaceRoot: overrides?.cwd ?? process.cwd(),
|
|
29
30
|
});
|
|
30
31
|
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// createFollowContext
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
export interface CreateFollowContextOptions {
|
|
37
|
+
readonly responses?: Record<string, Result<unknown, Error>> | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a mock `FollowFn` for testing composite trails.
|
|
42
|
+
*
|
|
43
|
+
* Returns preconfigured `Result` values keyed by trail ID. Calls to
|
|
44
|
+
* unregistered IDs return `Result.err` with a descriptive message.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const follow = createFollowContext({
|
|
49
|
+
* responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
|
|
50
|
+
* });
|
|
51
|
+
* const ctx = { ...createTestContext(), follow };
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const createFollowContext = (
|
|
55
|
+
options?: CreateFollowContextOptions
|
|
56
|
+
): FollowFn => {
|
|
57
|
+
const responses = options?.responses ?? {};
|
|
58
|
+
return <O>(id: string, _input: unknown): Promise<Result<O, Error>> => {
|
|
59
|
+
const response = responses[id];
|
|
60
|
+
if (response === undefined) {
|
|
61
|
+
return Promise.resolve(
|
|
62
|
+
Result.err(new Error(`No mock response for follow("${id}")`)) as Result<
|
|
63
|
+
O,
|
|
64
|
+
Error
|
|
65
|
+
>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return Promise.resolve(response as Result<O, Error>);
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
31
72
|
/**
|
|
32
73
|
* Merge a Partial<TrailContext> into a test context.
|
|
33
74
|
* Used internally when the public API accepts Partial<TrailContext>.
|
package/src/harness-mcp.ts
CHANGED
|
@@ -31,9 +31,12 @@ import type {
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
export const createMcpHarness = (options: McpHarnessOptions): McpHarness => {
|
|
34
|
-
const
|
|
34
|
+
const toolsResult = buildMcpTools(options.app);
|
|
35
|
+
if (toolsResult.isErr()) {
|
|
36
|
+
throw toolsResult.error;
|
|
37
|
+
}
|
|
35
38
|
const toolMap = new Map<string, McpToolDefinition>();
|
|
36
|
-
for (const tool of
|
|
39
|
+
for (const tool of toolsResult.value) {
|
|
37
40
|
toolMap.set(tool.name, tool);
|
|
38
41
|
}
|
|
39
42
|
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ export {
|
|
|
16
16
|
} from './assertions.js';
|
|
17
17
|
|
|
18
18
|
// Mock factories
|
|
19
|
-
export { createTestContext } from './context.js';
|
|
19
|
+
export { createFollowContext, createTestContext } from './context.js';
|
|
20
20
|
export { createTestLogger } from './logger.js';
|
|
21
21
|
|
|
22
22
|
// Surface harnesses
|
|
@@ -24,6 +24,7 @@ export { createCliHarness } from './harness-cli.js';
|
|
|
24
24
|
export { createMcpHarness } from './harness-mcp.js';
|
|
25
25
|
|
|
26
26
|
// Types
|
|
27
|
+
export type { CreateFollowContextOptions } from './context.js';
|
|
27
28
|
export type { TestFollowOptions } from './follows.js';
|
|
28
29
|
|
|
29
30
|
export type {
|