@ontrails/trails 1.0.0-beta.13 → 1.0.0-beta.15
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 +29 -0
- package/__tests__/examples.test.ts +39 -0
- package/dist/src/app.d.ts.map +1 -1
- package/dist/src/app.js +12 -1
- package/dist/src/app.js.map +1 -1
- package/dist/src/cli.js +4 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/trails/add-surface.d.ts +3 -3
- package/dist/src/trails/add-surface.d.ts.map +1 -1
- package/dist/src/trails/add-surface.js +46 -24
- package/dist/src/trails/add-surface.js.map +1 -1
- package/dist/src/trails/add-trail.d.ts +3 -1
- package/dist/src/trails/add-trail.d.ts.map +1 -1
- package/dist/src/trails/add-trail.js +49 -22
- package/dist/src/trails/add-trail.js.map +1 -1
- package/dist/src/trails/add-trailhead.d.ts +13 -0
- package/dist/src/trails/add-trailhead.d.ts.map +1 -0
- package/dist/src/trails/add-trailhead.js +88 -0
- package/dist/src/trails/add-trailhead.js.map +1 -0
- package/dist/src/trails/add-verify.d.ts +1 -1
- package/dist/src/trails/add-verify.d.ts.map +1 -1
- package/dist/src/trails/add-verify.js +17 -16
- package/dist/src/trails/add-verify.js.map +1 -1
- package/dist/src/trails/create-scaffold.d.ts +1 -1
- package/dist/src/trails/create-scaffold.d.ts.map +1 -1
- package/dist/src/trails/create-scaffold.js +34 -27
- package/dist/src/trails/create-scaffold.js.map +1 -1
- package/dist/src/trails/create.d.ts +9 -13
- package/dist/src/trails/create.d.ts.map +1 -1
- package/dist/src/trails/create.js +40 -35
- package/dist/src/trails/create.js.map +1 -1
- package/dist/src/trails/dev-clean.d.ts +9 -0
- package/dist/src/trails/dev-clean.d.ts.map +1 -0
- package/dist/src/trails/dev-clean.js +66 -0
- package/dist/src/trails/dev-clean.js.map +1 -0
- package/dist/src/trails/dev-reset.d.ts +6 -0
- package/dist/src/trails/dev-reset.d.ts.map +1 -0
- package/dist/src/trails/dev-reset.js +39 -0
- package/dist/src/trails/dev-reset.js.map +1 -0
- package/dist/src/trails/dev-stats.d.ts +7 -0
- package/dist/src/trails/dev-stats.d.ts.map +1 -0
- package/dist/src/trails/dev-stats.js +61 -0
- package/dist/src/trails/dev-stats.js.map +1 -0
- package/dist/src/trails/dev-support.d.ts +64 -0
- package/dist/src/trails/dev-support.d.ts.map +1 -0
- package/dist/src/trails/dev-support.js +181 -0
- package/dist/src/trails/dev-support.js.map +1 -0
- package/dist/src/trails/draft-promote.d.ts +18 -0
- package/dist/src/trails/draft-promote.d.ts.map +1 -0
- package/dist/src/trails/draft-promote.js +400 -0
- package/dist/src/trails/draft-promote.js.map +1 -0
- package/dist/src/trails/guide.d.ts +14 -4
- package/dist/src/trails/guide.d.ts.map +1 -1
- package/dist/src/trails/guide.js +22 -41
- package/dist/src/trails/guide.js.map +1 -1
- package/dist/src/trails/load-app.d.ts +9 -1
- package/dist/src/trails/load-app.d.ts.map +1 -1
- package/dist/src/trails/load-app.js +404 -13
- package/dist/src/trails/load-app.js.map +1 -1
- package/dist/src/trails/project.d.ts.map +1 -1
- package/dist/src/trails/project.js +14 -3
- package/dist/src/trails/project.js.map +1 -1
- package/dist/src/trails/survey.d.ts +6 -60
- package/dist/src/trails/survey.d.ts.map +1 -1
- package/dist/src/trails/survey.js +83 -182
- package/dist/src/trails/survey.js.map +1 -1
- package/dist/src/trails/topo-constants.d.ts +3 -0
- package/dist/src/trails/topo-constants.d.ts.map +1 -0
- package/dist/src/trails/topo-constants.js +3 -0
- package/dist/src/trails/topo-constants.js.map +1 -0
- package/dist/src/trails/topo-export.d.ts +19 -0
- package/dist/src/trails/topo-export.d.ts.map +1 -0
- package/dist/src/trails/topo-export.js +31 -0
- package/dist/src/trails/topo-export.js.map +1 -0
- package/dist/src/trails/topo-history.d.ts +20 -0
- package/dist/src/trails/topo-history.d.ts.map +1 -0
- package/dist/src/trails/topo-history.js +32 -0
- package/dist/src/trails/topo-history.js.map +1 -0
- package/dist/src/trails/topo-pin.d.ts +17 -0
- package/dist/src/trails/topo-pin.d.ts.map +1 -0
- package/dist/src/trails/topo-pin.js +31 -0
- package/dist/src/trails/topo-pin.js.map +1 -0
- package/dist/src/trails/topo-read-support.d.ts +58 -0
- package/dist/src/trails/topo-read-support.d.ts.map +1 -0
- package/dist/src/trails/topo-read-support.js +167 -0
- package/dist/src/trails/topo-read-support.js.map +1 -0
- package/dist/src/trails/topo-reports.d.ts +54 -0
- package/dist/src/trails/topo-reports.d.ts.map +1 -0
- package/dist/src/trails/topo-reports.js +128 -0
- package/dist/src/trails/topo-reports.js.map +1 -0
- package/dist/src/trails/topo-show.d.ts +23 -0
- package/dist/src/trails/topo-show.d.ts.map +1 -0
- package/dist/src/trails/topo-show.js +49 -0
- package/dist/src/trails/topo-show.js.map +1 -0
- package/dist/src/trails/topo-store-support.d.ts +13 -0
- package/dist/src/trails/topo-store-support.d.ts.map +1 -0
- package/dist/src/trails/topo-store-support.js +55 -0
- package/dist/src/trails/topo-store-support.js.map +1 -0
- package/dist/src/trails/topo-support.d.ts +76 -0
- package/dist/src/trails/topo-support.d.ts.map +1 -0
- package/dist/src/trails/topo-support.js +132 -0
- package/dist/src/trails/topo-support.js.map +1 -0
- package/dist/src/trails/topo-unpin.d.ts +20 -0
- package/dist/src/trails/topo-unpin.d.ts.map +1 -0
- package/dist/src/trails/topo-unpin.js +44 -0
- package/dist/src/trails/topo-unpin.js.map +1 -0
- package/dist/src/trails/topo-verify.d.ts +5 -0
- package/dist/src/trails/topo-verify.d.ts.map +1 -0
- package/dist/src/trails/topo-verify.js +24 -0
- package/dist/src/trails/topo-verify.js.map +1 -0
- package/dist/src/trails/topo.d.ts +5 -0
- package/dist/src/trails/topo.d.ts.map +1 -0
- package/dist/src/trails/topo.js +63 -0
- package/dist/src/trails/topo.js.map +1 -0
- package/dist/src/trails/warden.d.ts +3 -2
- package/dist/src/trails/warden.d.ts.map +1 -1
- package/dist/src/trails/warden.js +37 -27
- package/dist/src/trails/warden.js.map +1 -1
- package/dist/src/versions.d.ts +12 -0
- package/dist/src/versions.d.ts.map +1 -0
- package/dist/src/versions.js +23 -0
- package/dist/src/versions.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/__tests__/add-trail.test.ts +97 -0
- package/src/__tests__/create.test.ts +91 -27
- package/src/__tests__/draft-promote.test.ts +144 -0
- package/src/__tests__/guide.test.ts +10 -5
- package/src/__tests__/load-app.test.ts +406 -2
- package/src/__tests__/survey.test.ts +221 -60
- package/src/__tests__/topo-dev.test.ts +426 -0
- package/src/app.ts +24 -2
- package/src/clack.ts +1 -1
- package/src/cli.ts +4 -3
- package/src/trails/add-surface.ts +150 -0
- package/src/trails/add-trail.ts +46 -10
- package/src/trails/add-verify.ts +11 -6
- package/src/trails/create-scaffold.ts +16 -3
- package/src/trails/create.ts +76 -71
- package/src/trails/dev-clean.ts +77 -0
- package/src/trails/dev-reset.ts +45 -0
- package/src/trails/dev-stats.ts +67 -0
- package/src/trails/dev-support.ts +328 -0
- package/src/trails/draft-promote.ts +739 -0
- package/src/trails/guide.ts +23 -41
- package/src/trails/load-app.ts +556 -14
- package/src/trails/project.ts +17 -3
- package/src/trails/survey.ts +110 -285
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-export.ts +35 -0
- package/src/trails/topo-history.ts +38 -0
- package/src/trails/topo-pin.ts +38 -0
- package/src/trails/topo-read-support.ts +329 -0
- package/src/trails/topo-reports.ts +228 -0
- package/src/trails/topo-show.ts +54 -0
- package/src/trails/topo-store-support.ts +104 -0
- package/src/trails/topo-support.ts +230 -0
- package/src/trails/topo-unpin.ts +56 -0
- package/src/trails/topo-verify.ts +25 -0
- package/src/trails/topo.ts +69 -0
- package/src/trails/warden.ts +13 -3
- package/src/versions.ts +43 -0
- package/tsconfig.tests.json +10 -0
- package/src/trails/add-trailhead.ts +0 -121
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from 'node:fs';
|
|
9
|
+
import { join, resolve } from 'node:path';
|
|
2
10
|
|
|
3
|
-
import { Result,
|
|
11
|
+
import { ConflictError, Result, resource, topo, trail } from '@ontrails/core';
|
|
4
12
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
deriveSurfaceMap,
|
|
14
|
+
deriveSurfaceMapHash,
|
|
15
|
+
deriveSurfaceMapDiff,
|
|
16
|
+
writeSurfaceMap,
|
|
8
17
|
} from '@ontrails/schema';
|
|
9
|
-
import type {
|
|
18
|
+
import type { SurfaceMap } from '@ontrails/schema';
|
|
10
19
|
import { z } from 'zod';
|
|
11
20
|
|
|
12
21
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
deriveBriefReport,
|
|
23
|
+
deriveSurveyList,
|
|
24
|
+
deriveTrailDetail,
|
|
25
|
+
surveyTrail,
|
|
16
26
|
} from '../trails/survey.js';
|
|
27
|
+
import { loadApp } from '../trails/load-app.js';
|
|
17
28
|
import type {
|
|
18
29
|
BriefReport,
|
|
19
30
|
SurveyListReport,
|
|
@@ -30,9 +41,13 @@ const helloTrail = trail('hello', {
|
|
|
30
41
|
return Result.ok({ message: `Hello, ${name}!` });
|
|
31
42
|
},
|
|
32
43
|
description: 'Say hello',
|
|
33
|
-
detours:
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
detours: [
|
|
45
|
+
{
|
|
46
|
+
on: ConflictError,
|
|
47
|
+
/* oxlint-disable-next-line require-await -- test stub */
|
|
48
|
+
recover: async () => Result.ok({ message: 'recovered' }),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
36
51
|
examples: [
|
|
37
52
|
{
|
|
38
53
|
expected: { message: 'Hello, world!' },
|
|
@@ -43,8 +58,8 @@ const helloTrail = trail('hello', {
|
|
|
43
58
|
input: z.object({ name: z.string().optional() }),
|
|
44
59
|
intent: 'read',
|
|
45
60
|
output: z.object({ message: z.string() }),
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
resources: [
|
|
62
|
+
resource('db.main', {
|
|
48
63
|
create: () => Result.ok({ source: 'factory' }),
|
|
49
64
|
}),
|
|
50
65
|
],
|
|
@@ -57,62 +72,124 @@ const byeTrail = trail('bye', {
|
|
|
57
72
|
output: z.object({ message: z.string() }),
|
|
58
73
|
});
|
|
59
74
|
|
|
60
|
-
const [
|
|
61
|
-
if (!
|
|
75
|
+
const [dbResource] = helloTrail.resources;
|
|
76
|
+
if (!dbResource) {
|
|
62
77
|
throw new Error('Expected helloTrail to declare db.main');
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
const app = topo('test-app', {
|
|
66
81
|
bye: byeTrail,
|
|
67
|
-
|
|
82
|
+
dbResource,
|
|
68
83
|
hello: helloTrail,
|
|
69
84
|
});
|
|
70
85
|
|
|
86
|
+
const expectOk = <T>(result: Result<T, Error>): T => {
|
|
87
|
+
if (result.isErr()) {
|
|
88
|
+
throw result.error;
|
|
89
|
+
}
|
|
90
|
+
return result.value;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const writeSurveyAppFixture = (
|
|
94
|
+
dir: string,
|
|
95
|
+
options?: { withBye?: boolean }
|
|
96
|
+
) => {
|
|
97
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
98
|
+
const byeSource = options?.withBye
|
|
99
|
+
? `
|
|
100
|
+
const bye = trail('bye', {
|
|
101
|
+
blaze: async (input) => Result.ok({ message: \`Bye, \${input.name ?? 'world'}!\` }),
|
|
102
|
+
input: z.object({ name: z.string().optional() }),
|
|
103
|
+
intent: 'read',
|
|
104
|
+
output: z.object({ message: z.string() }),
|
|
105
|
+
});
|
|
106
|
+
`
|
|
107
|
+
: '';
|
|
108
|
+
const topoMembers = options?.withBye
|
|
109
|
+
? '{ bye, dbMain, hello }'
|
|
110
|
+
: '{ dbMain, hello }';
|
|
111
|
+
writeFileSync(
|
|
112
|
+
join(dir, 'src', 'app.ts'),
|
|
113
|
+
`import { Result, resource, topo, trail } from '@ontrails/core';
|
|
114
|
+
import { z } from 'zod';
|
|
115
|
+
|
|
116
|
+
const hello = trail('hello', {
|
|
117
|
+
blaze: async (input) => Result.ok({ message: \`Hello, \${input.name ?? 'world'}!\` }),
|
|
118
|
+
input: z.object({ name: z.string().optional() }),
|
|
119
|
+
intent: 'read',
|
|
120
|
+
output: z.object({ message: z.string() }),
|
|
121
|
+
resources: [
|
|
122
|
+
resource('db.main', {
|
|
123
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
124
|
+
}),
|
|
125
|
+
],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const [dbMain] = hello.resources;
|
|
129
|
+
if (!dbMain) {
|
|
130
|
+
throw new Error('expected hello to declare db.main');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
${byeSource}
|
|
134
|
+
|
|
135
|
+
export const app = topo('survey-fixture', ${topoMembers});
|
|
136
|
+
`
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const repoTempDir = (): string =>
|
|
141
|
+
join(
|
|
142
|
+
resolve(import.meta.dir, '../..'),
|
|
143
|
+
'.tmp-tests',
|
|
144
|
+
`trails-survey-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
145
|
+
);
|
|
146
|
+
|
|
71
147
|
// ---------------------------------------------------------------------------
|
|
72
148
|
// Tests
|
|
73
149
|
// ---------------------------------------------------------------------------
|
|
74
150
|
|
|
75
151
|
describe('trails survey', () => {
|
|
76
|
-
test('
|
|
77
|
-
const
|
|
78
|
-
expect(
|
|
79
|
-
const ids =
|
|
152
|
+
test('deriveSurfaceMap includes all trails', () => {
|
|
153
|
+
const surfaceMap = deriveSurfaceMap(app);
|
|
154
|
+
expect(surfaceMap.entries.length).toBe(3);
|
|
155
|
+
const ids = surfaceMap.entries.map((e) => e.id);
|
|
80
156
|
expect(ids).toContain('hello');
|
|
81
157
|
expect(ids).toContain('bye');
|
|
82
158
|
expect(ids).toContain('db.main');
|
|
83
159
|
});
|
|
84
160
|
|
|
85
|
-
test('
|
|
86
|
-
const
|
|
87
|
-
const hello =
|
|
161
|
+
test('surface map entries have expected fields', () => {
|
|
162
|
+
const surfaceMap = deriveSurfaceMap(app);
|
|
163
|
+
const hello = surfaceMap.entries.find((e) => e.id === 'hello');
|
|
88
164
|
expect(hello).toBeDefined();
|
|
165
|
+
expect(hello?.cli?.path).toEqual(['hello']);
|
|
89
166
|
expect(hello?.kind).toBe('trail');
|
|
90
167
|
expect(hello?.intent).toBe('read');
|
|
91
168
|
expect(hello?.exampleCount).toBe(1);
|
|
92
|
-
expect(hello?.
|
|
169
|
+
expect(hello?.resources).toEqual(['db.main']);
|
|
93
170
|
});
|
|
94
171
|
|
|
95
172
|
test('JSON output is valid JSON', () => {
|
|
96
|
-
const
|
|
97
|
-
const json = JSON.stringify(
|
|
98
|
-
const parsed = JSON.parse(json) as
|
|
173
|
+
const surfaceMap = deriveSurfaceMap(app);
|
|
174
|
+
const json = JSON.stringify(surfaceMap, null, 2);
|
|
175
|
+
const parsed = JSON.parse(json) as SurfaceMap;
|
|
99
176
|
expect(parsed.version).toBe('1.0');
|
|
100
177
|
expect(parsed.entries.length).toBe(3);
|
|
101
178
|
});
|
|
102
179
|
|
|
103
|
-
test('
|
|
104
|
-
const
|
|
105
|
-
const hash1 =
|
|
106
|
-
const hash2 =
|
|
180
|
+
test('deriveSurfaceMapHash produces stable hash', () => {
|
|
181
|
+
const surfaceMap = deriveSurfaceMap(app);
|
|
182
|
+
const hash1 = deriveSurfaceMapHash(surfaceMap);
|
|
183
|
+
const hash2 = deriveSurfaceMapHash(surfaceMap);
|
|
107
184
|
expect(hash1).toBe(hash2);
|
|
108
185
|
// SHA-256 hex
|
|
109
186
|
expect(hash1.length).toBe(64);
|
|
110
187
|
});
|
|
111
188
|
|
|
112
|
-
test('
|
|
113
|
-
const prev =
|
|
114
|
-
const curr =
|
|
115
|
-
const diff =
|
|
189
|
+
test('deriveSurfaceMapDiff detects added trails', () => {
|
|
190
|
+
const prev = deriveSurfaceMap(topo('test', { hello: helloTrail }));
|
|
191
|
+
const curr = deriveSurfaceMap(app);
|
|
192
|
+
const diff = deriveSurfaceMapDiff(prev, curr);
|
|
116
193
|
|
|
117
194
|
expect(diff.info.length).toBeGreaterThan(0);
|
|
118
195
|
const addedBye = diff.info.find((e) => e.id === 'bye');
|
|
@@ -120,10 +197,10 @@ describe('trails survey', () => {
|
|
|
120
197
|
expect(addedBye?.change).toBe('added');
|
|
121
198
|
});
|
|
122
199
|
|
|
123
|
-
test('
|
|
124
|
-
const prev =
|
|
125
|
-
const curr =
|
|
126
|
-
const diff =
|
|
200
|
+
test('deriveSurfaceMapDiff detects removed trails', () => {
|
|
201
|
+
const prev = deriveSurfaceMap(app);
|
|
202
|
+
const curr = deriveSurfaceMap(topo('test', { hello: helloTrail }));
|
|
203
|
+
const diff = deriveSurfaceMapDiff(prev, curr);
|
|
127
204
|
|
|
128
205
|
expect(diff.hasBreaking).toBe(true);
|
|
129
206
|
const removedBye = diff.breaking.find((e) => e.id === 'bye');
|
|
@@ -131,9 +208,9 @@ describe('trails survey', () => {
|
|
|
131
208
|
expect(removedBye?.change).toBe('removed');
|
|
132
209
|
});
|
|
133
210
|
|
|
134
|
-
test('
|
|
135
|
-
const
|
|
136
|
-
const diff =
|
|
211
|
+
test('deriveSurfaceMapDiff returns empty for identical maps', () => {
|
|
212
|
+
const surfaceMap = deriveSurfaceMap(app);
|
|
213
|
+
const diff = deriveSurfaceMapDiff(surfaceMap, surfaceMap);
|
|
137
214
|
expect(diff.entries.length).toBe(0);
|
|
138
215
|
expect(diff.hasBreaking).toBe(false);
|
|
139
216
|
});
|
|
@@ -145,72 +222,156 @@ describe('trails survey', () => {
|
|
|
145
222
|
|
|
146
223
|
describe('trails survey --brief', () => {
|
|
147
224
|
test('produces a valid capability report', () => {
|
|
148
|
-
const report =
|
|
225
|
+
const report = deriveBriefReport(app);
|
|
149
226
|
expect(report.name).toBe('test-app');
|
|
150
227
|
expect(report.contractVersion).toBe('2026-03');
|
|
151
228
|
});
|
|
152
229
|
|
|
153
230
|
test('report includes correct trail count', () => {
|
|
154
|
-
const report =
|
|
231
|
+
const report = deriveBriefReport(app);
|
|
155
232
|
expect(report.trails).toBe(2);
|
|
156
233
|
expect(report.signals).toBe(0);
|
|
157
|
-
expect(report.
|
|
234
|
+
expect(report.resources).toBe(1);
|
|
158
235
|
});
|
|
159
236
|
|
|
160
237
|
test('detects features in use', () => {
|
|
161
|
-
const report =
|
|
238
|
+
const report = deriveBriefReport(app);
|
|
162
239
|
expect(report.features.outputSchemas).toBe(true);
|
|
163
240
|
expect(report.features.examples).toBe(true);
|
|
164
241
|
expect(report.features.detours).toBe(true);
|
|
165
242
|
expect(report.features.signals).toBe(false);
|
|
166
|
-
expect(report.features.
|
|
243
|
+
expect(report.features.resources).toBe(true);
|
|
167
244
|
});
|
|
168
245
|
|
|
169
246
|
test('JSON output is valid', () => {
|
|
170
|
-
const report =
|
|
247
|
+
const report = deriveBriefReport(app);
|
|
171
248
|
const json = JSON.stringify(report, null, 2);
|
|
172
249
|
const parsed = JSON.parse(json) as BriefReport;
|
|
173
250
|
expect(parsed.name).toBe('test-app');
|
|
174
251
|
expect(parsed.trails).toBe(2);
|
|
175
|
-
expect(parsed.
|
|
252
|
+
expect(parsed.resources).toBe(1);
|
|
176
253
|
});
|
|
177
254
|
|
|
178
255
|
test('empty app reports zero features', () => {
|
|
179
256
|
const emptyApp = topo('empty', {});
|
|
180
|
-
const report =
|
|
257
|
+
const report = deriveBriefReport(emptyApp);
|
|
181
258
|
expect(report.trails).toBe(0);
|
|
182
259
|
expect(report.features.outputSchemas).toBe(false);
|
|
183
260
|
expect(report.features.examples).toBe(false);
|
|
184
261
|
expect(report.features.detours).toBe(false);
|
|
185
|
-
expect(report.features.
|
|
262
|
+
expect(report.features.resources).toBe(false);
|
|
186
263
|
});
|
|
187
264
|
});
|
|
188
265
|
|
|
189
266
|
describe('trails survey detail', () => {
|
|
190
|
-
test('trail detail includes declared
|
|
191
|
-
const detail =
|
|
267
|
+
test('trail detail includes declared resources, crossings, and intent', () => {
|
|
268
|
+
const detail = deriveTrailDetail(helloTrail);
|
|
192
269
|
const parsed = structuredClone(detail) as TrailDetailReport;
|
|
193
270
|
|
|
194
271
|
expect(parsed.crosses).toEqual([]);
|
|
195
272
|
expect(parsed.intent).toBe('read');
|
|
196
|
-
expect(parsed.
|
|
273
|
+
expect(parsed.resources).toEqual(['db.main']);
|
|
197
274
|
});
|
|
198
275
|
});
|
|
199
276
|
|
|
200
|
-
describe('trails survey
|
|
201
|
-
test('list output includes
|
|
202
|
-
const report =
|
|
277
|
+
describe('trails survey resources section', () => {
|
|
278
|
+
test('list output includes resource lifetime and health status', () => {
|
|
279
|
+
const report = deriveSurveyList(app);
|
|
203
280
|
const parsed = structuredClone(report) as SurveyListReport;
|
|
204
|
-
const db = parsed.
|
|
281
|
+
const db = parsed.resources.find((entry) => entry.id === 'db.main');
|
|
205
282
|
|
|
206
|
-
expect(parsed.
|
|
283
|
+
expect(parsed.resourceCount).toBe(1);
|
|
207
284
|
expect(db).toEqual({
|
|
208
285
|
description: null,
|
|
209
286
|
health: 'none',
|
|
210
287
|
id: 'db.main',
|
|
211
|
-
kind: '
|
|
288
|
+
kind: 'resource',
|
|
212
289
|
lifetime: 'singleton',
|
|
213
290
|
usedBy: ['hello'],
|
|
214
291
|
});
|
|
215
292
|
});
|
|
216
293
|
});
|
|
294
|
+
|
|
295
|
+
describe('trails survey generate', () => {
|
|
296
|
+
test('delegates to topo export and writes a structured lock', async () => {
|
|
297
|
+
const dir = repoTempDir();
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
writeSurveyAppFixture(dir);
|
|
301
|
+
|
|
302
|
+
const generated = expectOk(
|
|
303
|
+
await surveyTrail.blaze({ generate: true, module: './src/app.ts' }, {
|
|
304
|
+
cwd: dir,
|
|
305
|
+
} as never)
|
|
306
|
+
) as {
|
|
307
|
+
readonly hash: string;
|
|
308
|
+
readonly lockPath: string;
|
|
309
|
+
readonly mapPath: string;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
expect(generated.hash).toHaveLength(64);
|
|
313
|
+
expect(existsSync(join(dir, '.trails', '_surface.json'))).toBe(true);
|
|
314
|
+
expect(existsSync(join(dir, '.trails', 'trails.lock'))).toBe(true);
|
|
315
|
+
expect(
|
|
316
|
+
JSON.parse(readFileSync(join(dir, '.trails', 'trails.lock'), 'utf8'))
|
|
317
|
+
).toMatchObject({
|
|
318
|
+
hash: generated.hash,
|
|
319
|
+
version: 1,
|
|
320
|
+
});
|
|
321
|
+
} finally {
|
|
322
|
+
rmSync(dir, { force: true, recursive: true });
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('trails survey diffSaved', () => {
|
|
328
|
+
test('returns an error when no saved surface map exists yet', async () => {
|
|
329
|
+
const dir = repoTempDir();
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
writeSurveyAppFixture(dir);
|
|
333
|
+
|
|
334
|
+
const result = await surveyTrail.blaze(
|
|
335
|
+
{ diffSaved: true, module: './src/app.ts' },
|
|
336
|
+
{ cwd: dir } as never
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
expect(result.isErr()).toBe(true);
|
|
340
|
+
expect(result.error.message).toContain('Run `trails topo export` first');
|
|
341
|
+
} finally {
|
|
342
|
+
rmSync(dir, { force: true, recursive: true });
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test('diffs against the saved local surface map', async () => {
|
|
347
|
+
const dir = repoTempDir();
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
writeSurveyAppFixture(dir);
|
|
351
|
+
const baselineApp = await loadApp('./src/app.ts', dir);
|
|
352
|
+
await writeSurfaceMap(deriveSurfaceMap(baselineApp), {
|
|
353
|
+
dir: join(dir, '.trails'),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
writeSurveyAppFixture(dir, { withBye: true });
|
|
357
|
+
|
|
358
|
+
const result = await surveyTrail.blaze(
|
|
359
|
+
{ diffSaved: true, module: './src/app.ts' },
|
|
360
|
+
{ cwd: dir } as never
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(result.isOk()).toBe(true);
|
|
364
|
+
expect(result.value).toMatchObject({
|
|
365
|
+
hasBreaking: false,
|
|
366
|
+
info: [
|
|
367
|
+
expect.objectContaining({
|
|
368
|
+
change: 'added',
|
|
369
|
+
id: 'bye',
|
|
370
|
+
}),
|
|
371
|
+
],
|
|
372
|
+
});
|
|
373
|
+
} finally {
|
|
374
|
+
rmSync(dir, { force: true, recursive: true });
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
});
|