openclaw/test/scripts/check.test.ts
Peter Steinberger be052821d6
chore: block new wall-clock test timeout races (#162357)
Tests must not race real timers (docs/help/testing/writing-tests.md, Cost
budget and Flake triage), yet withTestTimeout and raceWithTimeoutResult call
sites grew from 123 (2026-09-01) to 478 (2026-09-26). #161691 audited them,
fixed the worst files, and added the timer-free replacements
awaitGateBeforeSettlement and withinTest. This stops new uses.

check:test-timeout-race-ratchet keeps per-file counts in
config/test-timeout-race-baseline.txt (172 files, 403 sites) and only lets
them shrink. It parses every repository code file that mentions either helper
and counts each identifier reference except import specifiers: plain and
generic calls, namespace calls, aliases, re-exports, and local copies such as
the private raceWithTimeoutResult in fetch-guard.ssrf.test.ts. Comments and
strings do not count; test/helpers/promise.ts owns the helpers and is
excluded. Failures point authors at awaitGateBeforeSettlement, withinTest, or
fake timers through the owner's clock seam; removed sites require --prune.

The per-file count lifecycle (merge-base comparison, verified renames, base
drift allowance, prune, shrink demand) moves from the assertion-safety ratchet
into scripts/lib/shrink-ratchet.mts so both ratchets share one owner. The
assertion-safety output and exit codes are unchanged.

Wiring: scripts/check.mts preflight, check:changed routing for any code file
or the baseline, and one added line each in the existing PR baseline-ratchet
step and the main-push ratchet step (ci.yml +164 bytes, no new steps).

Proof (Linux Testbox, pre-rebase tree; baseline refreshed after rebase): new ratchet passes in under 1 s; injecting a call into
a baselined file and a new test file fails with the guidance; assertion-safety
reports its unchanged totals; tsgo:scripts, tsgo:test:root, and
check:changed --base origin/main pass; the affected test/scripts files pass.
New test file: 4 s with --maxWorkers=1.

Release note context: maintainer tooling only; no user-visible change.

CI: run 36812237216 green except checks-windows-node-test-3, which fails the same
package-update-swap.windows.test.ts assertion on main (scheduled run 36807764485);
fix owned by #162361.

Related: #161691
2026-09-30 21:39:29 -07:00

77 lines
2.6 KiB
TypeScript

// Check tests cover check script behavior.
import { spawnSync } from "node:child_process";
import { describe, expect, it } from "vitest";
import { PREFLIGHT_CHECKS, runCommand } from "../../scripts/check.mts";
describe("scripts/check", () => {
function runCheck(...args: string[]) {
return spawnSync(process.execPath, ["--import", "tsx", "scripts/check.mts", ...args], {
cwd: process.cwd(),
encoding: "utf8",
});
}
it("prints help without running check stages", () => {
const result = runCheck("--help");
expect(result.status).toBe(0);
expect(result.stderr).toBe("");
expect(result.stdout).toContain("Usage: node --import tsx scripts/check.mts");
expect(result.stdout).not.toContain("[check]");
});
it("rejects unknown args before running check stages", () => {
for (const args of [["--bogus"], ["bogus", "--help"]]) {
const result = runCheck(...args);
expect(result.status).toBe(2);
expect(result.stdout).toBe("");
expect(result.stderr).toContain(`unknown argument: ${args[0]}`);
expect(result.stderr).toContain("Usage: node --import tsx scripts/check.mts");
expect(result.stderr).not.toContain("[check]");
}
});
it("runs pnpm commands through the managed child runner", async () => {
const calls: Array<{ args: string[]; bin: string }> = [];
const result = await runCommand(
{ args: ["lint"], name: "lint" },
async (options: { args: string[]; bin: string }) => {
calls.push(options);
return 0;
},
);
expect(calls).toEqual([{ args: ["lint"], bin: "pnpm" }]);
expect(result).toMatchObject({ name: "lint", status: 0 });
expect(result.durationMs).toBeGreaterThanOrEqual(0);
});
it("keeps script policy guards in the aggregate preflight", () => {
expect(PREFLIGHT_CHECKS).not.toContainEqual({
name: "environment variable count ratchet",
args: ["check:env-var-count"],
});
expect(PREFLIGHT_CHECKS).toContainEqual(
expect.objectContaining({
name: "max-lines suppression ratchet",
args: ["check:max-lines-ratchet"],
}),
);
expect(PREFLIGHT_CHECKS).toContainEqual(
expect.objectContaining({
name: "assertion SAFETY comment ratchet",
args: ["check:assertion-safety"],
}),
);
expect(PREFLIGHT_CHECKS).toContainEqual({
name: "test timeout race ratchet",
args: ["check:test-timeout-race-ratchet"],
usesBase: true,
});
expect(PREFLIGHT_CHECKS).toContainEqual({
name: "script TypeScript erasability",
args: ["check:script-erasability"],
});
});
});