Files
OmniRoute/tests/unit/build/check-lockfile.test.ts
Diego Rodrigues de Sa e Souza 3d4f3e4960 test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) (#11968)
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966)

Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.

One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.

Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.

* fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff

The gate shells out to `git diff` through execFileSync with Node's default 1 MB
maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to
overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing
anything. 64 MB is far above any real PR and costs nothing when unused.
2026-08-29 01:17:40 -03:00

316 lines
11 KiB
TypeScript

// tests/unit/build/check-lockfile.test.ts
// TDD tests for check-lockfile.mjs — lockfile policy gate (Task 7.7).
//
// Strategy: the lockfile-lint binary is an external CLI tool; we do not spawn it
// in unit tests. Instead, we test the exported policy helpers and inject the
// process boundary used by the workspace consistency runner:
// - getLockfileLintConfig() — returns the policy configuration object
// - buildLockfileLintArgs() — maps a config object to the argv array
//
// This validates the policy settings and the arg-assembly logic without requiring
// a real package-lock.json or a network call.
import test from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
// @ts-expect-error — .mjs helper has no type declarations; runtime shape is known.
import {
getLockfileLintConfig,
buildLockfileLintArgs,
getWorkspaceDependencyCheckCommand,
runWorkspaceDependencyCheck,
} from "../../../scripts/check/check-lockfile.mjs";
// ---------------------------------------------------------------------------
// getLockfileLintConfig
// ---------------------------------------------------------------------------
test("getLockfileLintConfig: returns an object with required keys", () => {
const cfg = getLockfileLintConfig();
assert.ok(typeof cfg === "object" && cfg !== null, "config should be an object");
assert.ok("lockfilePath" in cfg, "should have lockfilePath");
assert.ok("type" in cfg, "should have type");
assert.ok("validateHttps" in cfg, "should have validateHttps");
assert.ok("validateIntegrity" in cfg, "should have validateIntegrity");
assert.ok("allowedHosts" in cfg, "should have allowedHosts");
});
test("getLockfileLintConfig: lockfilePath points to package-lock.json", () => {
const cfg = getLockfileLintConfig();
assert.ok(
cfg.lockfilePath.endsWith("package-lock.json"),
`lockfilePath should end with package-lock.json, got: ${cfg.lockfilePath}`
);
});
test("getLockfileLintConfig: type is npm", () => {
const cfg = getLockfileLintConfig();
assert.equal(cfg.type, "npm");
});
test("getLockfileLintConfig: validateHttps is true (HTTPS enforcement)", () => {
const cfg = getLockfileLintConfig();
assert.equal(cfg.validateHttps, true, "HTTPS enforcement must be enabled");
});
test("getLockfileLintConfig: validateIntegrity is true (integrity enforcement)", () => {
const cfg = getLockfileLintConfig();
assert.equal(cfg.validateIntegrity, true, "integrity validation must be enabled");
});
test("getLockfileLintConfig: allowedHosts includes npm (official registry)", () => {
const cfg = getLockfileLintConfig();
assert.ok(Array.isArray(cfg.allowedHosts), "allowedHosts should be an array");
assert.ok(
cfg.allowedHosts.includes("npm"),
"npm must be in allowedHosts (covers registry.npmjs.org)"
);
});
test("getLockfileLintConfig: no http:// hosts in allowedHosts", () => {
const cfg = getLockfileLintConfig();
for (const host of cfg.allowedHosts) {
assert.ok(
!host.startsWith("http://"),
`allowedHosts must not contain http:// URLs, found: ${host}`
);
}
});
// ---------------------------------------------------------------------------
// buildLockfileLintArgs
// ---------------------------------------------------------------------------
test("buildLockfileLintArgs: includes --path and --type", () => {
const cfg = getLockfileLintConfig();
const args = buildLockfileLintArgs(cfg);
assert.ok(args.includes("--path"), "args should include --path");
assert.ok(args.includes("--type"), "args should include --type");
const pathIdx = args.indexOf("--path");
assert.equal(args[pathIdx + 1], cfg.lockfilePath);
const typeIdx = args.indexOf("--type");
assert.equal(args[typeIdx + 1], cfg.type);
});
test("buildLockfileLintArgs: includes --validate-https when validateHttps=true", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: true,
validateIntegrity: false,
allowedHosts: [],
});
assert.ok(args.includes("--validate-https"), "should include --validate-https");
});
test("buildLockfileLintArgs: omits --validate-https when validateHttps=false", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: false,
validateIntegrity: false,
allowedHosts: [],
});
assert.ok(!args.includes("--validate-https"), "should not include --validate-https");
});
test("buildLockfileLintArgs: includes --validate-integrity when validateIntegrity=true", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: false,
validateIntegrity: true,
allowedHosts: [],
});
assert.ok(args.includes("--validate-integrity"), "should include --validate-integrity");
});
test("buildLockfileLintArgs: omits --validate-integrity when validateIntegrity=false", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: false,
validateIntegrity: false,
allowedHosts: [],
});
assert.ok(!args.includes("--validate-integrity"), "should not include --validate-integrity");
});
test("buildLockfileLintArgs: includes --allowed-hosts and its values", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: false,
validateIntegrity: false,
allowedHosts: ["npm", "myprivatescope"],
});
assert.ok(args.includes("--allowed-hosts"), "should include --allowed-hosts");
assert.ok(args.includes("npm"), "should include npm host");
assert.ok(args.includes("myprivatescope"), "should include additional host");
});
test("buildLockfileLintArgs: omits --allowed-hosts when array is empty", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: false,
validateIntegrity: false,
allowedHosts: [],
});
assert.ok(!args.includes("--allowed-hosts"), "should not include --allowed-hosts when empty");
});
test("buildLockfileLintArgs: full config produces expected canonical args", () => {
const cfg = getLockfileLintConfig();
const args = buildLockfileLintArgs(cfg);
// Must include all four enforcement flags
assert.ok(args.includes("--validate-https"), "must enforce HTTPS");
assert.ok(args.includes("--validate-integrity"), "must enforce integrity");
assert.ok(args.includes("--allowed-hosts"), "must restrict hosts");
assert.ok(args.includes("npm"), "npm must be an allowed host");
});
test("buildLockfileLintArgs: --allowed-hosts values follow immediately after the flag", () => {
const args = buildLockfileLintArgs({
lockfilePath: "/tmp/package-lock.json",
type: "npm",
validateHttps: false,
validateIntegrity: false,
allowedHosts: ["npm", "verdaccio"],
});
const hostIdx = args.indexOf("--allowed-hosts");
assert.ok(hostIdx !== -1, "--allowed-hosts should be present");
assert.equal(args[hostIdx + 1], "npm");
assert.equal(args[hostIdx + 2], "verdaccio");
});
// ---------------------------------------------------------------------------
// workspace dependency consistency
// ---------------------------------------------------------------------------
test("getWorkspaceDependencyCheckCommand: checks every workspace at direct depth", () => {
const command = getWorkspaceDependencyCheckCommand("linux");
assert.deepEqual(command.args, ["ls", "--workspaces", "--depth=0", "--package-lock-only"]);
});
test("getWorkspaceDependencyCheckCommand: invokes npm directly outside Windows", () => {
const command = getWorkspaceDependencyCheckCommand("linux");
assert.equal(command.command, "npm");
});
test("getWorkspaceDependencyCheckCommand: invokes npm.cmd through cmd.exe on Windows", () => {
const command = getWorkspaceDependencyCheckCommand("win32", "C:\\Windows\\System32\\cmd.exe");
assert.equal(command.command, "C:\\Windows\\System32\\cmd.exe");
assert.deepEqual(command.args, [
"/d",
"/s",
"/c",
"npm.cmd",
"ls",
"--workspaces",
"--depth=0",
"--package-lock-only",
]);
});
test("runWorkspaceDependencyCheck: executes the selected command and returns success", () => {
const calls: unknown[][] = [];
const result = runWorkspaceDependencyCheck({
platform: "linux",
execFile: (...args: unknown[]) => {
calls.push(args);
return "tree is valid";
},
});
assert.deepEqual(result, { ok: true });
assert.equal(calls.length, 1);
assert.equal(calls[0]?.[0], "npm");
assert.deepEqual(calls[0]?.[1], ["ls", "--workspaces", "--depth=0", "--package-lock-only"]);
});
test("runWorkspaceDependencyCheck: reports npm ls failures without masking diagnostics", () => {
const failure = Object.assign(new Error("ELSPROBLEMS"), {
stdout: "invalid playwright",
stderr: "npm error code ELSPROBLEMS",
});
const result = runWorkspaceDependencyCheck({
platform: "linux",
execFile: () => {
throw failure;
},
});
assert.deepEqual(result, {
ok: false,
stdout: "invalid playwright",
stderr: "npm error code ELSPROBLEMS",
});
});
test("workspace check validates lock entries independently of node_modules", (t) => {
const root = mkdtempSync(path.join(os.tmpdir(), "omniroute-lockfile-check-"));
t.after(() => rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }));
mkdirSync(path.join(root, "packages", "example"), { recursive: true });
writeFileSync(
path.join(root, "package.json"),
JSON.stringify({ name: "root", version: "1.0.0", private: true, workspaces: ["packages/*"] })
);
writeFileSync(
path.join(root, "packages", "example", "package.json"),
JSON.stringify({ name: "example", version: "1.0.0", dependencies: { semver: "7.7.4" } })
);
const lock = {
name: "root",
version: "1.0.0",
lockfileVersion: 3,
requires: true,
packages: {
"": { name: "root", version: "1.0.0", workspaces: ["packages/*"] },
"node_modules/example": { resolved: "packages/example", link: true },
"node_modules/semver": { version: "7.6.0" },
"packages/example": {
name: "example",
version: "1.0.0",
dependencies: { semver: "7.7.4" },
},
},
};
const lockPath = path.join(root, "package-lock.json");
writeFileSync(lockPath, JSON.stringify(lock));
const command = getWorkspaceDependencyCheckCommand(process.platform, process.env.ComSpec);
assert.throws(
() =>
execFileSync(command.command, command.args, {
cwd: root,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
}),
(error: unknown) => {
const diagnostics = `${String((error as { stdout?: string }).stdout ?? "")}\n${String(
(error as { stderr?: string }).stderr ?? ""
)}`;
return /ELSPROBLEMS|invalid/i.test(diagnostics);
}
);
lock.packages["node_modules/semver"].version = "7.7.4";
writeFileSync(lockPath, JSON.stringify(lock));
mkdirSync(path.join(root, "node_modules", "semver"), { recursive: true });
writeFileSync(
path.join(root, "node_modules", "semver", "package.json"),
JSON.stringify({ name: "semver", version: "7.6.0" })
);
assert.doesNotThrow(() =>
execFileSync(command.command, command.args, {
cwd: root,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
})
);
});