@ontrails/testing 1.0.0-beta.10 → 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.
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +28 -0
- package/dist/all.d.ts +2 -1
- package/dist/all.d.ts.map +1 -1
- package/dist/all.js.map +1 -1
- package/dist/context.d.ts +9 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +47 -13
- package/dist/context.js.map +1 -1
- package/dist/contracts.d.ts +2 -1
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +13 -8
- package/dist/contracts.js.map +1 -1
- package/dist/examples.d.ts +2 -1
- package/dist/examples.d.ts.map +1 -1
- package/dist/examples.js +28 -21
- package/dist/examples.js.map +1 -1
- package/dist/follows.d.ts +7 -1
- package/dist/follows.d.ts.map +1 -1
- package/dist/follows.js +60 -17
- package/dist/follows.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/all.test.ts +64 -0
- package/src/__tests__/context.test.ts +58 -3
- package/src/__tests__/contracts.test.ts +92 -1
- package/src/__tests__/examples.test.ts +265 -1
- package/src/__tests__/follows.test.ts +427 -1
- package/src/all.ts +5 -1
- package/src/context.ts +82 -15
- package/src/contracts.ts +28 -10
- package/src/examples.ts +63 -21
- package/src/follows.ts +112 -17
- package/src/index.ts +1 -0
package/dist/follows.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"follows.js","sourceRoot":"","sources":["../src/follows.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"follows.js","sourceRoot":"","sources":["../src/follows.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAQlD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,MAAM,EACN,eAAe,EACf,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAYvE,MAAM,uBAAuB,GAAG,CAC9B,QAAkB,EAClB,SAAoD,EAC9B,EAAE;IACxB,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,QAAQ,GAAmC,EAAE,CAAC;IAEpD,MAAM,OAAO,GAAG,CAAC,SAAmB,EAAQ,EAAE;QAC5C,KAAK,MAAM,eAAe,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;YACjD,IAAI,cAAc,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC3C,SAAS;YACX,CAAC;YACD,cAAc,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,SAAmB,EAAQ,EAAE;QAC1C,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACnC,OAAO;QACT,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC/B,OAAO,CAAC,SAAS,CAAC,CAAC;QACnB,KAAK,MAAM,UAAU,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,aAAa,GAAG,SAAS,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;YACjD,IAAI,aAAa,EAAE,CAAC;gBAClB,KAAK,CAAC,aAAa,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChB,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,KAAK,EAC/B,QAAkB,EAClB,SAAoD,EACvB,EAAE;IAC/B,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAE7C,KAAK,MAAM,eAAe,IAAI,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3E,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,CAAC;IAC9D,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;GAEG;AACH,MAAM,gBAAgB,GAAG,CACvB,QAAkB,EAClB,WAAmB,EACC,EAAE;IACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CACrC,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,KAAK,KAAK,SAAS;QACtB,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAC3E,CAAC;IACF,OAAO,OAAO,EAAE,KAAK,CAAC;AACxB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,cAAc,GAAG,CACrB,EAAU,EACV,QAAwB,EACxB,SAAoD,EAChB,EAAE;IACtC,MAAM,SAAS,GAAG,QAAQ,CAAC,iBAAiB,EAAE,CAAC,EAAE,CAAC,CAAC;IACnD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACpC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,GAAG,CACf,IAAI,aAAa,CAAC,yBAAyB,EAAE,eAAe,CAAC,CAC9D,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACxD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC,GAAG,CACf,IAAI,aAAa,CACf,8BAA8B,SAAS,eAAe,EAAE,GAAG,CAC5D,CACF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAG,CACrB,EAAU,EACV,KAAc,EACd,SAAoD,EACpD,GAAiB,EACjB,QAAwC,EACxC,MAAiB,EACqD,EAAE;IACxE,MAAM,QAAQ,GAAG,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACpC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACpD,OAAO,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE;QACnC,GAAG,EAAE,SAAS;QACd,QAAQ;KACT,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAC5B,KAAqB,EACrB,QAAwB,EACxB,SAAoD,EACpD,UAAgC,EAChC,GAAiB,EACjB,QAAwC,EAC9B,EAAE;IACZ,mEAAmE;IACnE,oEAAoE;IACpE,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,KAAc,EAAE,EAAE;QAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAE1B,MAAM,QAAQ,GAAG,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,MAAM,QAAQ,GAAG,cAAc,CAC7B,EAAE,EACF,KAAK,EACL,SAAS,EACT,GAAG,EACH,QAAQ,EACR,MAAkB,CACnB,CAAC;QACF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC;IACF,OAAO,MAAkB,CAAC;AAC5B,CAAC,CAAC;AAEF,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,MAAM,oBAAoB,GAAG,CAC3B,MAA8B,EAC9B,QAAwB,EACxB,QAAkB,EACZ,EAAE;IACR,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACvC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChD,CAAC;SAAM,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5C,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC1E,CAAC;SAAM,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;YACnB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;SAAM,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACxB,KAA8B,EAC9B,QAAwB,EAClB,EAAE;IACR,IAAI,QAAQ,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,QAAQ,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;YAC3B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAC5B,SAAiC,EACjC,QAAwB,EACf,EAAE;IACX,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,KAAK,eAAe,EAAE,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QACxD,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC5C,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,KAAK,CACb,yCAAyC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,CACnE,CAAC;AACJ,CAAC,CAAC;AAEF,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,MAAM,gBAAgB,GAAG,CACvB,QAAwB,EACxB,GAAsC,EACtC,SAAoD,EACpD,QAAwC,EACU,EAAE;IACpD,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,qBAAqB,CAClC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,OAAO,CAAC,MAAM,EACd,OAAO,EACP,QAAQ,CACT,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,KAAK,EACvB,QAAkB,EAClB,QAAwB,EACxB,GAAsC,EACtC,SAAoD,EACpD,QAAwC,EACzB,EAAE;IACjB,MAAM,SAAS,GAAG,aAAa,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChE,IAAI,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,gBAAgB,CACzC,QAAQ,EACR,GAAG,EACH,SAAS,EACT,QAAQ,CACT,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE;QAC1D,GAAG,EAAE,OAAO;QACZ,QAAQ;KACT,CAAC,CAAC;IACH,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnC,oBAAoB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC,CAAC;AAoBF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,QAAkB,EAClB,SAAoC,EACpC,OAA2B,EACrB,EAAE;IACR,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE;QACzB,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CACvB,cAAc,EACd,KAAK,EAAE,QAAwB,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,qBAAqB,CACpC,MAAM,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EACpD,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,QAAQ,CAClB,CAAC;YACF,MAAM,WAAW,CACf,QAAQ,EACR,QAAQ,EACR,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,MAAM,EACf,QAAQ,CACT,CAAC;QACJ,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { createTestLogger } from './logger.js';
|
|
|
10
10
|
export { createCliHarness } from './harness-cli.js';
|
|
11
11
|
export { createMcpHarness } from './harness-mcp.js';
|
|
12
12
|
export type { CreateFollowContextOptions } from './context.js';
|
|
13
|
+
export type { TestExecutionOptions } from './context.js';
|
|
13
14
|
export type { TestFollowOptions } from './follows.js';
|
|
14
15
|
export type { FollowScenario, TestScenario, TestLogger, TestTrailContextOptions, CliHarness, CliHarnessOptions, CliHarnessResult, McpHarness, McpHarnessOptions, McpHarnessResult, } from './types.js';
|
|
15
16
|
//# 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,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"}
|
|
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,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,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/package.json
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { Result, service, trail, topo } from '@ontrails/core';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
import { testAll } from '../all.js';
|
|
7
|
+
|
|
8
|
+
const mockDbService = service('db.mock.all', {
|
|
9
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
10
|
+
mock: () => ({ source: 'mock' }),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const mockedTrail = trail('service.mocked.all', {
|
|
14
|
+
description: 'Trail that uses a mocked service through testAll',
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
expected: { source: 'mock' },
|
|
18
|
+
input: {},
|
|
19
|
+
name: 'Uses auto-resolved service mock',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
input: z.object({}),
|
|
23
|
+
output: z.object({ source: z.string() }),
|
|
24
|
+
run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
|
|
25
|
+
services: [mockDbService],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const overrideTrail = trail('service.override.all', {
|
|
29
|
+
description: 'Trail that prefers explicit overrides over mock factories',
|
|
30
|
+
examples: [
|
|
31
|
+
{
|
|
32
|
+
expected: { source: 'override' },
|
|
33
|
+
input: {},
|
|
34
|
+
name: 'Explicit service override wins',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
input: z.object({}),
|
|
38
|
+
output: z.object({ source: z.string() }),
|
|
39
|
+
run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
|
|
40
|
+
services: [mockDbService],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('testAll service mocks', () => {
|
|
44
|
+
// eslint-disable-next-line jest/require-hook
|
|
45
|
+
testAll(
|
|
46
|
+
topo('test-all-service-mock-app', {
|
|
47
|
+
mockDbService,
|
|
48
|
+
mockedTrail,
|
|
49
|
+
} as Record<string, unknown>)
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('testAll explicit service overrides', () => {
|
|
54
|
+
// eslint-disable-next-line jest/require-hook
|
|
55
|
+
testAll(
|
|
56
|
+
topo('test-all-service-override-app', {
|
|
57
|
+
mockDbService,
|
|
58
|
+
overrideTrail,
|
|
59
|
+
} as Record<string, unknown>),
|
|
60
|
+
{
|
|
61
|
+
services: { 'db.mock.all': { source: 'override' } },
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
});
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import { Result } from '@ontrails/core';
|
|
3
|
+
import { Result, service, topo } from '@ontrails/core';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createFollowContext,
|
|
7
|
+
createTestContext,
|
|
8
|
+
mergeTestContext,
|
|
9
|
+
resolveMockServices,
|
|
10
|
+
} from '../context.js';
|
|
6
11
|
import type { TestLogger } from '../types.js';
|
|
7
12
|
|
|
8
13
|
// ---------------------------------------------------------------------------
|
|
@@ -12,11 +17,12 @@ import type { TestLogger } from '../types.js';
|
|
|
12
17
|
describe('createTestContext', () => {
|
|
13
18
|
test('produces a valid TrailContext with no args', () => {
|
|
14
19
|
const ctx = createTestContext();
|
|
20
|
+
expect(ctx.cwd).toBe(process.cwd());
|
|
15
21
|
expect(ctx.requestId).toBe('test-request-001');
|
|
16
22
|
expect(ctx.signal).toBeDefined();
|
|
17
23
|
expect(ctx.signal.aborted).toBe(false);
|
|
18
24
|
expect(ctx.logger).toBeDefined();
|
|
19
|
-
expect(ctx.workspaceRoot).
|
|
25
|
+
expect(ctx.workspaceRoot).toBe(process.cwd());
|
|
20
26
|
});
|
|
21
27
|
|
|
22
28
|
test('default logger is a TestLogger', () => {
|
|
@@ -49,6 +55,7 @@ describe('createTestContext', () => {
|
|
|
49
55
|
|
|
50
56
|
test('overrides cwd', () => {
|
|
51
57
|
const ctx = createTestContext({ cwd: '/tmp/test' });
|
|
58
|
+
expect(ctx.cwd).toBe('/tmp/test');
|
|
52
59
|
expect(ctx.workspaceRoot).toBe('/tmp/test');
|
|
53
60
|
});
|
|
54
61
|
|
|
@@ -93,3 +100,51 @@ describe('createFollowContext', () => {
|
|
|
93
100
|
expect(result.isErr()).toBe(true);
|
|
94
101
|
});
|
|
95
102
|
});
|
|
103
|
+
|
|
104
|
+
describe('mergeTestContext', () => {
|
|
105
|
+
test('merges service overrides into extensions and the service accessor', () => {
|
|
106
|
+
const ctx = mergeTestContext(
|
|
107
|
+
{
|
|
108
|
+
extensions: { existing: true },
|
|
109
|
+
},
|
|
110
|
+
{ 'db.main': { source: 'mock' } }
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(ctx.extensions).toEqual({
|
|
114
|
+
'db.main': { source: 'mock' },
|
|
115
|
+
existing: true,
|
|
116
|
+
});
|
|
117
|
+
expect(ctx.service<{ source: string }>('db.main').source).toBe('mock');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('resolveMockServices', () => {
|
|
122
|
+
test('creates fresh mock services for each invocation', async () => {
|
|
123
|
+
let mockCalls = 0;
|
|
124
|
+
const mockable = service(`service.mock.${Bun.randomUUIDv7()}`, {
|
|
125
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
126
|
+
mock: () => {
|
|
127
|
+
mockCalls += 1;
|
|
128
|
+
return Promise.resolve({ source: 'mock' });
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
const app = topo('mock-app', { mockable } as Record<string, unknown>);
|
|
132
|
+
|
|
133
|
+
const first = await resolveMockServices(app);
|
|
134
|
+
const second = await resolveMockServices(app);
|
|
135
|
+
|
|
136
|
+
expect(first).toEqual({ [mockable.id]: { source: 'mock' } });
|
|
137
|
+
expect(second).toEqual(first);
|
|
138
|
+
expect(second[mockable.id]).not.toBe(first[mockable.id]);
|
|
139
|
+
expect(mockCalls).toBe(2);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('skips services without mock factories', async () => {
|
|
143
|
+
const plain = service(`service.plain.${Bun.randomUUIDv7()}`, {
|
|
144
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
145
|
+
});
|
|
146
|
+
const app = topo('plain-app', { plain } as Record<string, unknown>);
|
|
147
|
+
|
|
148
|
+
expect(await resolveMockServices(app)).toEqual({});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -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, service, trail, topo } from '@ontrails/core';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
6
6
|
import { testContracts } from '../contracts.js';
|
|
@@ -57,6 +57,63 @@ const compositionTrail = trail('composition.valid', {
|
|
|
57
57
|
Result.ok({ total: input.a + input.b }),
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
const transformedInputTrail = trail('contract.transformed', {
|
|
61
|
+
examples: [
|
|
62
|
+
{
|
|
63
|
+
expected: { value: 2 },
|
|
64
|
+
input: { value: '1' },
|
|
65
|
+
name: 'Raw contract input is only transformed once',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
input: z
|
|
69
|
+
.object({ value: z.string() })
|
|
70
|
+
.transform(({ value }) => ({ value: Number(value) + 1 })),
|
|
71
|
+
output: z.object({ value: z.number() }),
|
|
72
|
+
run: (input: { value: number }) => Result.ok({ value: input.value }),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const undeclaredContractDbService = service('db.undeclared.contracts', {
|
|
76
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
77
|
+
mock: () => ({ source: 'mock' }),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const undeclaredContractTrail = trail('service.undeclared.contracts', {
|
|
81
|
+
examples: [
|
|
82
|
+
{
|
|
83
|
+
expected: { hasInjectedService: false },
|
|
84
|
+
input: {},
|
|
85
|
+
name: 'Undeclared services are not preloaded into contract contexts',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
input: z.object({}),
|
|
89
|
+
output: z.object({ hasInjectedService: z.literal(false) }),
|
|
90
|
+
run: (_input, ctx) =>
|
|
91
|
+
Result.ok({
|
|
92
|
+
hasInjectedService:
|
|
93
|
+
ctx.extensions?.[undeclaredContractDbService.id] !== undefined,
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const ctxOverrideContractService = service('db.mock.contracts', {
|
|
98
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
99
|
+
mock: () => ({ source: 'mock' }),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const ctxOverrideContractTrail = trail('service.ctx.contracts', {
|
|
103
|
+
examples: [
|
|
104
|
+
{
|
|
105
|
+
expected: { source: 'ctx' },
|
|
106
|
+
input: {},
|
|
107
|
+
name: 'Context extensions beat auto-resolved contract mocks',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
input: z.object({}),
|
|
111
|
+
output: z.object({ source: z.literal('ctx') }),
|
|
112
|
+
run: (_input, ctx) =>
|
|
113
|
+
Result.ok({ source: ctxOverrideContractService.from(ctx).source }),
|
|
114
|
+
services: [ctxOverrideContractService],
|
|
115
|
+
});
|
|
116
|
+
|
|
60
117
|
// ---------------------------------------------------------------------------
|
|
61
118
|
// Tests
|
|
62
119
|
// ---------------------------------------------------------------------------
|
|
@@ -92,3 +149,37 @@ describe('testContracts: validates composition trail output schemas', () => {
|
|
|
92
149
|
topo('test-app', { compositionTrail } as Record<string, unknown>)
|
|
93
150
|
);
|
|
94
151
|
});
|
|
152
|
+
|
|
153
|
+
describe('testContracts: raw transformed input', () => {
|
|
154
|
+
// eslint-disable-next-line jest/require-hook
|
|
155
|
+
testContracts(
|
|
156
|
+
topo('transformed-contract-app', {
|
|
157
|
+
transformedInputTrail,
|
|
158
|
+
} as Record<string, unknown>)
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('testContracts: context extension overrides', () => {
|
|
163
|
+
// eslint-disable-next-line jest/require-hook
|
|
164
|
+
testContracts(
|
|
165
|
+
topo('ctx-contract-app', {
|
|
166
|
+
ctxOverrideContractService,
|
|
167
|
+
ctxOverrideContractTrail,
|
|
168
|
+
} as Record<string, unknown>),
|
|
169
|
+
{
|
|
170
|
+
ctx: {
|
|
171
|
+
extensions: { 'db.mock.contracts': { source: 'ctx' } },
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('testContracts service declarations', () => {
|
|
178
|
+
// eslint-disable-next-line jest/require-hook
|
|
179
|
+
testContracts(
|
|
180
|
+
topo('undeclared-contract-service-app', {
|
|
181
|
+
undeclaredContractDbService,
|
|
182
|
+
undeclaredContractTrail,
|
|
183
|
+
} as Record<string, unknown>)
|
|
184
|
+
);
|
|
185
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import { NotFoundError, Result, trail, topo } from '@ontrails/core';
|
|
3
|
+
import { NotFoundError, Result, service, trail, topo } from '@ontrails/core';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
6
6
|
import { testExamples } from '../examples.js';
|
|
@@ -67,6 +67,136 @@ const noExamplesTrail = trail('noexamples', {
|
|
|
67
67
|
run: (input: { x: number }) => Result.ok(input.x * 2),
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
const mockDbService = service('db.mock.examples', {
|
|
71
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
72
|
+
mock: () => ({ source: 'mock' }),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const mockServiceTrail = trail('service.mocked', {
|
|
76
|
+
description: 'Trail that reads from a mocked service',
|
|
77
|
+
examples: [
|
|
78
|
+
{
|
|
79
|
+
expected: { source: 'mock' },
|
|
80
|
+
input: {},
|
|
81
|
+
name: 'Uses auto-resolved service mock',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
input: z.object({}),
|
|
85
|
+
output: z.object({ source: z.string() }),
|
|
86
|
+
run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
|
|
87
|
+
services: [mockDbService],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const explicitOverrideTrail = trail('service.override', {
|
|
91
|
+
description: 'Trail whose service mock can be overridden explicitly',
|
|
92
|
+
examples: [
|
|
93
|
+
{
|
|
94
|
+
expected: { source: 'override' },
|
|
95
|
+
input: {},
|
|
96
|
+
name: 'Explicit service override wins over mock factory',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
input: z.object({}),
|
|
100
|
+
output: z.object({ source: z.string() }),
|
|
101
|
+
run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
|
|
102
|
+
services: [mockDbService],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const transformedInputTrail = trail('example.transformed', {
|
|
106
|
+
description: 'Trail whose input schema transforms once',
|
|
107
|
+
examples: [
|
|
108
|
+
{
|
|
109
|
+
expected: { value: 2 },
|
|
110
|
+
input: { value: '1' },
|
|
111
|
+
name: 'Raw example input is only transformed once',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
input: z
|
|
115
|
+
.object({ value: z.string() })
|
|
116
|
+
.transform(({ value }) => ({ value: Number(value) + 1 })),
|
|
117
|
+
output: z.object({ value: z.number() }),
|
|
118
|
+
run: (input: { value: number }) => Result.ok({ value: input.value }),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const ctxOverrideTrail = trail('service.ctx-override', {
|
|
122
|
+
description: 'Trail whose service mock can be overridden by ctx.extensions',
|
|
123
|
+
examples: [
|
|
124
|
+
{
|
|
125
|
+
expected: { source: 'ctx' },
|
|
126
|
+
input: {},
|
|
127
|
+
name: 'Context extensions beat auto-resolved mock services',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
input: z.object({}),
|
|
131
|
+
output: z.object({ source: z.string() }),
|
|
132
|
+
run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
|
|
133
|
+
services: [mockDbService],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const undeclaredDbService = service('db.undeclared.examples', {
|
|
137
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
138
|
+
mock: () => ({ source: 'mock' }),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const undeclaredServiceTrail = trail('service.undeclared.examples', {
|
|
142
|
+
description: 'Trail that uses a service without declaring it',
|
|
143
|
+
examples: [
|
|
144
|
+
{
|
|
145
|
+
error: 'InternalError',
|
|
146
|
+
input: {},
|
|
147
|
+
name: 'Undeclared services stay unavailable during example execution',
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
input: z.object({}),
|
|
151
|
+
output: z.object({ source: z.string() }),
|
|
152
|
+
run: (_input, ctx) =>
|
|
153
|
+
Result.ok({ source: undeclaredDbService.from(ctx).source }),
|
|
154
|
+
});
|
|
155
|
+
const followedDbService = service('db.mock.examples.follow', {
|
|
156
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
157
|
+
mock: () => ({ source: 'mock' }),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const followedLeafTrail = trail('service.follow.leaf', {
|
|
161
|
+
description: 'Leaf trail that resolves a service inside a follow chain',
|
|
162
|
+
input: z.object({}),
|
|
163
|
+
output: z.object({ childSource: z.string() }),
|
|
164
|
+
run: (_input, ctx) =>
|
|
165
|
+
Result.ok({ childSource: followedDbService.from(ctx).source }),
|
|
166
|
+
services: [followedDbService],
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const followedRootTrail = trail('service.follow.root', {
|
|
170
|
+
description: 'Root trail that follows a child trail using services',
|
|
171
|
+
examples: [
|
|
172
|
+
{
|
|
173
|
+
expected: { childSource: 'mock', rootSource: 'mock' },
|
|
174
|
+
input: {},
|
|
175
|
+
name: 'Propagates service mocks through follow execution',
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
follow: ['service.follow.leaf'],
|
|
179
|
+
input: z.object({}),
|
|
180
|
+
output: z.object({ childSource: z.string(), rootSource: z.string() }),
|
|
181
|
+
run: async (_input, ctx) => {
|
|
182
|
+
if (!ctx.follow) {
|
|
183
|
+
return Result.err(new Error('follow not available'));
|
|
184
|
+
}
|
|
185
|
+
const childResult = await ctx.follow<{ childSource: string }>(
|
|
186
|
+
'service.follow.leaf',
|
|
187
|
+
{}
|
|
188
|
+
);
|
|
189
|
+
if (childResult.isErr()) {
|
|
190
|
+
return childResult;
|
|
191
|
+
}
|
|
192
|
+
return Result.ok({
|
|
193
|
+
childSource: childResult.value.childSource,
|
|
194
|
+
rootSource: followedDbService.from(ctx).source,
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
services: [followedDbService],
|
|
198
|
+
});
|
|
199
|
+
|
|
70
200
|
// ---------------------------------------------------------------------------
|
|
71
201
|
// Composition trails (for follow coverage)
|
|
72
202
|
// ---------------------------------------------------------------------------
|
|
@@ -163,6 +293,63 @@ describe('testExamples skips trails with no examples', () => {
|
|
|
163
293
|
});
|
|
164
294
|
});
|
|
165
295
|
|
|
296
|
+
describe('testExamples service mocks', () => {
|
|
297
|
+
// eslint-disable-next-line jest/require-hook
|
|
298
|
+
testExamples(
|
|
299
|
+
topo('service-mock-app', {
|
|
300
|
+
mockDbService,
|
|
301
|
+
mockServiceTrail,
|
|
302
|
+
} as Record<string, unknown>)
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe('testExamples explicit service overrides', () => {
|
|
307
|
+
// eslint-disable-next-line jest/require-hook
|
|
308
|
+
testExamples(
|
|
309
|
+
topo('service-override-app', {
|
|
310
|
+
explicitOverrideTrail,
|
|
311
|
+
mockDbService,
|
|
312
|
+
} as Record<string, unknown>),
|
|
313
|
+
{
|
|
314
|
+
services: { 'db.mock.examples': { source: 'override' } },
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe('testExamples raw transformed input', () => {
|
|
320
|
+
// eslint-disable-next-line jest/require-hook
|
|
321
|
+
testExamples(
|
|
322
|
+
topo('transformed-input-app', {
|
|
323
|
+
transformedInputTrail,
|
|
324
|
+
} as Record<string, unknown>)
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
describe('testExamples context extension overrides', () => {
|
|
329
|
+
// eslint-disable-next-line jest/require-hook
|
|
330
|
+
testExamples(
|
|
331
|
+
topo('ctx-override-app', {
|
|
332
|
+
ctxOverrideTrail,
|
|
333
|
+
mockDbService,
|
|
334
|
+
} as Record<string, unknown>),
|
|
335
|
+
{
|
|
336
|
+
ctx: {
|
|
337
|
+
extensions: { 'db.mock.examples': { source: 'ctx' } },
|
|
338
|
+
},
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe('testExamples service declarations', () => {
|
|
344
|
+
// eslint-disable-next-line jest/require-hook
|
|
345
|
+
testExamples(
|
|
346
|
+
topo('undeclared-service-app', {
|
|
347
|
+
undeclaredDbService,
|
|
348
|
+
undeclaredServiceTrail,
|
|
349
|
+
} as Record<string, unknown>)
|
|
350
|
+
);
|
|
351
|
+
});
|
|
352
|
+
|
|
166
353
|
describe('testExamples follow coverage for composition trails', () => {
|
|
167
354
|
// eslint-disable-next-line jest/require-hook
|
|
168
355
|
testExamples(
|
|
@@ -173,3 +360,80 @@ describe('testExamples follow coverage for composition trails', () => {
|
|
|
173
360
|
} as Record<string, unknown>)
|
|
174
361
|
);
|
|
175
362
|
});
|
|
363
|
+
|
|
364
|
+
describe('testExamples service mocks through follow', () => {
|
|
365
|
+
// eslint-disable-next-line jest/require-hook
|
|
366
|
+
testExamples(
|
|
367
|
+
topo('service-follow-app', {
|
|
368
|
+
followedDbService,
|
|
369
|
+
followedLeafTrail,
|
|
370
|
+
followedRootTrail,
|
|
371
|
+
} as Record<string, unknown>)
|
|
372
|
+
);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
// Nested follow chain: A → B → C
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
|
|
379
|
+
const leafTrail = trail('step.leaf', {
|
|
380
|
+
description: 'Leaf trail in a nested chain',
|
|
381
|
+
input: z.object({ value: z.string() }),
|
|
382
|
+
output: z.object({ leaf: z.string() }),
|
|
383
|
+
run: (input: { value: string }) => Result.ok({ leaf: input.value }),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const middleTrail = trail('step.middle', {
|
|
387
|
+
description: 'Middle trail that follows the leaf',
|
|
388
|
+
follow: ['step.leaf'],
|
|
389
|
+
input: z.object({ value: z.string() }),
|
|
390
|
+
output: z.object({ middle: z.string() }),
|
|
391
|
+
run: async (input: { value: string }, ctx) => {
|
|
392
|
+
if (!ctx.follow) {
|
|
393
|
+
return Result.err(new Error('follow not available'));
|
|
394
|
+
}
|
|
395
|
+
const leafResult = await ctx.follow<{ leaf: string }>('step.leaf', input);
|
|
396
|
+
if (leafResult.isErr()) {
|
|
397
|
+
return leafResult;
|
|
398
|
+
}
|
|
399
|
+
return Result.ok({ middle: leafResult.value.leaf });
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const rootTrail = trail('step.root', {
|
|
404
|
+
description: 'Root trail that follows the middle trail',
|
|
405
|
+
examples: [
|
|
406
|
+
{
|
|
407
|
+
expected: { root: 'hello' },
|
|
408
|
+
input: { value: 'hello' },
|
|
409
|
+
name: 'Nested follow chain A→B→C',
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
follow: ['step.middle'],
|
|
413
|
+
input: z.object({ value: z.string() }),
|
|
414
|
+
output: z.object({ root: z.string() }),
|
|
415
|
+
run: async (input: { value: string }, ctx) => {
|
|
416
|
+
if (!ctx.follow) {
|
|
417
|
+
return Result.err(new Error('follow not available'));
|
|
418
|
+
}
|
|
419
|
+
const midResult = await ctx.follow<{ middle: string }>(
|
|
420
|
+
'step.middle',
|
|
421
|
+
input
|
|
422
|
+
);
|
|
423
|
+
if (midResult.isErr()) {
|
|
424
|
+
return midResult;
|
|
425
|
+
}
|
|
426
|
+
return Result.ok({ root: midResult.value.middle });
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
describe('testExamples nested follow chain (A → B → C)', () => {
|
|
431
|
+
// eslint-disable-next-line jest/require-hook
|
|
432
|
+
testExamples(
|
|
433
|
+
topo('nested-chain-app', {
|
|
434
|
+
leafTrail,
|
|
435
|
+
middleTrail,
|
|
436
|
+
rootTrail,
|
|
437
|
+
} as Record<string, unknown>)
|
|
438
|
+
);
|
|
439
|
+
});
|