Files
2026-07-13 13:39:12 +08:00

182 lines
6.9 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 two exported pure functions:
// - 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";
// @ts-expect-error — .mjs helper has no type declarations; runtime shape is known.
import {
getLockfileLintConfig,
buildLockfileLintArgs,
} 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");
});