gsd-pi 2.63.0-dev.d04bbc5 → 2.64.0-dev.9c14bd0
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/dist/resources/extensions/gsd/auto-dashboard.js +5 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +98 -1
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto.js +5 -0
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
- package/dist/resources/extensions/gsd/preferences-types.js +4 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
- package/dist/resources/extensions/gsd/preferences.js +4 -0
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +122 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto.ts +4 -0
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
- package/src/resources/extensions/gsd/preferences-types.ts +28 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
- package/src/resources/extensions/gsd/preferences.ts +4 -0
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- /package/dist/web/standalone/.next/static/{vIq9fmvRUaFOpguoX5j4W → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vIq9fmvRUaFOpguoX5j4W → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,813 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* post-execution-checks.test.ts — Unit tests for post-execution validation checks.
|
|
3
|
+
*
|
|
4
|
+
* Tests all 3 check types:
|
|
5
|
+
* 1. Import resolution — verify relative imports resolve to existing files
|
|
6
|
+
* 2. Cross-task signatures — detect signature drift and hallucination cascades
|
|
7
|
+
* 3. Pattern consistency — async style drift, naming convention warnings
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
extractRelativeImports,
|
|
18
|
+
resolveImportPath,
|
|
19
|
+
checkImportResolution,
|
|
20
|
+
checkCrossTaskSignatures,
|
|
21
|
+
checkPatternConsistency,
|
|
22
|
+
runPostExecutionChecks,
|
|
23
|
+
type PostExecutionResult,
|
|
24
|
+
} from "../post-execution-checks.ts";
|
|
25
|
+
import type { TaskRow } from "../gsd-db.ts";
|
|
26
|
+
|
|
27
|
+
// ─── Test Fixtures ───────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a minimal TaskRow for testing.
|
|
31
|
+
*/
|
|
32
|
+
function createTask(overrides: Partial<TaskRow> = {}): TaskRow {
|
|
33
|
+
return {
|
|
34
|
+
milestone_id: "M001",
|
|
35
|
+
slice_id: "S01",
|
|
36
|
+
id: overrides.id ?? "T01",
|
|
37
|
+
title: "Test Task",
|
|
38
|
+
status: "complete",
|
|
39
|
+
one_liner: "",
|
|
40
|
+
narrative: "",
|
|
41
|
+
verification_result: "",
|
|
42
|
+
duration: "",
|
|
43
|
+
completed_at: new Date().toISOString(),
|
|
44
|
+
blocker_discovered: false,
|
|
45
|
+
deviations: "",
|
|
46
|
+
known_issues: "",
|
|
47
|
+
key_files: overrides.key_files ?? [],
|
|
48
|
+
key_decisions: [],
|
|
49
|
+
full_summary_md: "",
|
|
50
|
+
description: overrides.description ?? "",
|
|
51
|
+
estimate: "",
|
|
52
|
+
files: overrides.files ?? [],
|
|
53
|
+
verify: "",
|
|
54
|
+
inputs: overrides.inputs ?? [],
|
|
55
|
+
expected_output: overrides.expected_output ?? [],
|
|
56
|
+
observability_impact: "",
|
|
57
|
+
full_plan_md: "",
|
|
58
|
+
sequence: overrides.sequence ?? 0,
|
|
59
|
+
...overrides,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Import Extraction Tests ─────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
describe("extractRelativeImports", () => {
|
|
66
|
+
test("extracts import ... from statements", () => {
|
|
67
|
+
const source = `
|
|
68
|
+
import { foo } from './utils';
|
|
69
|
+
import bar from "../helpers/bar";
|
|
70
|
+
`;
|
|
71
|
+
const imports = extractRelativeImports(source);
|
|
72
|
+
assert.equal(imports.length, 2);
|
|
73
|
+
assert.ok(imports.some((i) => i.importPath === "./utils"));
|
|
74
|
+
assert.ok(imports.some((i) => i.importPath === "../helpers/bar"));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("extracts side-effect imports", () => {
|
|
78
|
+
const source = `import './polyfill';`;
|
|
79
|
+
const imports = extractRelativeImports(source);
|
|
80
|
+
assert.equal(imports.length, 1);
|
|
81
|
+
assert.equal(imports[0].importPath, "./polyfill");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("extracts require statements", () => {
|
|
85
|
+
const source = `
|
|
86
|
+
const utils = require('./utils');
|
|
87
|
+
const { bar } = require("../helpers/bar");
|
|
88
|
+
`;
|
|
89
|
+
const imports = extractRelativeImports(source);
|
|
90
|
+
assert.equal(imports.length, 2);
|
|
91
|
+
assert.ok(imports.some((i) => i.importPath === "./utils"));
|
|
92
|
+
assert.ok(imports.some((i) => i.importPath === "../helpers/bar"));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("ignores non-relative imports", () => {
|
|
96
|
+
const source = `
|
|
97
|
+
import express from 'express';
|
|
98
|
+
import { readFile } from 'node:fs';
|
|
99
|
+
const lodash = require('lodash');
|
|
100
|
+
`;
|
|
101
|
+
const imports = extractRelativeImports(source);
|
|
102
|
+
assert.equal(imports.length, 0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("reports correct line numbers", () => {
|
|
106
|
+
const source = `// comment
|
|
107
|
+
import { a } from './a';
|
|
108
|
+
// another comment
|
|
109
|
+
import { b } from './b';
|
|
110
|
+
`;
|
|
111
|
+
const imports = extractRelativeImports(source);
|
|
112
|
+
assert.equal(imports.length, 2);
|
|
113
|
+
const importA = imports.find((i) => i.importPath === "./a");
|
|
114
|
+
const importB = imports.find((i) => i.importPath === "./b");
|
|
115
|
+
assert.equal(importA?.lineNum, 2);
|
|
116
|
+
assert.equal(importB?.lineNum, 4);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("handles multiple imports on same line", () => {
|
|
120
|
+
const source = `import a from './a'; import b from './b';`;
|
|
121
|
+
const imports = extractRelativeImports(source);
|
|
122
|
+
assert.equal(imports.length, 2);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("handles empty source", () => {
|
|
126
|
+
const imports = extractRelativeImports("");
|
|
127
|
+
assert.deepEqual(imports, []);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── Import Resolution Tests ─────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
describe("resolveImportPath", () => {
|
|
134
|
+
let tempDir: string;
|
|
135
|
+
|
|
136
|
+
test("resolves file with exact extension", () => {
|
|
137
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
138
|
+
mkdirSync(tempDir, { recursive: true });
|
|
139
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
140
|
+
writeFileSync(join(tempDir, "src", "utils.ts"), "export const a = 1;");
|
|
141
|
+
writeFileSync(join(tempDir, "src", "main.ts"), "import { a } from './utils';");
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const result = resolveImportPath("./utils", "src/main.ts", tempDir);
|
|
145
|
+
assert.ok(result.exists);
|
|
146
|
+
assert.ok(result.resolvedPath?.endsWith("utils.ts"));
|
|
147
|
+
} finally {
|
|
148
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("resolves file without extension", () => {
|
|
153
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
154
|
+
mkdirSync(tempDir, { recursive: true });
|
|
155
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
156
|
+
writeFileSync(join(tempDir, "src", "helpers.js"), "module.exports = {};");
|
|
157
|
+
writeFileSync(join(tempDir, "src", "index.ts"), "");
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const result = resolveImportPath("./helpers", "src/index.ts", tempDir);
|
|
161
|
+
assert.ok(result.exists);
|
|
162
|
+
} finally {
|
|
163
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("resolves directory index file", () => {
|
|
168
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
169
|
+
mkdirSync(tempDir, { recursive: true });
|
|
170
|
+
mkdirSync(join(tempDir, "src", "utils"), { recursive: true });
|
|
171
|
+
writeFileSync(join(tempDir, "src", "utils", "index.ts"), "export {};");
|
|
172
|
+
writeFileSync(join(tempDir, "src", "main.ts"), "");
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const result = resolveImportPath("./utils", "src/main.ts", tempDir);
|
|
176
|
+
assert.ok(result.exists);
|
|
177
|
+
assert.ok(result.resolvedPath?.endsWith("index.ts"));
|
|
178
|
+
} finally {
|
|
179
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("resolves parent directory imports", () => {
|
|
184
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
185
|
+
mkdirSync(tempDir, { recursive: true });
|
|
186
|
+
mkdirSync(join(tempDir, "src", "nested"), { recursive: true });
|
|
187
|
+
writeFileSync(join(tempDir, "src", "utils.ts"), "export {};");
|
|
188
|
+
writeFileSync(join(tempDir, "src", "nested", "child.ts"), "");
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const result = resolveImportPath("../utils", "src/nested/child.ts", tempDir);
|
|
192
|
+
assert.ok(result.exists);
|
|
193
|
+
} finally {
|
|
194
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("fails for non-existent file", () => {
|
|
199
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
200
|
+
mkdirSync(tempDir, { recursive: true });
|
|
201
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
202
|
+
writeFileSync(join(tempDir, "src", "main.ts"), "");
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const result = resolveImportPath("./nonexistent", "src/main.ts", tempDir);
|
|
206
|
+
assert.ok(!result.exists);
|
|
207
|
+
assert.equal(result.resolvedPath, null);
|
|
208
|
+
} finally {
|
|
209
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("handles explicit extension in import", () => {
|
|
214
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
215
|
+
mkdirSync(tempDir, { recursive: true });
|
|
216
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
217
|
+
writeFileSync(join(tempDir, "src", "data.json"), "{}");
|
|
218
|
+
writeFileSync(join(tempDir, "src", "main.ts"), "");
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const result = resolveImportPath("./data.json", "src/main.ts", tempDir);
|
|
222
|
+
assert.ok(result.exists);
|
|
223
|
+
} finally {
|
|
224
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// ─── Import Resolution Check Tests ───────────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
describe("checkImportResolution", () => {
|
|
232
|
+
let tempDir: string;
|
|
233
|
+
|
|
234
|
+
test("passes when all imports resolve", () => {
|
|
235
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
236
|
+
mkdirSync(tempDir, { recursive: true });
|
|
237
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
238
|
+
writeFileSync(join(tempDir, "src", "utils.ts"), "export const a = 1;");
|
|
239
|
+
writeFileSync(
|
|
240
|
+
join(tempDir, "src", "main.ts"),
|
|
241
|
+
"import { a } from './utils';"
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const task = createTask({
|
|
246
|
+
id: "T01",
|
|
247
|
+
key_files: ["src/main.ts"],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
251
|
+
assert.deepEqual(results, []);
|
|
252
|
+
} finally {
|
|
253
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("fails when import doesn't resolve", () => {
|
|
258
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
259
|
+
mkdirSync(tempDir, { recursive: true });
|
|
260
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
261
|
+
writeFileSync(
|
|
262
|
+
join(tempDir, "src", "main.ts"),
|
|
263
|
+
"import { a } from './nonexistent';"
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
const task = createTask({
|
|
268
|
+
id: "T01",
|
|
269
|
+
key_files: ["src/main.ts"],
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
273
|
+
assert.equal(results.length, 1);
|
|
274
|
+
assert.equal(results[0].category, "import");
|
|
275
|
+
assert.equal(results[0].passed, false);
|
|
276
|
+
assert.equal(results[0].blocking, true);
|
|
277
|
+
assert.ok(results[0].message.includes("nonexistent"));
|
|
278
|
+
assert.ok(results[0].target.includes("src/main.ts"));
|
|
279
|
+
} finally {
|
|
280
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("skips non-JS/TS files", () => {
|
|
285
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
286
|
+
mkdirSync(tempDir, { recursive: true });
|
|
287
|
+
writeFileSync(join(tempDir, "README.md"), "# Docs");
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const task = createTask({
|
|
291
|
+
id: "T01",
|
|
292
|
+
key_files: ["README.md"],
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
296
|
+
assert.deepEqual(results, []);
|
|
297
|
+
} finally {
|
|
298
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("handles multiple files with multiple imports", () => {
|
|
303
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
304
|
+
mkdirSync(tempDir, { recursive: true });
|
|
305
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
306
|
+
writeFileSync(join(tempDir, "src", "utils.ts"), "export const a = 1;");
|
|
307
|
+
writeFileSync(
|
|
308
|
+
join(tempDir, "src", "a.ts"),
|
|
309
|
+
"import { a } from './utils';\nimport { b } from './missing';"
|
|
310
|
+
);
|
|
311
|
+
writeFileSync(
|
|
312
|
+
join(tempDir, "src", "b.ts"),
|
|
313
|
+
"import { x } from './also-missing';"
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const task = createTask({
|
|
318
|
+
id: "T01",
|
|
319
|
+
key_files: ["src/a.ts", "src/b.ts"],
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
323
|
+
assert.equal(results.length, 2);
|
|
324
|
+
assert.ok(results.some((r) => r.message.includes("missing")));
|
|
325
|
+
assert.ok(results.some((r) => r.message.includes("also-missing")));
|
|
326
|
+
} finally {
|
|
327
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("skips if key_file doesn't exist", () => {
|
|
332
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
333
|
+
mkdirSync(tempDir, { recursive: true });
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const task = createTask({
|
|
337
|
+
id: "T01",
|
|
338
|
+
key_files: ["src/deleted.ts"],
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
342
|
+
assert.deepEqual(results, []);
|
|
343
|
+
} finally {
|
|
344
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ─── Cross-Task Signature Tests ──────────────────────────────────────────────
|
|
350
|
+
|
|
351
|
+
describe("checkCrossTaskSignatures", () => {
|
|
352
|
+
let tempDir: string;
|
|
353
|
+
|
|
354
|
+
test("passes when no prior tasks exist", () => {
|
|
355
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
356
|
+
mkdirSync(tempDir, { recursive: true });
|
|
357
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
358
|
+
writeFileSync(
|
|
359
|
+
join(tempDir, "src", "api.ts"),
|
|
360
|
+
"export function getData(): string { return ''; }"
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const task = createTask({
|
|
365
|
+
id: "T02",
|
|
366
|
+
key_files: ["src/api.ts"],
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const results = checkCrossTaskSignatures(task, [], tempDir);
|
|
370
|
+
assert.deepEqual(results, []);
|
|
371
|
+
} finally {
|
|
372
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("passes when signatures match", () => {
|
|
377
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
378
|
+
mkdirSync(tempDir, { recursive: true });
|
|
379
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
380
|
+
writeFileSync(
|
|
381
|
+
join(tempDir, "src", "utils.ts"),
|
|
382
|
+
"export function process(data: string): boolean { return true; }"
|
|
383
|
+
);
|
|
384
|
+
writeFileSync(
|
|
385
|
+
join(tempDir, "src", "api.ts"),
|
|
386
|
+
"export function process(data: string): boolean { return false; }"
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const priorTask = createTask({
|
|
391
|
+
id: "T01",
|
|
392
|
+
key_files: ["src/utils.ts"],
|
|
393
|
+
});
|
|
394
|
+
const currentTask = createTask({
|
|
395
|
+
id: "T02",
|
|
396
|
+
key_files: ["src/api.ts"],
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const results = checkCrossTaskSignatures(currentTask, [priorTask], tempDir);
|
|
400
|
+
assert.deepEqual(results, []);
|
|
401
|
+
} finally {
|
|
402
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("warns on parameter mismatch (non-blocking)", () => {
|
|
407
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
408
|
+
mkdirSync(tempDir, { recursive: true });
|
|
409
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
410
|
+
writeFileSync(
|
|
411
|
+
join(tempDir, "src", "utils.ts"),
|
|
412
|
+
"export function save(name: string): void {}"
|
|
413
|
+
);
|
|
414
|
+
writeFileSync(
|
|
415
|
+
join(tempDir, "src", "api.ts"),
|
|
416
|
+
"export function save(name: string, id: number): void {}"
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
const priorTask = createTask({
|
|
421
|
+
id: "T01",
|
|
422
|
+
key_files: ["src/utils.ts"],
|
|
423
|
+
});
|
|
424
|
+
const currentTask = createTask({
|
|
425
|
+
id: "T02",
|
|
426
|
+
key_files: ["src/api.ts"],
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const results = checkCrossTaskSignatures(currentTask, [priorTask], tempDir);
|
|
430
|
+
assert.equal(results.length, 1);
|
|
431
|
+
assert.equal(results[0].category, "signature");
|
|
432
|
+
assert.equal(results[0].target, "save");
|
|
433
|
+
assert.equal(results[0].passed, false);
|
|
434
|
+
assert.equal(results[0].blocking, false);
|
|
435
|
+
assert.ok(results[0].message.includes("parameters"));
|
|
436
|
+
} finally {
|
|
437
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test("warns on return type mismatch (non-blocking)", () => {
|
|
442
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
443
|
+
mkdirSync(tempDir, { recursive: true });
|
|
444
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
445
|
+
writeFileSync(
|
|
446
|
+
join(tempDir, "src", "utils.ts"),
|
|
447
|
+
"export function fetch(): string { return ''; }"
|
|
448
|
+
);
|
|
449
|
+
writeFileSync(
|
|
450
|
+
join(tempDir, "src", "api.ts"),
|
|
451
|
+
"export function fetch(): number { return 0; }"
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
const priorTask = createTask({
|
|
456
|
+
id: "T01",
|
|
457
|
+
key_files: ["src/utils.ts"],
|
|
458
|
+
});
|
|
459
|
+
const currentTask = createTask({
|
|
460
|
+
id: "T02",
|
|
461
|
+
key_files: ["src/api.ts"],
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
const results = checkCrossTaskSignatures(currentTask, [priorTask], tempDir);
|
|
465
|
+
assert.equal(results.length, 1);
|
|
466
|
+
assert.ok(results[0].message.includes("return"));
|
|
467
|
+
} finally {
|
|
468
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test("handles multiple prior tasks", () => {
|
|
473
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
474
|
+
mkdirSync(tempDir, { recursive: true });
|
|
475
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
476
|
+
writeFileSync(
|
|
477
|
+
join(tempDir, "src", "types.ts"),
|
|
478
|
+
"export function parse(s: string): object { return {}; }"
|
|
479
|
+
);
|
|
480
|
+
writeFileSync(
|
|
481
|
+
join(tempDir, "src", "utils.ts"),
|
|
482
|
+
"export function validate(x: object): boolean { return true; }"
|
|
483
|
+
);
|
|
484
|
+
writeFileSync(
|
|
485
|
+
join(tempDir, "src", "api.ts"),
|
|
486
|
+
`export function parse(s: number): object { return {}; }
|
|
487
|
+
export function validate(x: object): boolean { return true; }`
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
const priorTask1 = createTask({ id: "T01", key_files: ["src/types.ts"] });
|
|
492
|
+
const priorTask2 = createTask({ id: "T02", key_files: ["src/utils.ts"] });
|
|
493
|
+
const currentTask = createTask({ id: "T03", key_files: ["src/api.ts"] });
|
|
494
|
+
|
|
495
|
+
const results = checkCrossTaskSignatures(
|
|
496
|
+
currentTask,
|
|
497
|
+
[priorTask1, priorTask2],
|
|
498
|
+
tempDir
|
|
499
|
+
);
|
|
500
|
+
// Should have 1 warning for parse() parameter mismatch
|
|
501
|
+
assert.equal(results.length, 1);
|
|
502
|
+
assert.ok(results[0].message.includes("parse"));
|
|
503
|
+
} finally {
|
|
504
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// ─── Pattern Consistency Tests ───────────────────────────────────────────────
|
|
510
|
+
|
|
511
|
+
describe("checkPatternConsistency", () => {
|
|
512
|
+
let tempDir: string;
|
|
513
|
+
|
|
514
|
+
test("passes when async style is consistent (await only)", () => {
|
|
515
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
516
|
+
mkdirSync(tempDir, { recursive: true });
|
|
517
|
+
writeFileSync(
|
|
518
|
+
join(tempDir, "api.ts"),
|
|
519
|
+
`async function getData(): Promise<string> {
|
|
520
|
+
const result = await fetch('/api');
|
|
521
|
+
return await result.text();
|
|
522
|
+
}`
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
const task = createTask({ id: "T01", key_files: ["api.ts"] });
|
|
527
|
+
const results = checkPatternConsistency(task, [], tempDir);
|
|
528
|
+
const asyncResults = results.filter((r) => r.message.includes("async"));
|
|
529
|
+
assert.equal(asyncResults.length, 0);
|
|
530
|
+
} finally {
|
|
531
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test("passes when async style is consistent (.then only)", () => {
|
|
536
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
537
|
+
mkdirSync(tempDir, { recursive: true });
|
|
538
|
+
writeFileSync(
|
|
539
|
+
join(tempDir, "api.ts"),
|
|
540
|
+
`function getData(): Promise<string> {
|
|
541
|
+
return fetch('/api').then(r => r.text());
|
|
542
|
+
}`
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
try {
|
|
546
|
+
const task = createTask({ id: "T01", key_files: ["api.ts"] });
|
|
547
|
+
const results = checkPatternConsistency(task, [], tempDir);
|
|
548
|
+
const asyncResults = results.filter((r) => r.message.includes("async"));
|
|
549
|
+
assert.equal(asyncResults.length, 0);
|
|
550
|
+
} finally {
|
|
551
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test("warns when mixing async/await with .then()", () => {
|
|
556
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
557
|
+
mkdirSync(tempDir, { recursive: true });
|
|
558
|
+
writeFileSync(
|
|
559
|
+
join(tempDir, "api.ts"),
|
|
560
|
+
`async function getData(): Promise<string> {
|
|
561
|
+
const result = await fetch('/api');
|
|
562
|
+
return result.text().then(t => t.toUpperCase());
|
|
563
|
+
}`
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
try {
|
|
567
|
+
const task = createTask({ id: "T01", key_files: ["api.ts"] });
|
|
568
|
+
const results = checkPatternConsistency(task, [], tempDir);
|
|
569
|
+
const asyncResults = results.filter((r) => r.message.includes("async"));
|
|
570
|
+
assert.equal(asyncResults.length, 1);
|
|
571
|
+
assert.equal(asyncResults[0].category, "pattern");
|
|
572
|
+
assert.equal(asyncResults[0].passed, true); // Warning only
|
|
573
|
+
assert.equal(asyncResults[0].blocking, false);
|
|
574
|
+
} finally {
|
|
575
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test("passes when naming is consistent (camelCase only)", () => {
|
|
580
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
581
|
+
mkdirSync(tempDir, { recursive: true });
|
|
582
|
+
writeFileSync(
|
|
583
|
+
join(tempDir, "api.ts"),
|
|
584
|
+
`function getUserData() {}
|
|
585
|
+
const processItems = () => {};
|
|
586
|
+
function validateInput() {}`
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
const task = createTask({ id: "T01", key_files: ["api.ts"] });
|
|
591
|
+
const results = checkPatternConsistency(task, [], tempDir);
|
|
592
|
+
const namingResults = results.filter((r) => r.message.includes("naming") || r.message.includes("Case"));
|
|
593
|
+
assert.equal(namingResults.length, 0);
|
|
594
|
+
} finally {
|
|
595
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
test("warns when mixing camelCase and snake_case", () => {
|
|
600
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
601
|
+
mkdirSync(tempDir, { recursive: true });
|
|
602
|
+
writeFileSync(
|
|
603
|
+
join(tempDir, "api.ts"),
|
|
604
|
+
`function getUserData() {}
|
|
605
|
+
function process_items() {}
|
|
606
|
+
const validate_input = () => {};`
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
const task = createTask({ id: "T01", key_files: ["api.ts"] });
|
|
611
|
+
const results = checkPatternConsistency(task, [], tempDir);
|
|
612
|
+
const namingResults = results.filter((r) => r.message.includes("camelCase") || r.message.includes("snake_case"));
|
|
613
|
+
assert.equal(namingResults.length, 1);
|
|
614
|
+
assert.equal(namingResults[0].category, "pattern");
|
|
615
|
+
assert.equal(namingResults[0].blocking, false);
|
|
616
|
+
} finally {
|
|
617
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
test("skips non-JS/TS files", () => {
|
|
622
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
623
|
+
mkdirSync(tempDir, { recursive: true });
|
|
624
|
+
writeFileSync(join(tempDir, "config.json"), '{"key": "value"}');
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
const task = createTask({ id: "T01", key_files: ["config.json"] });
|
|
628
|
+
const results = checkPatternConsistency(task, [], tempDir);
|
|
629
|
+
assert.deepEqual(results, []);
|
|
630
|
+
} finally {
|
|
631
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
// ─── runPostExecutionChecks Integration Tests ────────────────────────────────
|
|
637
|
+
|
|
638
|
+
describe("runPostExecutionChecks", () => {
|
|
639
|
+
let tempDir: string;
|
|
640
|
+
|
|
641
|
+
test("returns pass status when all checks pass", () => {
|
|
642
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
643
|
+
mkdirSync(tempDir, { recursive: true });
|
|
644
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
645
|
+
writeFileSync(join(tempDir, "src", "utils.ts"), "export const a = 1;");
|
|
646
|
+
writeFileSync(
|
|
647
|
+
join(tempDir, "src", "main.ts"),
|
|
648
|
+
`import { a } from './utils';
|
|
649
|
+
function processData(): void {}`
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
try {
|
|
653
|
+
const task = createTask({ id: "T01", key_files: ["src/main.ts"] });
|
|
654
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
655
|
+
assert.equal(result.status, "pass");
|
|
656
|
+
assert.equal(result.checks.length, 0);
|
|
657
|
+
assert.ok(result.durationMs >= 0);
|
|
658
|
+
} finally {
|
|
659
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
test("returns fail status when blocking failure exists", () => {
|
|
664
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
665
|
+
mkdirSync(tempDir, { recursive: true });
|
|
666
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
667
|
+
writeFileSync(
|
|
668
|
+
join(tempDir, "src", "main.ts"),
|
|
669
|
+
"import { a } from './nonexistent';"
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
try {
|
|
673
|
+
const task = createTask({ id: "T01", key_files: ["src/main.ts"] });
|
|
674
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
675
|
+
assert.equal(result.status, "fail");
|
|
676
|
+
assert.ok(result.checks.length > 0);
|
|
677
|
+
assert.ok(result.checks.some((c) => c.blocking === true));
|
|
678
|
+
} finally {
|
|
679
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test("returns warn status for non-blocking issues only", () => {
|
|
684
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
685
|
+
mkdirSync(tempDir, { recursive: true });
|
|
686
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
687
|
+
writeFileSync(
|
|
688
|
+
join(tempDir, "src", "api.ts"),
|
|
689
|
+
`async function getData() {
|
|
690
|
+
const result = await fetch('/api');
|
|
691
|
+
return result.text().then(t => t);
|
|
692
|
+
}`
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
try {
|
|
696
|
+
const task = createTask({ id: "T01", key_files: ["src/api.ts"] });
|
|
697
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
698
|
+
assert.equal(result.status, "warn");
|
|
699
|
+
assert.ok(result.checks.some((c) => c.category === "pattern"));
|
|
700
|
+
} finally {
|
|
701
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
test("combines results from all check types", () => {
|
|
706
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
707
|
+
mkdirSync(tempDir, { recursive: true });
|
|
708
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
709
|
+
writeFileSync(
|
|
710
|
+
join(tempDir, "src", "utils.ts"),
|
|
711
|
+
"export function process(s: string): void {}"
|
|
712
|
+
);
|
|
713
|
+
writeFileSync(
|
|
714
|
+
join(tempDir, "src", "api.ts"),
|
|
715
|
+
`import { x } from './missing';
|
|
716
|
+
async function getData() {
|
|
717
|
+
await fetch('/api');
|
|
718
|
+
return fetch('/api2').then(r => r);
|
|
719
|
+
}
|
|
720
|
+
export function process(n: number): void {}`
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
try {
|
|
724
|
+
const priorTask = createTask({ id: "T01", key_files: ["src/utils.ts"] });
|
|
725
|
+
const currentTask = createTask({ id: "T02", key_files: ["src/api.ts"] });
|
|
726
|
+
|
|
727
|
+
const result = runPostExecutionChecks(currentTask, [priorTask], tempDir);
|
|
728
|
+
assert.equal(result.status, "fail"); // Import failure is blocking
|
|
729
|
+
|
|
730
|
+
const categories = new Set(result.checks.map((c) => c.category));
|
|
731
|
+
assert.ok(categories.has("import")); // From unresolved import
|
|
732
|
+
assert.ok(categories.has("signature")); // From signature mismatch
|
|
733
|
+
assert.ok(categories.has("pattern")); // From async style drift
|
|
734
|
+
} finally {
|
|
735
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
test("reports duration in milliseconds", () => {
|
|
740
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
741
|
+
mkdirSync(tempDir, { recursive: true });
|
|
742
|
+
|
|
743
|
+
try {
|
|
744
|
+
const task = createTask({ id: "T01", key_files: [] });
|
|
745
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
746
|
+
assert.ok(typeof result.durationMs === "number");
|
|
747
|
+
assert.ok(result.durationMs >= 0);
|
|
748
|
+
} finally {
|
|
749
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
test("handles empty key_files array", () => {
|
|
754
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
755
|
+
mkdirSync(tempDir, { recursive: true });
|
|
756
|
+
|
|
757
|
+
try {
|
|
758
|
+
const task = createTask({ id: "T01", key_files: [] });
|
|
759
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
760
|
+
assert.equal(result.status, "pass");
|
|
761
|
+
assert.deepEqual(result.checks, []);
|
|
762
|
+
} finally {
|
|
763
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
// ─── PostExecutionResult Type Tests ──────────────────────────────────────────
|
|
769
|
+
|
|
770
|
+
describe("PostExecutionResult type", () => {
|
|
771
|
+
test("status is one of pass, warn, fail", () => {
|
|
772
|
+
const tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
773
|
+
mkdirSync(tempDir, { recursive: true });
|
|
774
|
+
|
|
775
|
+
try {
|
|
776
|
+
const task = createTask({ id: "T01", key_files: [] });
|
|
777
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
778
|
+
assert.ok(["pass", "warn", "fail"].includes(result.status));
|
|
779
|
+
} finally {
|
|
780
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
test("checks array matches PostExecutionCheckJSON schema", () => {
|
|
785
|
+
const tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
786
|
+
mkdirSync(tempDir, { recursive: true });
|
|
787
|
+
mkdirSync(join(tempDir, "src"), { recursive: true });
|
|
788
|
+
writeFileSync(
|
|
789
|
+
join(tempDir, "src", "main.ts"),
|
|
790
|
+
"import { a } from './missing';"
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
try {
|
|
794
|
+
const task = createTask({ id: "T01", key_files: ["src/main.ts"] });
|
|
795
|
+
const result = runPostExecutionChecks(task, [], tempDir);
|
|
796
|
+
|
|
797
|
+
for (const check of result.checks) {
|
|
798
|
+
assert.ok(
|
|
799
|
+
["import", "signature", "pattern"].includes(check.category),
|
|
800
|
+
`Invalid category: ${check.category}`
|
|
801
|
+
);
|
|
802
|
+
assert.ok(typeof check.target === "string");
|
|
803
|
+
assert.ok(typeof check.passed === "boolean");
|
|
804
|
+
assert.ok(typeof check.message === "string");
|
|
805
|
+
if (check.blocking !== undefined) {
|
|
806
|
+
assert.ok(typeof check.blocking === "boolean");
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
} finally {
|
|
810
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
});
|