@ontrails/testing 1.0.0-beta.12 → 1.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +72 -27
  3. package/README.md +9 -9
  4. package/dist/context.d.ts +11 -11
  5. package/dist/context.d.ts.map +1 -1
  6. package/dist/context.js +23 -21
  7. package/dist/context.js.map +1 -1
  8. package/dist/contracts.js +8 -8
  9. package/dist/contracts.js.map +1 -1
  10. package/dist/crosses.d.ts +38 -0
  11. package/dist/crosses.d.ts.map +1 -0
  12. package/dist/crosses.js +213 -0
  13. package/dist/crosses.js.map +1 -0
  14. package/dist/examples.d.ts +4 -4
  15. package/dist/examples.js +31 -31
  16. package/dist/examples.js.map +1 -1
  17. package/dist/harness-cli.d.ts.map +1 -1
  18. package/dist/harness-cli.js +9 -22
  19. package/dist/harness-cli.js.map +1 -1
  20. package/dist/harness-mcp.d.ts +1 -1
  21. package/dist/harness-mcp.js +2 -2
  22. package/dist/harness-mcp.js.map +1 -1
  23. package/dist/index.d.ts +5 -5
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -3
  26. package/dist/index.js.map +1 -1
  27. package/dist/trail.js +1 -1
  28. package/dist/trail.js.map +1 -1
  29. package/dist/types.d.ts +8 -8
  30. package/dist/types.d.ts.map +1 -1
  31. package/package.json +8 -5
  32. package/src/__tests__/all.test.ts +97 -26
  33. package/src/__tests__/context.test.ts +27 -27
  34. package/src/__tests__/contracts.test.ts +29 -29
  35. package/src/__tests__/{follows.test.ts → crosses.test.ts} +193 -195
  36. package/src/__tests__/detours.test.ts +3 -3
  37. package/src/__tests__/examples.test.ts +145 -144
  38. package/src/__tests__/harness-cli.test.ts +66 -0
  39. package/src/__tests__/trail.test.ts +4 -4
  40. package/src/context.ts +36 -32
  41. package/src/contracts.ts +12 -12
  42. package/src/{follows.ts → crosses.ts} +97 -92
  43. package/src/examples.ts +43 -43
  44. package/src/harness-cli.ts +15 -39
  45. package/src/harness-mcp.ts +2 -2
  46. package/src/index.ts +6 -6
  47. package/src/trail.ts +1 -1
  48. package/src/types.ts +9 -9
  49. package/tsconfig.tsbuildinfo +1 -1
package/dist/trail.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"trail.js","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trail.js","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -18,13 +18,13 @@ export interface TestScenario {
18
18
  /** Input to pass to the implementation. */
19
19
  readonly input: unknown;
20
20
  }
21
- /** A test scenario for a trail's composition graph. */
22
- export interface FollowScenario extends TestScenario {
23
- /** Assert these trail IDs were followed, in order. */
24
- readonly expectFollowed?: readonly string[] | undefined;
25
- /** Assert follow counts per trail ID. */
26
- readonly expectFollowedCount?: Readonly<Record<string, number>> | undefined;
27
- /** Inject failure from a followed trail's example by description. */
21
+ /** A test scenario for a trail's crossing graph. */
22
+ export interface CrossScenario extends TestScenario {
23
+ /** Assert these trail IDs were crossed, in order. */
24
+ readonly expectCrossed?: readonly string[] | undefined;
25
+ /** Assert crossing counts per trail ID. */
26
+ readonly expectCrossedCount?: Readonly<Record<string, number>> | undefined;
27
+ /** Inject failure from a crossed trail's example by description. */
28
28
  readonly injectFromExample?: Readonly<Record<string, string>> | undefined;
29
29
  }
30
30
  /** A logger that captures entries for assertion in tests. */
@@ -44,7 +44,7 @@ export interface TestTrailContextOptions {
44
44
  readonly env?: Record<string, string> | undefined;
45
45
  readonly logger?: Logger | undefined;
46
46
  readonly requestId?: string | undefined;
47
- readonly signal?: AbortSignal | undefined;
47
+ readonly abortSignal?: AbortSignal | undefined;
48
48
  }
49
49
  /** Options for creating a CLI harness. */
50
50
  export interface CliHarnessOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/testing",
3
- "version": "1.0.0-beta.12",
3
+ "version": "1.0.0-beta.14",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -13,11 +13,14 @@
13
13
  "lint": "oxlint ./src",
14
14
  "clean": "rm -rf dist *.tsbuildinfo"
15
15
  },
16
+ "devDependencies": {
17
+ "@ontrails/store": "^1.0.0-beta.13"
18
+ },
16
19
  "peerDependencies": {
17
- "@ontrails/cli": "^1.0.0-beta.12",
18
- "@ontrails/core": "^1.0.0-beta.12",
19
- "@ontrails/logging": "^1.0.0-beta.12",
20
- "@ontrails/mcp": "^1.0.0-beta.12",
20
+ "@ontrails/cli": "^1.0.0-beta.13",
21
+ "@ontrails/core": "^1.0.0-beta.13",
22
+ "@ontrails/logging": "^1.0.0-beta.13",
23
+ "@ontrails/mcp": "^1.0.0-beta.13",
21
24
  "zod": "^4.3.5"
22
25
  }
23
26
  }
@@ -1,64 +1,135 @@
1
1
  import { describe } from 'bun:test';
2
2
 
3
- import { Result, service, trail, topo } from '@ontrails/core';
3
+ import { Result, trail, topo } from '@ontrails/core';
4
4
  import { z } from 'zod';
5
5
 
6
6
  import { testAll } from '../all.js';
7
+ import { store as defineStore } from '@ontrails/store';
8
+ import { connectDrizzle } from '@ontrails/store/drizzle';
7
9
 
8
- const mockDbService = service('db.mock.all', {
9
- create: () => Result.ok({ source: 'factory' }),
10
- mock: () => ({ source: 'mock' }),
10
+ const dbDefinition = defineStore({
11
+ entities: {
12
+ fixtures: [
13
+ {
14
+ id: 'seed-1',
15
+ name: 'Alpha',
16
+ source: 'mock',
17
+ },
18
+ ],
19
+ generated: ['id'],
20
+ primaryKey: 'id',
21
+ schema: z.object({
22
+ id: z.string(),
23
+ name: z.string(),
24
+ source: z.string(),
25
+ }),
26
+ },
11
27
  });
12
28
 
13
- const mockedTrail = trail('service.mocked.all', {
14
- description: 'Trail that uses a mocked service through testAll',
29
+ const createDbProvision = (
30
+ seed?: readonly {
31
+ readonly id: string;
32
+ readonly name: string;
33
+ readonly source: string;
34
+ }[]
35
+ ) =>
36
+ connectDrizzle(dbDefinition, {
37
+ id: 'db.mock.all',
38
+ ...(seed === undefined ? {} : { mockSeed: { entities: seed } }),
39
+ url: ':memory:',
40
+ });
41
+
42
+ const createOverrideStore = () => {
43
+ const { mock } = createDbProvision([
44
+ {
45
+ id: 'seed-1',
46
+ name: 'Override',
47
+ source: 'override',
48
+ },
49
+ ]);
50
+
51
+ if (mock === undefined) {
52
+ throw new Error('Expected drizzle test store to expose a mock factory');
53
+ }
54
+
55
+ const created = mock();
56
+ if (created instanceof Promise) {
57
+ throw new TypeError(
58
+ 'Expected drizzle test store mock to resolve synchronously'
59
+ );
60
+ }
61
+
62
+ return created;
63
+ };
64
+
65
+ const mockDbProvision = createDbProvision();
66
+
67
+ const mockedTrail = trail('provision.mocked.all', {
68
+ blaze: async (_input, ctx) => {
69
+ const entity = await mockDbProvision.from(ctx).entities.get('seed-1');
70
+ if (entity === null) {
71
+ return Result.err(new Error('expected seeded entity to exist'));
72
+ }
73
+
74
+ return Result.ok({ name: entity.name, source: entity.source });
75
+ },
76
+ description:
77
+ 'Trail that uses a mocked connector-bound provision through testAll',
15
78
  examples: [
16
79
  {
17
- expected: { source: 'mock' },
80
+ expected: { name: 'Alpha', source: 'mock' },
18
81
  input: {},
19
- name: 'Uses auto-resolved service mock',
82
+ name: 'Uses auto-resolved provision mock',
20
83
  },
21
84
  ],
22
85
  input: z.object({}),
23
- output: z.object({ source: z.string() }),
24
- run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
25
- services: [mockDbService],
86
+ output: z.object({ name: z.string(), source: z.string() }),
87
+ provisions: [mockDbProvision],
26
88
  });
27
89
 
28
- const overrideTrail = trail('service.override.all', {
90
+ const overrideTrail = trail('provision.override.all', {
91
+ blaze: async (_input, ctx) => {
92
+ const entity = await mockDbProvision.from(ctx).entities.get('seed-1');
93
+ if (entity === null) {
94
+ return Result.err(new Error('expected overridden entity to exist'));
95
+ }
96
+
97
+ return Result.ok({ name: entity.name, source: entity.source });
98
+ },
29
99
  description: 'Trail that prefers explicit overrides over mock factories',
30
100
  examples: [
31
101
  {
32
- expected: { source: 'override' },
102
+ expected: { name: 'Override', source: 'override' },
33
103
  input: {},
34
- name: 'Explicit service override wins',
104
+ name: 'Explicit provision override wins',
35
105
  },
36
106
  ],
37
107
  input: z.object({}),
38
- output: z.object({ source: z.string() }),
39
- run: (_input, ctx) => Result.ok({ source: mockDbService.from(ctx).source }),
40
- services: [mockDbService],
108
+ output: z.object({ name: z.string(), source: z.string() }),
109
+ provisions: [mockDbProvision],
41
110
  });
42
111
 
43
- describe('testAll service mocks', () => {
112
+ describe('testAll provision mocks', () => {
44
113
  // eslint-disable-next-line jest/require-hook
45
114
  testAll(
46
- topo('test-all-service-mock-app', {
47
- mockDbService,
115
+ topo('test-all-provision-mock-app', {
116
+ mockDbProvision,
48
117
  mockedTrail,
49
118
  } as Record<string, unknown>)
50
119
  );
51
120
  });
52
121
 
53
- describe('testAll explicit service overrides', () => {
122
+ describe('testAll explicit provision overrides', () => {
54
123
  // eslint-disable-next-line jest/require-hook
55
124
  testAll(
56
- topo('test-all-service-override-app', {
57
- mockDbService,
125
+ topo('test-all-provision-override-app', {
126
+ mockDbProvision,
58
127
  overrideTrail,
59
128
  } as Record<string, unknown>),
60
- {
61
- services: { 'db.mock.all': { source: 'override' } },
62
- }
129
+ () => ({
130
+ provisions: {
131
+ 'db.mock.all': createOverrideStore(),
132
+ },
133
+ })
63
134
  );
64
135
  });
@@ -1,12 +1,12 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
2
 
3
- import { Result, service, topo } from '@ontrails/core';
3
+ import { Result, provision, topo } from '@ontrails/core';
4
4
 
5
5
  import {
6
- createFollowContext,
6
+ createCrossContext,
7
7
  createTestContext,
8
8
  mergeTestContext,
9
- resolveMockServices,
9
+ resolveMockProvisions,
10
10
  } from '../context.js';
11
11
  import type { TestLogger } from '../types.js';
12
12
 
@@ -19,8 +19,8 @@ describe('createTestContext', () => {
19
19
  const ctx = createTestContext();
20
20
  expect(ctx.cwd).toBe(process.cwd());
21
21
  expect(ctx.requestId).toBe('test-request-001');
22
- expect(ctx.signal).toBeDefined();
23
- expect(ctx.signal.aborted).toBe(false);
22
+ expect(ctx.abortSignal).toBeDefined();
23
+ expect(ctx.abortSignal.aborted).toBe(false);
24
24
  expect(ctx.logger).toBeDefined();
25
25
  expect(ctx.workspaceRoot).toBe(process.cwd());
26
26
  });
@@ -46,11 +46,11 @@ describe('createTestContext', () => {
46
46
  expect(ctx.requestId).toBe('custom-id');
47
47
  });
48
48
 
49
- test('overrides signal', () => {
49
+ test('overrides abortSignal', () => {
50
50
  const controller = new AbortController();
51
51
  controller.abort();
52
- const ctx = createTestContext({ signal: controller.signal });
53
- expect(ctx.signal.aborted).toBe(true);
52
+ const ctx = createTestContext({ abortSignal: controller.signal });
53
+ expect(ctx.abortSignal.aborted).toBe(true);
54
54
  });
55
55
 
56
56
  test('overrides cwd', () => {
@@ -68,41 +68,41 @@ describe('createTestContext', () => {
68
68
  });
69
69
  });
70
70
 
71
- describe('createFollowContext', () => {
71
+ describe('createCrossContext', () => {
72
72
  test('returns configured ok response for registered id', async () => {
73
- const follow = createFollowContext({
73
+ const cross = createCrossContext({
74
74
  responses: { 'entity.add': Result.ok({ id: '1', name: 'Alpha' }) },
75
75
  });
76
- const result = await follow('entity.add', { name: 'Alpha' });
76
+ const result = await cross('entity.add', { name: 'Alpha' });
77
77
  expect(result.unwrap()).toEqual({ id: '1', name: 'Alpha' });
78
78
  });
79
79
 
80
80
  test('returns configured err response for registered id', async () => {
81
81
  const err = new Error('conflict');
82
- const follow = createFollowContext({
82
+ const cross = createCrossContext({
83
83
  responses: { 'entity.add': Result.err(err) },
84
84
  });
85
- const result = await follow('entity.add', {});
85
+ const result = await cross('entity.add', {});
86
86
  expect(result.isErr()).toBe(true);
87
87
  expect(result.error?.message).toBe('conflict');
88
88
  });
89
89
 
90
90
  test('returns err with descriptive message for unregistered id', async () => {
91
- const follow = createFollowContext();
92
- const result = await follow('unknown.trail', {});
91
+ const cross = createCrossContext();
92
+ const result = await cross('unknown.trail', {});
93
93
  expect(result.isErr()).toBe(true);
94
94
  expect(result.error?.message).toContain('unknown.trail');
95
95
  });
96
96
 
97
97
  test('no options defaults to empty responses', async () => {
98
- const follow = createFollowContext();
99
- const result = await follow('any.id', {});
98
+ const cross = createCrossContext();
99
+ const result = await cross('any.id', {});
100
100
  expect(result.isErr()).toBe(true);
101
101
  });
102
102
  });
103
103
 
104
104
  describe('mergeTestContext', () => {
105
- test('merges service overrides into extensions and the service accessor', () => {
105
+ test('merges provision overrides into extensions and the provision accessor', () => {
106
106
  const ctx = mergeTestContext(
107
107
  {
108
108
  extensions: { existing: true },
@@ -114,14 +114,14 @@ describe('mergeTestContext', () => {
114
114
  'db.main': { source: 'mock' },
115
115
  existing: true,
116
116
  });
117
- expect(ctx.service<{ source: string }>('db.main').source).toBe('mock');
117
+ expect(ctx.provision<{ source: string }>('db.main').source).toBe('mock');
118
118
  });
119
119
  });
120
120
 
121
- describe('resolveMockServices', () => {
122
- test('creates fresh mock services for each invocation', async () => {
121
+ describe('resolveMockProvisions', () => {
122
+ test('creates fresh mock provisions for each invocation', async () => {
123
123
  let mockCalls = 0;
124
- const mockable = service(`service.mock.${Bun.randomUUIDv7()}`, {
124
+ const mockable = provision(`provision.mock.${Bun.randomUUIDv7()}`, {
125
125
  create: () => Result.ok({ source: 'factory' }),
126
126
  mock: () => {
127
127
  mockCalls += 1;
@@ -130,8 +130,8 @@ describe('resolveMockServices', () => {
130
130
  });
131
131
  const app = topo('mock-app', { mockable } as Record<string, unknown>);
132
132
 
133
- const first = await resolveMockServices(app);
134
- const second = await resolveMockServices(app);
133
+ const first = await resolveMockProvisions(app);
134
+ const second = await resolveMockProvisions(app);
135
135
 
136
136
  expect(first).toEqual({ [mockable.id]: { source: 'mock' } });
137
137
  expect(second).toEqual(first);
@@ -139,12 +139,12 @@ describe('resolveMockServices', () => {
139
139
  expect(mockCalls).toBe(2);
140
140
  });
141
141
 
142
- test('skips services without mock factories', async () => {
143
- const plain = service(`service.plain.${Bun.randomUUIDv7()}`, {
142
+ test('skips provisions without mock factories', async () => {
143
+ const plain = provision(`provision.plain.${Bun.randomUUIDv7()}`, {
144
144
  create: () => Result.ok({ source: 'factory' }),
145
145
  });
146
146
  const app = topo('plain-app', { plain } as Record<string, unknown>);
147
147
 
148
- expect(await resolveMockServices(app)).toEqual({});
148
+ expect(await resolveMockProvisions(app)).toEqual({});
149
149
  });
150
150
  });
@@ -1,6 +1,6 @@
1
1
  import { describe, test } from 'bun:test';
2
2
 
3
- import { Result, service, trail, topo } from '@ontrails/core';
3
+ import { Result, provision, trail, topo } from '@ontrails/core';
4
4
  import { z } from 'zod';
5
5
 
6
6
  import { testContracts } from '../contracts.js';
@@ -11,6 +11,7 @@ import { testContracts } from '../contracts.js';
11
11
 
12
12
  /** Trail whose implementation matches the output schema. */
13
13
  const validTrail = trail('valid', {
14
+ blaze: (input: { name: string }) => Result.ok({ id: 1, name: input.name }),
14
15
  examples: [
15
16
  {
16
17
  expected: { id: 1, name: 'Alpha' },
@@ -20,21 +21,20 @@ const validTrail = trail('valid', {
20
21
  ],
21
22
  input: z.object({ name: z.string() }),
22
23
  output: z.object({ id: z.number(), name: z.string() }),
23
- run: (input: { name: string }) => Result.ok({ id: 1, name: input.name }),
24
24
  });
25
25
 
26
26
  /** Trail without output schema -- should be skipped. */
27
27
  const noSchemaTrail = trail('noschema', {
28
+ blaze: (input: { x: number }) => Result.ok(input.x * 2),
28
29
  examples: [{ expected: 10, input: { x: 5 }, name: 'No schema' }],
29
30
  input: z.object({ x: z.number() }),
30
- run: (input: { x: number }) => Result.ok(input.x * 2),
31
31
  });
32
32
 
33
33
  /** Trail without examples -- should be skipped. */
34
34
  const noExamplesTrail = trail('noexamples', {
35
+ blaze: (input: { x: number }) => Result.ok({ value: input.x }),
35
36
  input: z.object({ x: z.number() }),
36
37
  output: z.object({ value: z.number() }),
37
- run: (input: { x: number }) => Result.ok({ value: input.x }),
38
38
  });
39
39
 
40
40
  // ---------------------------------------------------------------------------
@@ -43,6 +43,9 @@ const noExamplesTrail = trail('noexamples', {
43
43
 
44
44
  /** Composition trail whose implementation matches the output schema. */
45
45
  const compositionTrail = trail('composition.valid', {
46
+ blaze: (input: { a: number; b: number }) =>
47
+ Result.ok({ total: input.a + input.b }),
48
+ crosses: ['valid'],
46
49
  examples: [
47
50
  {
48
51
  expected: { total: 3 },
@@ -50,14 +53,12 @@ const compositionTrail = trail('composition.valid', {
50
53
  name: 'Valid composition output',
51
54
  },
52
55
  ],
53
- follow: ['valid'],
54
56
  input: z.object({ a: z.number(), b: z.number() }),
55
57
  output: z.object({ total: z.number() }),
56
- run: (input: { a: number; b: number }) =>
57
- Result.ok({ total: input.a + input.b }),
58
58
  });
59
59
 
60
60
  const transformedInputTrail = trail('contract.transformed', {
61
+ blaze: (input: { value: number }) => Result.ok({ value: input.value }),
61
62
  examples: [
62
63
  {
63
64
  expected: { value: 2 },
@@ -69,49 +70,48 @@ const transformedInputTrail = trail('contract.transformed', {
69
70
  .object({ value: z.string() })
70
71
  .transform(({ value }) => ({ value: Number(value) + 1 })),
71
72
  output: z.object({ value: z.number() }),
72
- run: (input: { value: number }) => Result.ok({ value: input.value }),
73
73
  });
74
74
 
75
- const undeclaredContractDbService = service('db.undeclared.contracts', {
75
+ const undeclaredContractDbProvision = provision('db.undeclared.contracts', {
76
76
  create: () => Result.ok({ source: 'factory' }),
77
77
  mock: () => ({ source: 'mock' }),
78
78
  });
79
79
 
80
- const undeclaredContractTrail = trail('service.undeclared.contracts', {
80
+ const undeclaredContractTrail = trail('provision.undeclared.contracts', {
81
+ blaze: (_input, ctx) =>
82
+ Result.ok({
83
+ hasInjectedProvision:
84
+ ctx.extensions?.[undeclaredContractDbProvision.id] !== undefined,
85
+ }),
81
86
  examples: [
82
87
  {
83
- expected: { hasInjectedService: false },
88
+ expected: { hasInjectedProvision: false },
84
89
  input: {},
85
- name: 'Undeclared services are not preloaded into contract contexts',
90
+ name: 'Undeclared provisions are not preloaded into contract contexts',
86
91
  },
87
92
  ],
88
93
  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
- }),
94
+ output: z.object({ hasInjectedProvision: z.literal(false) }),
95
95
  });
96
96
 
97
- const ctxOverrideContractService = service('db.mock.contracts', {
97
+ const ctxOverrideContractProvision = provision('db.mock.contracts', {
98
98
  create: () => Result.ok({ source: 'factory' }),
99
99
  mock: () => ({ source: 'mock' }),
100
100
  });
101
101
 
102
- const ctxOverrideContractTrail = trail('service.ctx.contracts', {
102
+ const ctxOverrideContractTrail = trail('provision.ctx.contracts', {
103
+ blaze: (_input, ctx) =>
104
+ Result.ok({ source: ctxOverrideContractProvision.from(ctx).source }),
103
105
  examples: [
104
106
  {
105
107
  expected: { source: 'ctx' },
106
108
  input: {},
107
- name: 'Context extensions beat auto-resolved contract mocks',
109
+ name: 'Context extensions beat auto-resolved contract provision mocks',
108
110
  },
109
111
  ],
110
112
  input: z.object({}),
111
113
  output: z.object({ source: z.literal('ctx') }),
112
- run: (_input, ctx) =>
113
- Result.ok({ source: ctxOverrideContractService.from(ctx).source }),
114
- services: [ctxOverrideContractService],
114
+ provisions: [ctxOverrideContractProvision],
115
115
  });
116
116
 
117
117
  // ---------------------------------------------------------------------------
@@ -143,7 +143,7 @@ describe('testContracts: skips trails without examples', () => {
143
143
  });
144
144
  });
145
145
 
146
- describe('testContracts: validates composition trail output schemas', () => {
146
+ describe('testContracts: validates output schemas for trails with crossings', () => {
147
147
  // eslint-disable-next-line jest/require-hook
148
148
  testContracts(
149
149
  topo('test-app', { compositionTrail } as Record<string, unknown>)
@@ -163,7 +163,7 @@ describe('testContracts: context extension overrides', () => {
163
163
  // eslint-disable-next-line jest/require-hook
164
164
  testContracts(
165
165
  topo('ctx-contract-app', {
166
- ctxOverrideContractService,
166
+ ctxOverrideContractProvision,
167
167
  ctxOverrideContractTrail,
168
168
  } as Record<string, unknown>),
169
169
  {
@@ -174,11 +174,11 @@ describe('testContracts: context extension overrides', () => {
174
174
  );
175
175
  });
176
176
 
177
- describe('testContracts service declarations', () => {
177
+ describe('testContracts provision declarations', () => {
178
178
  // eslint-disable-next-line jest/require-hook
179
179
  testContracts(
180
- topo('undeclared-contract-service-app', {
181
- undeclaredContractDbService,
180
+ topo('undeclared-contract-provision-app', {
181
+ undeclaredContractDbProvision,
182
182
  undeclaredContractTrail,
183
183
  } as Record<string, unknown>)
184
184
  );