Files
tech-log-frontend/tests/unit/task3-selective-integration.test.ts
T
DongHyeonkaandClaude Opus 5 bdee07a93b chore: sync the frontend template from a0fbafb to 5434760
Carries eight template commits: the provider sandbox actually running, release
admission to a named environment, the product feature manifest with its runtime
kill switch, architecture and documentation rules that match what is enforced,
the removability fixtures, and the browser, visual and performance evidence.

Product identity is unchanged. `package.json` keeps `tech-log-frontend` and the
catalog keeps the Tech Log naming; the home page was not in the delta. The
visual baselines are this product's own — the template's were excluded from the
transplant and these were regenerated here, where the only difference is the
platform overview's new product-feature section.

What this repository gains operationally: `config/runtime/{local,development,
staging,production}.json` with `FE-GATE-027` refusing an artifact whose runtime
document does not match the environment it is being admitted to, and
`FEATURE_OVERRIDES` for taking an installed feature out of service without a
rebuild.

Verified here: eight gates green, build green, visual 5/5, and 1,858 of 1,859
tests in the suites that do not need a sandbox — the one failure passes in
isolation and is a jsdom lazy-chunk timeout under parallel load. The provider
suites cannot run on this machine at all: `kernel.apparmor_restrict_unprivileged
_userns=1` makes `bwrap --unshare-net` fail, reproducible without any code from
either repository.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 21:34:19 +09:00

250 lines
9.7 KiB
TypeScript

import { spawn } from "node:child_process";
import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
isReducedCiContractRun,
loadCiGateContract,
parseCiGateContract,
} from "../../scripts/contracts/ci-gates.ts";
import { generateCiWorkflow } from "../../scripts/generate-ci-workflow.ts";
import { validatePackageScriptGraph } from "../../scripts/lib/package-script-graph.ts";
const roots: string[] = [];
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
describe("selective Task 3 contract closure", () => {
it("builds a private offline aggregate-cgroup provider launch without argv secrets", async () => {
const {
encodeProviderBwrapInput,
encodeProviderScopeFrame,
formatProviderCgroupUnitName,
systemctlKillProviderArguments,
systemdRunProviderArguments,
} = await import("../../scripts/lib/provider-cgroup.ts");
const unit = formatProviderCgroupUnitName(
"vulnerability",
42,
"0123456789abcdef01234567",
);
const command = "node provider.mjs --token command-secret";
const credential = "credential-secret";
const launch = systemdRunProviderArguments(
unit,
1_800_000,
1_200,
"/trusted/node",
"/workspace/scripts/lib/provider-scope-wrapper.ts",
"/exact/report.json",
12,
34,
);
expect(launch).toEqual(expect.arrayContaining([
"--scope",
"--property=MemoryMax=1073741824",
"--property=MemorySwapMax=0",
"--property=TasksMax=64",
"--property=CPUQuota=100%",
"--property=KillMode=control-group",
"/trusted/node",
"/workspace/scripts/lib/provider-scope-wrapper.ts",
]));
expect(launch.join("\0")).not.toContain(command);
expect(launch.join("\0")).not.toContain(credential);
expect(
encodeProviderBwrapInput(
["--unshare-net", "--bind", "/exact/report.json", "/exact/report.json"],
{ PROVIDER_COMMAND: command, PROVIDER_CREDENTIAL: credential },
),
).toEqual(expect.any(Buffer));
expect(systemctlKillProviderArguments(unit)).toEqual([
"--user",
"kill",
"--kill-whom=all",
"--signal=SIGKILL",
unit,
]);
// `bwrap --args FD` stops parsing at the first non-option and never hands
// the remainder back, so a command placed in the args file is dropped and
// bubblewrap exits with its usage text. Refusing `--` in the option stream
// is what keeps that silent no-sandbox launch from returning.
expect(() =>
encodeProviderBwrapInput(
["--unshare-net", "--", "/usr/bin/prlimit"],
{ PROVIDER_COMMAND: command },
),
).toThrow(/terminate the option stream/u);
const frame = encodeProviderScopeFrame({
bwrapInput: Buffer.from("private-bwrap-vector\0"),
bwrapCommand: ["/usr/bin/prlimit", "--nofile=64:64", "--", "/bin/sh", "-eu", "-c", 'exec /bin/sh -eu -c "$PROVIDER_COMMAND"'],
reportPath: "/exact/report.json",
reportDev: 12,
reportIno: 34,
});
expect(frame.readUInt32BE(0)).toBe(frame.byteLength - 4);
expect(frame.subarray(4).toString("utf8")).toContain(
Buffer.from("private-bwrap-vector\0").toString("base64"),
);
expect(launch.join("\0")).not.toContain("private-bwrap-vector");
// The command vector rides on real argv, so it must never be able to carry
// the secret that the args file exists to hide.
expect(frame.subarray(4).toString("utf8")).not.toContain(credential);
expect(() =>
encodeProviderScopeFrame({
bwrapInput: Buffer.from("x\0"),
bwrapCommand: [],
reportPath: "/exact/report.json",
reportDev: 12,
reportIno: 34,
}),
).toThrow(/bwrap command is invalid/u);
expect(() =>
encodeProviderScopeFrame({
bwrapInput: Buffer.from("x\0"),
bwrapCommand: ["prlimit"],
reportPath: "/exact/report.json",
reportDev: 12,
reportIno: 34,
}),
).toThrow(/bwrap command is invalid/u);
});
it("removes only the pinned raw inode during parent-loss cleanup", async () => {
const { cleanupOwnedProviderReport } = await import(
"../../scripts/lib/provider-raw-cleanup.ts"
);
const root = await mkdtemp(path.join(tmpdir(), "provider-raw-cleanup-"));
roots.push(root);
const reportPath = path.join(root, "raw.json");
const originalPath = path.join(root, "original.json");
await writeFile(reportPath, "owned\n");
const identity = await lstat(reportPath);
await rename(reportPath, originalPath);
await writeFile(reportPath, "unrelated\n");
await expect(cleanupOwnedProviderReport({
reportPath,
reportDev: identity.dev,
reportIno: identity.ino,
})).resolves.toBe(false);
await expect(readFile(reportPath, "utf8")).resolves.toBe("unrelated\n");
await rm(reportPath);
await rename(originalPath, reportPath);
await expect(cleanupOwnedProviderReport({
reportPath,
reportDev: identity.dev,
reportIno: identity.ino,
})).resolves.toBe(true);
await expect(lstat(reportPath)).rejects.toMatchObject({ code: "ENOENT" });
await expect(readdir(root)).resolves.toEqual([]);
});
it("uses early liveness EOF to clean the pinned raw file without waiting for a command frame", async () => {
const root = await mkdtemp(path.join(tmpdir(), "provider-scope-eof-"));
roots.push(root);
const reportPath = path.join(root, "raw.json");
await writeFile(reportPath, "partial\n");
const identity = await lstat(reportPath);
const child = spawn(process.execPath, [
path.resolve("scripts/lib/provider-scope-wrapper.ts"),
"1",
reportPath,
String(identity.dev),
String(identity.ino),
], { stdio: ["pipe", "pipe", "pipe"] });
const completion = waitForChildResult(child);
await new Promise<void>((resolve) => setTimeout(resolve, 75));
expect(child.exitCode).toBeNull();
await expect(readFile(reportPath, "utf8")).resolves.toBe("partial\n");
child.stdin.end();
const result = await within(completion, 1_000, "provider scope EOF close");
expect(result).toEqual({ code: 125, signal: null });
await expect(lstat(reportPath)).rejects.toMatchObject({ code: "ENOENT" });
await expect(readdir(root)).resolves.toEqual([]);
});
it("tracks npm run-script dependencies instead of bypassing the graph", () => {
expect(validatePackageScriptGraph({ root: "npm run-script missing" }, "root"))
.toContain("package script missing: root -> missing");
});
// A removal fixture runs against a pruned contract on purpose, so the
// canonical counts do not describe it. Asserting them there failed the
// fixture for the reduction it exists to demonstrate.
it.skipIf(isReducedCiContractRun())("accepts only the canonical exact-count authority and rejects orphan retention", async () => {
const canonical = await loadCiGateContract(process.cwd());
expect(canonical.gates).toHaveLength(27);
expect(canonical.commands).toHaveLength(82);
expect(canonical.gates.reduce((sum, gate) => sum + gate.commandIds.length, 0)).toBe(94);
expect(canonical.artifacts).toHaveLength(107);
expect(canonical.stages).toHaveLength(5);
expect(canonical.retention.classes).toHaveLength(5);
const orphan = JSON.parse(JSON.stringify(canonical)) as Record<string, any>;
orphan.retention.classes.push({ id: "unused", policy: "never referenced" });
expect(() => parseCiGateContract(orphan)).toThrow(/five canonical retention|orphan retention/u);
});
it.skipIf(isReducedCiContractRun())("rejects the retired validate-candidate-archive grammar", async () => {
const canonical = JSON.parse(
JSON.stringify(await loadCiGateContract(process.cwd())),
) as Record<string, any>;
canonical.jobs.find((job: Record<string, any>) => job.id === "vulnerability_provider")
.steps.splice(4, 0, {
kind: "validate-candidate-archive",
archivePath: ".release/candidate/release-candidate.tar.gz",
});
expect(() => parseCiGateContract(canonical)).toThrow(
/invalid discriminator|forbidden|canonical job step sequence/iu,
);
});
it("publishes a generated workflow as exactly 0644 under a restrictive umask", async () => {
const root = await mkdtemp(path.join(tmpdir(), "ci-workflow-mode-"));
roots.push(root);
await mkdir(path.join(root, ".gitea/workflows"), { recursive: true });
const previous = process.umask(0o777);
try {
const contract = await loadCiGateContract(process.cwd());
await generateCiWorkflow({ root, contract, check: false });
} finally {
process.umask(previous);
}
const target = path.join(root, ".gitea/workflows/quality-gates.yml");
const metadata = await lstat(target);
expect(metadata.mode & 0o777).toBe(0o644);
expect((await readFile(target, "utf8")).startsWith("# GENERATED FILE")).toBe(true);
});
});
async function waitForChildResult(
child: ReturnType<typeof spawn>,
): Promise<Readonly<{ code: number | null; signal: NodeJS.Signals | null }>> {
return await new Promise((resolve, reject) => {
child.once("error", reject);
child.once("close", (code, signal) => resolve({ code, signal }));
});
}
async function within<T>(operation: Promise<T>, timeoutMs: number, label: string): Promise<T> {
let timer: NodeJS.Timeout | undefined;
try {
return await Promise.race([
operation,
new Promise<never>((_resolve, reject) => {
timer = setTimeout(() => reject(new Error(`${label} timed out`)), timeoutMs);
}),
]);
} finally {
if (timer) clearTimeout(timer);
}
}