@intentius/chant 0.1.14 → 0.1.16
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/package.json +1 -1
- package/src/build.ts +18 -2
- package/src/cli/commands/build.ts +9 -1
- package/src/cli/commands/import-live.test.ts +126 -0
- package/src/cli/commands/import.ts +152 -2
- package/src/cli/commands/migrate.ts +2 -2
- package/src/cli/handlers/{state.test.ts → lifecycle.test.ts} +80 -37
- package/src/cli/handlers/{state.ts → lifecycle.ts} +166 -40
- package/src/cli/handlers/misc.ts +31 -2
- package/src/cli/handlers/run.test.ts +98 -0
- package/src/cli/handlers/run.ts +123 -0
- package/src/cli/main.test.ts +14 -0
- package/src/cli/main.ts +49 -15
- package/src/cli/mcp/{state-tools.ts → lifecycle-tools.ts} +9 -9
- package/src/cli/mcp/op-tools.ts +2 -2
- package/src/cli/mcp/resource-handlers.ts +1 -1
- package/src/cli/mcp/server.test.ts +2 -2
- package/src/cli/mcp/server.ts +1 -1
- package/src/cli/registry.ts +23 -2
- package/src/codegen/fetch.test.ts +103 -2
- package/src/codegen/fetch.ts +62 -10
- package/src/config.ts +41 -0
- package/src/detectLexicon.test.ts +2 -2
- package/src/index.ts +2 -2
- package/src/lexicon-export.test.ts +92 -0
- package/src/lexicon.ts +88 -9
- package/src/lifecycle/change-set.test.ts +151 -0
- package/src/lifecycle/change-set.ts +172 -0
- package/src/{state → lifecycle}/git.test.ts +15 -15
- package/src/{state → lifecycle}/git.ts +14 -14
- package/src/{state → lifecycle}/index.ts +2 -0
- package/src/{state → lifecycle}/snapshot.test.ts +5 -5
- package/src/{state → lifecycle}/snapshot.ts +9 -9
- package/src/{state → lifecycle}/types.ts +1 -1
- package/src/op/activity-registry.test.ts +59 -0
- package/src/op/activity-registry.ts +98 -0
- package/src/op/builders.ts +56 -20
- package/src/op/index.ts +6 -1
- package/src/op/local-executor.test.ts +263 -0
- package/src/op/local-executor.ts +300 -0
- package/src/op/local-output.test.ts +54 -0
- package/src/op/local-output.ts +63 -0
- package/src/op/op.test.ts +41 -4
- package/src/op/types.ts +2 -2
- package/src/ownership.test.ts +109 -0
- package/src/ownership.ts +142 -0
- package/src/serializer.ts +19 -1
- package/src/toml-parse.ts +3 -3
- /package/src/{state → lifecycle}/digest.test.ts +0 -0
- /package/src/{state → lifecycle}/digest.ts +0 -0
- /package/src/{state → lifecycle}/live-diff.test.ts +0 -0
- /package/src/{state → lifecycle}/live-diff.ts +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { describe, test, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { sep } from "node:path";
|
|
2
3
|
import { createMockPlugin, staticDescribeResources, staticListArtifacts } from "@intentius/chant-test-utils";
|
|
3
4
|
import type { LexiconPlugin, ResourceMetadata } from "../../lexicon";
|
|
4
5
|
import type { BuildResult } from "../../build";
|
|
5
6
|
import type { ParsedArgs } from "../registry";
|
|
6
7
|
|
|
7
8
|
const buildMock = vi.fn();
|
|
8
|
-
const
|
|
9
|
+
const fetchLifecycleMock = vi.fn();
|
|
9
10
|
const readSnapshotMock = vi.fn();
|
|
10
11
|
const readEnvironmentSnapshotsMock = vi.fn();
|
|
11
12
|
const listSnapshotsMock = vi.fn();
|
|
@@ -13,20 +14,20 @@ const takeSnapshotMock = vi.fn();
|
|
|
13
14
|
const loadChantConfigMock = vi.fn();
|
|
14
15
|
|
|
15
16
|
vi.mock("../../build", () => ({ build: (...args: unknown[]) => buildMock(...args) }));
|
|
16
|
-
vi.mock("../../
|
|
17
|
-
|
|
17
|
+
vi.mock("../../lifecycle/git", () => ({
|
|
18
|
+
fetchLifecycle: () => fetchLifecycleMock(),
|
|
18
19
|
readSnapshot: (...args: unknown[]) => readSnapshotMock(...args),
|
|
19
20
|
readEnvironmentSnapshots: (...args: unknown[]) => readEnvironmentSnapshotsMock(...args),
|
|
20
21
|
listSnapshots: (...args: unknown[]) => listSnapshotsMock(...args),
|
|
21
22
|
}));
|
|
22
|
-
vi.mock("../../
|
|
23
|
+
vi.mock("../../lifecycle/snapshot", () => ({
|
|
23
24
|
takeSnapshot: (...args: unknown[]) => takeSnapshotMock(...args),
|
|
24
25
|
}));
|
|
25
26
|
vi.mock("../../config", () => ({
|
|
26
27
|
loadChantConfig: (...args: unknown[]) => loadChantConfigMock(...args),
|
|
27
28
|
}));
|
|
28
29
|
|
|
29
|
-
const {
|
|
30
|
+
const { runLifecycleDiff, runLifecycleSnapshot, runLifecycleShow, runLifecycleLog, runLifecycleUnknown } = await import("./lifecycle");
|
|
30
31
|
|
|
31
32
|
function makeArgs(overrides: Partial<ParsedArgs>): ParsedArgs {
|
|
32
33
|
return {
|
|
@@ -71,7 +72,7 @@ const meta = (overrides: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
|
|
|
71
72
|
...overrides,
|
|
72
73
|
});
|
|
73
74
|
|
|
74
|
-
describe("
|
|
75
|
+
describe("runLifecycleDiff --live", () => {
|
|
75
76
|
let stdoutSpy: ReturnType<typeof vi.spyOn>;
|
|
76
77
|
let stderrSpy: ReturnType<typeof vi.spyOn>;
|
|
77
78
|
let stdoutBuf: string[];
|
|
@@ -83,13 +84,15 @@ describe("runStateDiff --live", () => {
|
|
|
83
84
|
stdoutSpy = vi.spyOn(console, "log").mockImplementation((s: string) => { stdoutBuf.push(s); });
|
|
84
85
|
stderrSpy = vi.spyOn(console, "error").mockImplementation((s: string) => { stderrBuf.push(s); });
|
|
85
86
|
buildMock.mockReset();
|
|
86
|
-
|
|
87
|
+
fetchLifecycleMock.mockReset();
|
|
87
88
|
readSnapshotMock.mockReset();
|
|
89
|
+
loadChantConfigMock.mockReset();
|
|
90
|
+
loadChantConfigMock.mockResolvedValue({ config: {} });
|
|
88
91
|
});
|
|
89
92
|
|
|
90
93
|
test("surfaces drift between previous snapshot and live state", async () => {
|
|
91
94
|
buildMock.mockResolvedValue(makeBuildResult({ aws: ["bucket"] }));
|
|
92
|
-
|
|
95
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
93
96
|
readSnapshotMock.mockResolvedValue(JSON.stringify({
|
|
94
97
|
lexicon: "aws",
|
|
95
98
|
environment: "prod",
|
|
@@ -113,7 +116,7 @@ describe("runStateDiff --live", () => {
|
|
|
113
116
|
serializers: plugins.map((p) => p.serializer),
|
|
114
117
|
};
|
|
115
118
|
|
|
116
|
-
const exit = await
|
|
119
|
+
const exit = await runLifecycleDiff(ctx);
|
|
117
120
|
|
|
118
121
|
expect(exit).toBe(0);
|
|
119
122
|
const output = stdoutBuf.join("\n");
|
|
@@ -124,9 +127,49 @@ describe("runStateDiff --live", () => {
|
|
|
124
127
|
expect(output).toContain("UPDATE_COMPLETE");
|
|
125
128
|
});
|
|
126
129
|
|
|
130
|
+
test("builds from config.sourceDir on a mixed-layout project", async () => {
|
|
131
|
+
buildMock.mockResolvedValue(makeBuildResult({ aws: ["bucket"] }));
|
|
132
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
133
|
+
readSnapshotMock.mockResolvedValue(null);
|
|
134
|
+
loadChantConfigMock.mockResolvedValue({ config: { sourceDir: "src" } });
|
|
135
|
+
|
|
136
|
+
const plugins: LexiconPlugin[] = [
|
|
137
|
+
createMockPlugin({ name: "aws", describeResources: staticDescribeResources({}) }),
|
|
138
|
+
];
|
|
139
|
+
const exit = await runLifecycleDiff({
|
|
140
|
+
args: makeArgs({ path: "diff", extraPositional: "prod", extraPositional2: "aws", live: true }),
|
|
141
|
+
plugins,
|
|
142
|
+
serializers: plugins.map((p) => p.serializer),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(exit).toBe(0);
|
|
146
|
+
const builtPath = buildMock.mock.calls[0][0] as string;
|
|
147
|
+
expect(builtPath.endsWith(`${sep}src`)).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("--src overrides config.sourceDir for the build root", async () => {
|
|
151
|
+
buildMock.mockResolvedValue(makeBuildResult({ aws: ["bucket"] }));
|
|
152
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
153
|
+
readSnapshotMock.mockResolvedValue(null);
|
|
154
|
+
loadChantConfigMock.mockResolvedValue({ config: { sourceDir: "src" } });
|
|
155
|
+
|
|
156
|
+
const plugins: LexiconPlugin[] = [
|
|
157
|
+
createMockPlugin({ name: "aws", describeResources: staticDescribeResources({}) }),
|
|
158
|
+
];
|
|
159
|
+
const exit = await runLifecycleDiff({
|
|
160
|
+
args: makeArgs({ path: "diff", extraPositional: "prod", extraPositional2: "aws", live: true, src: "infra" }),
|
|
161
|
+
plugins,
|
|
162
|
+
serializers: plugins.map((p) => p.serializer),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
expect(exit).toBe(0);
|
|
166
|
+
const builtPath = buildMock.mock.calls[0][0] as string;
|
|
167
|
+
expect(builtPath.endsWith(`${sep}infra`)).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
127
170
|
test("warns and skips lexicons without describeResources", async () => {
|
|
128
171
|
buildMock.mockResolvedValue(makeBuildResult({ k8s: ["pod"] }));
|
|
129
|
-
|
|
172
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
130
173
|
readSnapshotMock.mockResolvedValue(null);
|
|
131
174
|
|
|
132
175
|
const plugins: LexiconPlugin[] = [
|
|
@@ -139,7 +182,7 @@ describe("runStateDiff --live", () => {
|
|
|
139
182
|
serializers: plugins.map((p) => p.serializer),
|
|
140
183
|
};
|
|
141
184
|
|
|
142
|
-
const exit = await
|
|
185
|
+
const exit = await runLifecycleDiff(ctx);
|
|
143
186
|
|
|
144
187
|
expect(exit).toBe(1);
|
|
145
188
|
const stderr = stderrBuf.join("\n");
|
|
@@ -149,7 +192,7 @@ describe("runStateDiff --live", () => {
|
|
|
149
192
|
|
|
150
193
|
test("--live with a listArtifacts-only plugin diffs artifacts (no resources path)", async () => {
|
|
151
194
|
buildMock.mockResolvedValue(makeBuildResult({ helm: [] }));
|
|
152
|
-
|
|
195
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
153
196
|
// Previous snapshot has no artifact entry for the new release → expect ARTIFACTS ADDED
|
|
154
197
|
readSnapshotMock.mockResolvedValue(JSON.stringify({
|
|
155
198
|
lexicon: "helm",
|
|
@@ -175,7 +218,7 @@ describe("runStateDiff --live", () => {
|
|
|
175
218
|
serializers: plugins.map((p) => p.serializer),
|
|
176
219
|
};
|
|
177
220
|
|
|
178
|
-
const exit = await
|
|
221
|
+
const exit = await runLifecycleDiff(ctx);
|
|
179
222
|
|
|
180
223
|
expect(exit).toBe(0);
|
|
181
224
|
const output = stdoutBuf.join("\n");
|
|
@@ -185,7 +228,7 @@ describe("runStateDiff --live", () => {
|
|
|
185
228
|
|
|
186
229
|
test("legacy digest mode still works without --live", async () => {
|
|
187
230
|
buildMock.mockResolvedValue(makeBuildResult({ aws: ["bucket"] }));
|
|
188
|
-
|
|
231
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
189
232
|
readSnapshotMock.mockResolvedValue(null);
|
|
190
233
|
|
|
191
234
|
const plugins: LexiconPlugin[] = [createMockPlugin({ name: "aws" })];
|
|
@@ -196,7 +239,7 @@ describe("runStateDiff --live", () => {
|
|
|
196
239
|
serializers: plugins.map((p) => p.serializer),
|
|
197
240
|
};
|
|
198
241
|
|
|
199
|
-
const exit = await
|
|
242
|
+
const exit = await runLifecycleDiff(ctx);
|
|
200
243
|
|
|
201
244
|
expect(exit).toBe(0);
|
|
202
245
|
const output = stdoutBuf.join("\n");
|
|
@@ -206,7 +249,7 @@ describe("runStateDiff --live", () => {
|
|
|
206
249
|
});
|
|
207
250
|
});
|
|
208
251
|
|
|
209
|
-
describe("
|
|
252
|
+
describe("runLifecycleSnapshot", () => {
|
|
210
253
|
let stdoutBuf: string[];
|
|
211
254
|
let stderrBuf: string[];
|
|
212
255
|
|
|
@@ -227,7 +270,7 @@ describe("runStateSnapshot", () => {
|
|
|
227
270
|
plugins: [],
|
|
228
271
|
serializers: [],
|
|
229
272
|
};
|
|
230
|
-
const exit = await
|
|
273
|
+
const exit = await runLifecycleSnapshot(ctx);
|
|
231
274
|
expect(exit).toBe(1);
|
|
232
275
|
expect(stderrBuf.join("\n")).toContain("Environment is required");
|
|
233
276
|
});
|
|
@@ -238,7 +281,7 @@ describe("runStateSnapshot", () => {
|
|
|
238
281
|
plugins: [],
|
|
239
282
|
serializers: [],
|
|
240
283
|
};
|
|
241
|
-
const exit = await
|
|
284
|
+
const exit = await runLifecycleSnapshot(ctx);
|
|
242
285
|
expect(exit).toBe(1);
|
|
243
286
|
expect(stderrBuf.join("\n")).toContain('Unknown environment "unknown"');
|
|
244
287
|
});
|
|
@@ -251,7 +294,7 @@ describe("runStateSnapshot", () => {
|
|
|
251
294
|
plugins,
|
|
252
295
|
serializers: plugins.map((p) => p.serializer),
|
|
253
296
|
};
|
|
254
|
-
const exit = await
|
|
297
|
+
const exit = await runLifecycleSnapshot(ctx);
|
|
255
298
|
expect(exit).toBe(1);
|
|
256
299
|
expect(stderrBuf.join("\n")).toContain("No plugins implement describeResources");
|
|
257
300
|
});
|
|
@@ -275,14 +318,14 @@ describe("runStateSnapshot", () => {
|
|
|
275
318
|
plugins,
|
|
276
319
|
serializers: plugins.map((p) => p.serializer),
|
|
277
320
|
};
|
|
278
|
-
const exit = await
|
|
321
|
+
const exit = await runLifecycleSnapshot(ctx);
|
|
279
322
|
expect(exit).toBe(0);
|
|
280
323
|
expect(stderrBuf.join("\n")).toContain("Snapshot saved");
|
|
281
324
|
expect(takeSnapshotMock).toHaveBeenCalledTimes(1);
|
|
282
325
|
});
|
|
283
326
|
});
|
|
284
327
|
|
|
285
|
-
describe("
|
|
328
|
+
describe("runLifecycleShow", () => {
|
|
286
329
|
let stdoutBuf: string[];
|
|
287
330
|
let stderrBuf: string[];
|
|
288
331
|
|
|
@@ -291,7 +334,7 @@ describe("runStateShow", () => {
|
|
|
291
334
|
stderrBuf = [];
|
|
292
335
|
vi.spyOn(console, "log").mockImplementation((s: string) => { stdoutBuf.push(s); });
|
|
293
336
|
vi.spyOn(console, "error").mockImplementation((s: string) => { stderrBuf.push(s); });
|
|
294
|
-
|
|
337
|
+
fetchLifecycleMock.mockReset();
|
|
295
338
|
readSnapshotMock.mockReset();
|
|
296
339
|
readEnvironmentSnapshotsMock.mockReset();
|
|
297
340
|
});
|
|
@@ -301,12 +344,12 @@ describe("runStateShow", () => {
|
|
|
301
344
|
args: makeArgs({ command: "state", path: "show" }),
|
|
302
345
|
plugins: [], serializers: [],
|
|
303
346
|
};
|
|
304
|
-
expect(await
|
|
347
|
+
expect(await runLifecycleShow(ctx)).toBe(1);
|
|
305
348
|
expect(stderrBuf.join("\n")).toContain("Environment is required");
|
|
306
349
|
});
|
|
307
350
|
|
|
308
351
|
test("specific lexicon: prints snapshot table when found", async () => {
|
|
309
|
-
|
|
352
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
310
353
|
readSnapshotMock.mockResolvedValue(JSON.stringify({
|
|
311
354
|
lexicon: "aws", environment: "prod", commit: "x", timestamp: "t",
|
|
312
355
|
resources: { bucket: { type: "AWS::S3::Bucket", physicalId: "b-1", status: "OK" } },
|
|
@@ -315,25 +358,25 @@ describe("runStateShow", () => {
|
|
|
315
358
|
args: makeArgs({ command: "state", path: "show", extraPositional: "prod", extraPositional2: "aws" }),
|
|
316
359
|
plugins: [], serializers: [],
|
|
317
360
|
};
|
|
318
|
-
expect(await
|
|
361
|
+
expect(await runLifecycleShow(ctx)).toBe(0);
|
|
319
362
|
const out = stdoutBuf.join("\n");
|
|
320
363
|
expect(out).toContain("bucket");
|
|
321
364
|
expect(out).toContain("AWS::S3::Bucket");
|
|
322
365
|
});
|
|
323
366
|
|
|
324
367
|
test("specific lexicon: returns 1 when no snapshot found", async () => {
|
|
325
|
-
|
|
368
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
326
369
|
readSnapshotMock.mockResolvedValue(null);
|
|
327
370
|
const ctx = {
|
|
328
371
|
args: makeArgs({ command: "state", path: "show", extraPositional: "prod", extraPositional2: "aws" }),
|
|
329
372
|
plugins: [], serializers: [],
|
|
330
373
|
};
|
|
331
|
-
expect(await
|
|
374
|
+
expect(await runLifecycleShow(ctx)).toBe(1);
|
|
332
375
|
expect(stderrBuf.join("\n")).toContain("No snapshot found");
|
|
333
376
|
});
|
|
334
377
|
|
|
335
378
|
test("no lexicon: lists all lexicons in env", async () => {
|
|
336
|
-
|
|
379
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
337
380
|
readEnvironmentSnapshotsMock.mockResolvedValue(new Map([
|
|
338
381
|
["aws", JSON.stringify({ lexicon: "aws", environment: "prod", commit: "x", timestamp: "t", resources: {} })],
|
|
339
382
|
["gcp", JSON.stringify({ lexicon: "gcp", environment: "prod", commit: "x", timestamp: "t", resources: {} })],
|
|
@@ -342,14 +385,14 @@ describe("runStateShow", () => {
|
|
|
342
385
|
args: makeArgs({ command: "state", path: "show", extraPositional: "prod" }),
|
|
343
386
|
plugins: [], serializers: [],
|
|
344
387
|
};
|
|
345
|
-
expect(await
|
|
388
|
+
expect(await runLifecycleShow(ctx)).toBe(0);
|
|
346
389
|
const out = stdoutBuf.join("\n");
|
|
347
390
|
expect(out).toContain("prod/aws");
|
|
348
391
|
expect(out).toContain("prod/gcp");
|
|
349
392
|
});
|
|
350
393
|
});
|
|
351
394
|
|
|
352
|
-
describe("
|
|
395
|
+
describe("runLifecycleLog", () => {
|
|
353
396
|
let stdoutBuf: string[];
|
|
354
397
|
let stderrBuf: string[];
|
|
355
398
|
|
|
@@ -358,23 +401,23 @@ describe("runStateLog", () => {
|
|
|
358
401
|
stderrBuf = [];
|
|
359
402
|
vi.spyOn(console, "log").mockImplementation((s: string) => { stdoutBuf.push(s); });
|
|
360
403
|
vi.spyOn(console, "error").mockImplementation((s: string) => { stderrBuf.push(s); });
|
|
361
|
-
|
|
404
|
+
fetchLifecycleMock.mockReset();
|
|
362
405
|
listSnapshotsMock.mockReset();
|
|
363
406
|
});
|
|
364
407
|
|
|
365
408
|
test("returns 1 with message when no entries exist", async () => {
|
|
366
|
-
|
|
409
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
367
410
|
listSnapshotsMock.mockResolvedValue([]);
|
|
368
411
|
const ctx = {
|
|
369
412
|
args: makeArgs({ command: "state", path: "log" }),
|
|
370
413
|
plugins: [], serializers: [],
|
|
371
414
|
};
|
|
372
|
-
expect(await
|
|
415
|
+
expect(await runLifecycleLog(ctx)).toBe(1);
|
|
373
416
|
expect(stderrBuf.join("\n")).toContain("No state snapshots");
|
|
374
417
|
});
|
|
375
418
|
|
|
376
419
|
test("prints commit / date / message rows for each entry", async () => {
|
|
377
|
-
|
|
420
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
378
421
|
listSnapshotsMock.mockResolvedValue([
|
|
379
422
|
{ commit: "abcdef1234567890", date: "2026-05-01T00:00:00Z", message: "Snapshot prod" },
|
|
380
423
|
{ commit: "fedcba9876543210", date: "2026-05-02T00:00:00Z", message: "Snapshot staging" },
|
|
@@ -383,7 +426,7 @@ describe("runStateLog", () => {
|
|
|
383
426
|
args: makeArgs({ command: "state", path: "log" }),
|
|
384
427
|
plugins: [], serializers: [],
|
|
385
428
|
};
|
|
386
|
-
expect(await
|
|
429
|
+
expect(await runLifecycleLog(ctx)).toBe(0);
|
|
387
430
|
const out = stdoutBuf.join("\n");
|
|
388
431
|
expect(out).toContain("abcdef1");
|
|
389
432
|
expect(out).toContain("Snapshot prod");
|
|
@@ -391,7 +434,7 @@ describe("runStateLog", () => {
|
|
|
391
434
|
});
|
|
392
435
|
});
|
|
393
436
|
|
|
394
|
-
describe("
|
|
437
|
+
describe("runLifecycleUnknown", () => {
|
|
395
438
|
test("returns 1 with subcommand list", async () => {
|
|
396
439
|
const stderrBuf: string[] = [];
|
|
397
440
|
vi.spyOn(console, "error").mockImplementation((s: string) => { stderrBuf.push(s); });
|
|
@@ -399,7 +442,7 @@ describe("runStateUnknown", () => {
|
|
|
399
442
|
args: makeArgs({ command: "state", path: "garbage" }),
|
|
400
443
|
plugins: [], serializers: [],
|
|
401
444
|
};
|
|
402
|
-
expect(await
|
|
445
|
+
expect(await runLifecycleUnknown(ctx)).toBe(1);
|
|
403
446
|
const stderr = stderrBuf.join("\n");
|
|
404
447
|
expect(stderr).toContain("snapshot");
|
|
405
448
|
expect(stderr).toContain("show");
|