singularity-forge/scripts/check-test-imports.test.mjs
Mikael Hugo f55d490e1d fix(subagent-runner): drop spurious 10s STUCK warning on session.prompt
The phaseWatchdog at 10s fired "STUCK phase=session.prompt" on every
healthy LLM call longer than 10 seconds. Verified via strace on the
running dogfood sf: bytes were actively flowing on the TLS socket
(fd 29) to the LLM provider while STUCK was being logged — the
session.prompt was never actually stuck, the watchdog was just
diagnostic-only and oblivious to stream activity.

The noOutputTimeoutMs watchdog (set to 60s for triage in commit
d80060fec) is the actual kill mechanism. It is already event-aware:
every meaningful subagent event resets the timer via armNoOutputTimer
+ isMeaningfulSubagentOutputEvent. The 10s STUCK warning was added
in commit 67e5ac9db as investigation infrastructure for the
sf-mp8e02m1-zpk903 family of bugs, but now it is just noise that
makes legitimate 30-200s LLM responses look broken.

Keeps the 10s STUCK watchdog for the three setup phases
(resourceLoader.reload, createAgentSession, bindExtensions) where
10s of silence is a real hang signal — those phases normally run in
sub-second.

Also includes:
- biome.json: bump $schema URL from 2.4.14 to 2.4.15 to match the
  current biome CLI (clears the deserialize warning)
- scripts/check-test-imports.{,test.}mjs: format + drop a useless
  regex escape that biome flagged in landed code

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:49:43 +02:00

223 lines
6.5 KiB
JavaScript

#!/usr/bin/env node
/**
* check-test-imports.test.mjs — regression test for the test-import-drift guard.
*
* Verifies that check-test-imports.mjs correctly detects the anti-pattern:
* a test file uses itemized `import { foo }` but references `bar` (not imported).
*
* All file paths are absolute to avoid resolution ambiguity when running from
* different working directories.
*/
import { execSync } from "node:child_process";
import { writeFileSync, rmdirSync, mkdirSync, existsSync } from "node:fs";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = fileURLToPath(new URL(".", import.meta.url)); // scripts/
const repoRoot = resolve(__dirname, ".."); // parent = repo root
const script = resolve(__dirname, "check-test-imports.mjs");
const tmpDir = resolve(__dirname, "tmp-check-test-imports");
// ── Helpers ───────────────────────────────────────────────────────────────────
/** Run the check script from the repo root, return {exitCode, stdout, stderr}. */
function run(args) {
const cmd = ["node", "--", script, ...args].join(" ");
try {
const out = execSync(cmd, {
cwd: repoRoot,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
});
return { exitCode: 0, stdout: out, stderr: "" };
} catch (err) {
return {
exitCode: err.status ?? 1,
stdout: (err.stdout || "").toString(),
stderr: (err.stderr || "").toString(),
};
}
}
/** Create a temp test file, return absolute path. */
function makeTempFile(id, content) {
try {
mkdirSync(tmpDir, { recursive: true });
} catch {
/* */
}
const file = resolve(tmpDir, `regression-${id}.test.mjs`);
writeFileSync(file, content);
return file;
}
function cleanupTempDir() {
try {
rmdirSync(tmpDir);
} catch {
/* ignore */
}
}
// ── Tests ─────────────────────────────────────────────────────────────────────
let passed = 0;
let failed = 0;
function assert(condition, message) {
if (condition) {
console.log(`${message}`);
passed++;
} else {
console.error(`${message}`);
failed++;
}
}
console.log("check-test-imports regression tests\n");
// ── Test 1: Script is loadable and produces valid JSON ───────────────────────
// Use a real test file that exists under the repo root
const realFile = resolve(repoRoot, "tests/autonomous-solver.test.mjs");
if (!existsSync(realFile)) {
console.error(` ⚠️ Test file not found: ${realFile}`);
} else {
const result1 = run([realFile, "--json"]);
try {
const parsed = JSON.parse(result1.stdout);
assert(
typeof parsed.count === "number",
`Valid JSON with count: ${parsed.count}`,
);
} catch {
assert(false, `Script should produce valid JSON output`);
}
}
// ── Test 2: Detects undeclared identifier in anti-pattern file ───────────────
console.log("\n[2] Detects undeclared identifier in anti-pattern file");
const file2 = makeTempFile(
"regression-02",
`
// Regression test — generated by check-test-imports.test.mjs
// DO NOT COMMIT
import { describe, it, expect } from "vitest";
import { fn1, fn2, fn3, fn4, fn5, fn6 } from "./fixture.mjs";
describe("test", () => {
it("uses undeclaredFn", () => {
undeclaredFn();
});
});
`,
);
const result2 = run([file2, "--json"]);
try {
const parsed = JSON.parse(result2.stdout);
assert(
parsed.count > 0,
`Drift detected in anti-pattern file (count: ${parsed.count})`,
);
assert(
result2.exitCode === 1,
`Exit code 1 when drift present (got: ${result2.exitCode})`,
);
} catch {
assert(false, `Should not throw when drift is detected`);
}
// ── Test 3: Clean file with namespace import passes ─────────────────────────
console.log("\n[3] Clean file with namespace import + local vars passes");
const file3 = makeTempFile(
"regression-03",
`
// Clean: namespace import + only local variables
// DO NOT COMMIT
import { describe, it, expect } from "vitest";
import * as Fixtures from "./fixture.mjs";
const myLocalVar = Fixtures.fn1();
describe("test", () => {
it("uses Fixtures methods", () => {
expect(myLocalVar).toBeDefined();
});
});
`,
);
const result3 = run([file3, "--json"]);
try {
const parsed = JSON.parse(result3.stdout);
assert(parsed.count === 0, `Clean file: count ${parsed.count}`);
} catch {
assert(false, `Script should not throw on clean file`);
}
// ── Test 4: Local const/let declarations not flagged ───────────────────────
console.log("\n[4] Local const/let declarations not flagged");
const file4 = makeTempFile(
"regression-04",
`
// Local variable declarations should not be flagged
// DO NOT COMMIT
import { describe, it, expect } from "vitest";
import { foo } from "./fixture.mjs";
const myLocalVar = foo();
const anotherLocal = foo();
describe("test", () => {
it("uses local variables", () => {
expect(myLocalVar).toBeDefined();
});
});
`,
);
const result4 = run([file4, "--json"]);
try {
const parsed = JSON.parse(result4.stdout);
assert(
parsed.count === 0,
`Local variables not flagged (count: ${parsed.count})`,
);
} catch {
assert(false, `Should not throw on clean file`);
}
// ── Test 5: Short lowercase variables not flagged ───────────────────────────
console.log("\n[5] Short lowercase variables not flagged");
const file5 = makeTempFile(
"regression-05",
`
// Short lowercase vars like i, fn are common test locals
// DO NOT COMMIT
import { describe, it, expect } from "vitest";
import { fn } from "./fixture.mjs";
describe("test", () => {
it("works with short vars", () => {
const i = fn();
const j = i + 1;
expect(j).toBeGreaterThan(0);
});
});
`,
);
const result5 = run([file5, "--json"]);
try {
const parsed = JSON.parse(result5.stdout);
assert(
parsed.count === 0,
`Short lowercase vars not flagged (count: ${parsed.count})`,
);
} catch {
assert(false, `Should not throw on clean file`);
}
// ── Summary ─────────────────────────────────────────────────────────────────
console.log(`\n${"-".repeat(50)}`);
console.log(`Results: ${passed} passed, ${failed} failed`);
cleanupTempDir();
process.exit(failed > 0 ? 1 : 0);