fix: harden repository coverage evidence
This commit is contained in:
+18
-18
@@ -1,50 +1,50 @@
|
||||
{
|
||||
"total": {
|
||||
"lines": { "pct": 100 },
|
||||
"statements": { "pct": 100 },
|
||||
"functions": { "pct": 100 },
|
||||
"branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 },
|
||||
"statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 },
|
||||
"functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 },
|
||||
"branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/http/bounded-body-reader.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/http/bounded-json.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/http/http-execution-v3.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/http/request-builder.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/http/retry-policy.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/query-cache/server-state-scope-runtime.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/service-worker/service-worker-lifecycle.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/storage/browser-storage-adapter.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/adapters/telemetry/best-effort-telemetry.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/application/create-application.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/application/policies/compatibility.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/application/policies/performance-budgets.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/application/policies/promotion-readiness.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
},
|
||||
"src/application/use-cases/decide-chunk-recovery.ts": {
|
||||
"lines": { "pct": 100 }, "statements": { "pct": 100 }, "functions": { "pct": 100 }, "branches": { "pct": 100 }
|
||||
"lines": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "statements": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "functions": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }, "branches": { "total": 0, "covered": 0, "skipped": 0, "pct": 100 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
import { constants } from "node:fs";
|
||||
import {
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
open,
|
||||
readFile,
|
||||
readdir,
|
||||
rename,
|
||||
rm,
|
||||
symlink,
|
||||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
readRiskCoverageInput,
|
||||
resolveRiskCoverageArtifactPath,
|
||||
writeRiskCoverageArtifactAtomic,
|
||||
} from "../../scripts/lib/risk-coverage-files.ts";
|
||||
|
||||
const roots: string[] = [];
|
||||
|
||||
async function fixture(): Promise<string> {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "risk-coverage-files-"));
|
||||
roots.push(root);
|
||||
await mkdir(path.join(root, "config/testing"), { recursive: true });
|
||||
await mkdir(path.join(root, "artifacts/tests/coverage"), { recursive: true });
|
||||
await writeFile(path.join(root, "config/testing/policy.json"), "{\"policy\":true}\n");
|
||||
await writeFile(path.join(root, "artifacts/tests/coverage/summary.json"), "{\"total\":{}}\n");
|
||||
return root;
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
roots.splice(0).map((root) => rm(root, { recursive: true, force: true })),
|
||||
);
|
||||
});
|
||||
|
||||
describe("risk coverage CLI files", () => {
|
||||
it("reads only exact contained regular input files", async () => {
|
||||
const repositoryRoot = await fixture();
|
||||
await expect(
|
||||
readRiskCoverageInput({
|
||||
repositoryRoot,
|
||||
relativePath: "config/testing/policy.json",
|
||||
label: "policy",
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
relativePath: "config/testing/policy.json",
|
||||
text: "{\"policy\":true}\n",
|
||||
});
|
||||
await expect(
|
||||
readRiskCoverageInput({
|
||||
repositoryRoot,
|
||||
relativePath: path.join(repositoryRoot, "config/testing/policy.json"),
|
||||
label: "policy",
|
||||
}),
|
||||
).rejects.toThrow(/repository-relative POSIX/u);
|
||||
await expect(
|
||||
readRiskCoverageInput({
|
||||
repositoryRoot,
|
||||
relativePath: "config\\testing\\policy.json",
|
||||
label: "policy",
|
||||
}),
|
||||
).rejects.toThrow(/repository-relative POSIX/u);
|
||||
});
|
||||
|
||||
it("rejects final and ancestor input symlinks", async () => {
|
||||
const repositoryRoot = await fixture();
|
||||
const outside = await mkdtemp(path.join(tmpdir(), "risk-coverage-input-outside-"));
|
||||
roots.push(outside);
|
||||
await writeFile(path.join(outside, "outside.json"), "{}\n");
|
||||
await symlink(
|
||||
path.join(outside, "outside.json"),
|
||||
path.join(repositoryRoot, "config/testing/link.json"),
|
||||
);
|
||||
await symlink(outside, path.join(repositoryRoot, "linked-config"), "dir");
|
||||
|
||||
await expect(
|
||||
readRiskCoverageInput({
|
||||
repositoryRoot,
|
||||
relativePath: "config/testing/link.json",
|
||||
label: "policy",
|
||||
}),
|
||||
).rejects.toThrow(/symlink/u);
|
||||
await expect(
|
||||
readRiskCoverageInput({
|
||||
repositoryRoot,
|
||||
relativePath: "linked-config/outside.json",
|
||||
label: "policy",
|
||||
}),
|
||||
).rejects.toThrow(/outside repository|symlink/u);
|
||||
});
|
||||
|
||||
it("confines artifact output and rejects input overwrite or symlink ancestors", async () => {
|
||||
const repositoryRoot = await fixture();
|
||||
await expect(
|
||||
resolveRiskCoverageArtifactPath({
|
||||
repositoryRoot,
|
||||
relativePath: "artifacts/quality/risk-coverage.json",
|
||||
inputPaths: ["config/testing/policy.json", "artifacts/tests/coverage/summary.json"],
|
||||
}),
|
||||
).resolves.toBe(path.join(repositoryRoot, "artifacts/quality/risk-coverage.json"));
|
||||
await expect(
|
||||
resolveRiskCoverageArtifactPath({
|
||||
repositoryRoot,
|
||||
relativePath: "config/testing/result.json",
|
||||
inputPaths: [],
|
||||
}),
|
||||
).rejects.toThrow(/artifacts\/quality/u);
|
||||
await expect(
|
||||
resolveRiskCoverageArtifactPath({
|
||||
repositoryRoot,
|
||||
relativePath: "artifacts/quality/risk-coverage.json",
|
||||
inputPaths: ["artifacts/quality/risk-coverage.json"],
|
||||
}),
|
||||
).rejects.toThrow(/must not overwrite an input/u);
|
||||
|
||||
const outside = await mkdtemp(path.join(tmpdir(), "risk-coverage-output-outside-"));
|
||||
roots.push(outside);
|
||||
await rm(path.join(repositoryRoot, "artifacts/quality"), { recursive: true, force: true });
|
||||
await symlink(outside, path.join(repositoryRoot, "artifacts/quality"), "dir");
|
||||
await expect(
|
||||
resolveRiskCoverageArtifactPath({
|
||||
repositoryRoot,
|
||||
relativePath: "artifacts/quality/risk-coverage.json",
|
||||
inputPaths: [],
|
||||
}),
|
||||
).rejects.toThrow(/symlink/u);
|
||||
});
|
||||
|
||||
it("syncs an exclusive sibling temp before atomic rename", async () => {
|
||||
const repositoryRoot = await fixture();
|
||||
const observed: string[] = [];
|
||||
let observedFlags = 0;
|
||||
await writeRiskCoverageArtifactAtomic(
|
||||
{
|
||||
repositoryRoot,
|
||||
relativePath: "artifacts/quality/risk-coverage.json",
|
||||
inputPaths: ["config/testing/policy.json", "artifacts/tests/coverage/summary.json"],
|
||||
value: { schemaVersion: 2, status: "PASS" },
|
||||
},
|
||||
{
|
||||
createNonce: () => "owned",
|
||||
fileSystem: {
|
||||
openFile: async (target, flags, mode) => {
|
||||
observedFlags = flags;
|
||||
const handle = await open(target, flags, mode);
|
||||
return {
|
||||
writeFile: async (data) => handle.writeFile(data, "utf8"),
|
||||
sync: async () => {
|
||||
observed.push("file-sync");
|
||||
await handle.sync();
|
||||
},
|
||||
close: async () => handle.close(),
|
||||
};
|
||||
},
|
||||
openDirectory: async (target) => {
|
||||
const handle = await open(target, constants.O_RDONLY);
|
||||
return {
|
||||
sync: async () => {
|
||||
observed.push("directory-sync");
|
||||
await handle.sync();
|
||||
},
|
||||
close: async () => handle.close(),
|
||||
};
|
||||
},
|
||||
rename: async (source, destination) => {
|
||||
observed.push("rename");
|
||||
await rename(source, destination);
|
||||
},
|
||||
rm,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(observedFlags & constants.O_EXCL).toBe(constants.O_EXCL);
|
||||
expect(observedFlags & constants.O_NOFOLLOW).toBe(constants.O_NOFOLLOW);
|
||||
expect(observed).toEqual(["file-sync", "rename", "directory-sync"]);
|
||||
expect(
|
||||
JSON.parse(
|
||||
await readFile(
|
||||
path.join(repositoryRoot, "artifacts/quality/risk-coverage.json"),
|
||||
"utf8",
|
||||
),
|
||||
),
|
||||
).toEqual({ schemaVersion: 2, status: "PASS" });
|
||||
});
|
||||
|
||||
it("cleans its owned temp and preserves destination when publication fails", async () => {
|
||||
const repositoryRoot = await fixture();
|
||||
const outputDirectory = path.join(repositoryRoot, "artifacts/quality");
|
||||
await mkdir(outputDirectory, { recursive: true });
|
||||
const destination = path.join(outputDirectory, "risk-coverage.json");
|
||||
await writeFile(destination, "previous\n");
|
||||
|
||||
await expect(
|
||||
writeRiskCoverageArtifactAtomic(
|
||||
{
|
||||
repositoryRoot,
|
||||
relativePath: "artifacts/quality/risk-coverage.json",
|
||||
inputPaths: [],
|
||||
value: { schemaVersion: 2 },
|
||||
},
|
||||
{
|
||||
createNonce: () => "owned",
|
||||
fileSystem: {
|
||||
openFile: async (target, flags, mode) => {
|
||||
const handle = await open(target, flags, mode);
|
||||
return {
|
||||
writeFile: async (data) => handle.writeFile(data, "utf8"),
|
||||
sync: async () => handle.sync(),
|
||||
close: async () => handle.close(),
|
||||
};
|
||||
},
|
||||
openDirectory: async (target) => {
|
||||
const handle = await open(target, constants.O_RDONLY);
|
||||
return { sync: async () => handle.sync(), close: async () => handle.close() };
|
||||
},
|
||||
rename: async () => {
|
||||
throw new Error("injected rename failure");
|
||||
},
|
||||
rm,
|
||||
},
|
||||
},
|
||||
),
|
||||
).rejects.toThrow(/injected rename failure/u);
|
||||
|
||||
await expect(readFile(destination, "utf8")).resolves.toBe("previous\n");
|
||||
expect(await readdir(outputDirectory)).toEqual(["risk-coverage.json"]);
|
||||
});
|
||||
|
||||
it("has no changed-files gate in the executable", async () => {
|
||||
const source = await readFile("scripts/check-risk-coverage.ts", "utf8");
|
||||
expect(source).not.toMatch(/changedFiles|changed-files/u);
|
||||
});
|
||||
});
|
||||
+336
-244
@@ -1,6 +1,8 @@
|
||||
import { constants } from "node:fs";
|
||||
import {
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
open,
|
||||
readFile,
|
||||
rm,
|
||||
symlink,
|
||||
@@ -14,18 +16,45 @@ import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildProductionModuleInventory,
|
||||
evaluateRiskCoverage,
|
||||
normalizeCoverageProducerPath,
|
||||
parseRepositoryRiskCoveragePolicy,
|
||||
parseRiskCoveragePolicy,
|
||||
type ProductionModuleInventory,
|
||||
} from "../../scripts/lib/risk-coverage.ts";
|
||||
|
||||
const roots: string[] = [];
|
||||
const now = Date.parse("2026-08-02T00:00:00.000Z");
|
||||
const fullMetrics = {
|
||||
lines: { pct: 100 },
|
||||
statements: { pct: 100 },
|
||||
functions: { pct: 100 },
|
||||
branches: { pct: 100 },
|
||||
};
|
||||
|
||||
function counter(total = 1, covered = total, skipped = 0) {
|
||||
return {
|
||||
total,
|
||||
covered,
|
||||
skipped,
|
||||
pct: total === 0 ? 100 : Math.floor((covered / total) * 10_000) / 100,
|
||||
};
|
||||
}
|
||||
|
||||
function metrics(total = 1, covered = total) {
|
||||
return {
|
||||
lines: counter(total, covered),
|
||||
statements: counter(total, covered),
|
||||
functions: counter(total, covered),
|
||||
branches: counter(total, covered),
|
||||
};
|
||||
}
|
||||
|
||||
const fullMetrics = metrics();
|
||||
|
||||
function inventory(
|
||||
files: readonly string[],
|
||||
generatedExclusions: readonly string[] = [],
|
||||
): ProductionModuleInventory {
|
||||
return {
|
||||
files,
|
||||
preExclusionTotal: files.length + generatedExclusions.length,
|
||||
generatedExclusions,
|
||||
};
|
||||
}
|
||||
|
||||
async function repositoryFixture(): Promise<string> {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "risk-coverage-"));
|
||||
@@ -87,17 +116,15 @@ describe("repository-aware risk coverage", () => {
|
||||
await readFile("tests/fixtures/coverage/repository-omission.json", "utf8"),
|
||||
) as unknown;
|
||||
const parsedPolicy = parseRepositoryRiskCoveragePolicy(rawPolicy, { now });
|
||||
const inventory = await buildProductionModuleInventory({
|
||||
const productionInventory = await buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
generatedPaths: parsedPolicy.generatedPaths,
|
||||
});
|
||||
const result = evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
inventory: productionInventory,
|
||||
policy: parsedPolicy,
|
||||
summary,
|
||||
changedFiles: [],
|
||||
now,
|
||||
});
|
||||
|
||||
expect(result.selectedTotal).toBe(14);
|
||||
@@ -108,342 +135,407 @@ describe("repository-aware risk coverage", () => {
|
||||
expect(result.status).toBe("FAIL");
|
||||
});
|
||||
|
||||
it("reports the exact selected and repository totals plus sorted omissions", async () => {
|
||||
it("reports exact inventory and generated-exclusion provenance", async () => {
|
||||
const repositoryRoot = await repositoryFixture();
|
||||
const inventory = await buildProductionModuleInventory({
|
||||
const productionInventory = await buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
generatedPaths: ["src/generated.ts"],
|
||||
});
|
||||
const result = evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
inventory: productionInventory,
|
||||
policy: parseRiskCoveragePolicy(policy(), { now }),
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
[path.join(repositoryRoot, "src/a.ts")]: fullMetrics,
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
});
|
||||
|
||||
expect(inventory).toEqual(["src/a.ts", "src/nested/b.tsx"]);
|
||||
expect(productionInventory).toEqual({
|
||||
files: ["src/a.ts", "src/nested/b.tsx"],
|
||||
preExclusionTotal: 3,
|
||||
generatedExclusions: ["src/generated.ts"],
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
status: "FAIL",
|
||||
selectedTotal: 1,
|
||||
repositoryTotal: 2,
|
||||
preExclusionTotal: 3,
|
||||
generatedExclusionCount: 1,
|
||||
generatedExclusions: ["src/generated.ts"],
|
||||
uncoveredModules: ["src/nested/b.tsx"],
|
||||
});
|
||||
expect(result.failures).toContain(
|
||||
"production module missing from coverage: src/nested/b.tsx",
|
||||
});
|
||||
|
||||
it("rejects arbitrary coverage paths instead of silently allowing inflation", () => {
|
||||
const parsedPolicy = parseRiskCoveragePolicy(
|
||||
policy({ repositoryBaseline: 1, generatedPaths: [] }),
|
||||
{ now },
|
||||
);
|
||||
});
|
||||
|
||||
it("maps only exact repository-relative POSIX coverage paths", async () => {
|
||||
const repositoryRoot = await repositoryFixture();
|
||||
const inventory = await buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
generatedPaths: ["src/generated.ts"],
|
||||
});
|
||||
const parsedPolicy = parseRiskCoveragePolicy(policy(), { now });
|
||||
|
||||
expect(
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
repositoryRoot: "/repository",
|
||||
inventory: inventory(["src/a.ts"]),
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
total: metrics(2),
|
||||
"src/a.ts": fullMetrics,
|
||||
"src/nested/b.tsx": fullMetrics,
|
||||
"tests/unit/a.test.ts": fullMetrics,
|
||||
"src/generated.ts": fullMetrics,
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toMatchObject({
|
||||
status: "PASS",
|
||||
selectedTotal: 2,
|
||||
ignoredCoveragePaths: ["src/generated.ts", "tests/unit/a.test.ts"],
|
||||
});
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
[path.join(tmpdir(), "other/src/a.ts")]: fullMetrics,
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toThrow(/outside repository/u);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": fullMetrics,
|
||||
[path.join(repositoryRoot, "src/a.ts")]: fullMetrics,
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toThrow(/duplicate coverage path/u);
|
||||
).toThrow(/unexpected coverage path.*tests\/unit\/a\.test\.ts/u);
|
||||
});
|
||||
|
||||
it("accepts and validates Vitest's branchesTrue total without evaluating it", () => {
|
||||
const parsedPolicy = parseRiskCoveragePolicy(policy(), { now });
|
||||
const branchesTrue = { total: 0, covered: 0, skipped: 0, pct: 100 };
|
||||
it("accepts only explicitly configured generated coverage paths", () => {
|
||||
const parsedPolicy = parseRiskCoveragePolicy(
|
||||
policy({ repositoryBaseline: 1 }),
|
||||
{ now },
|
||||
);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: inventory(["src/a.ts"], ["src/generated.ts"]),
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: metrics(2, 1),
|
||||
"src/a.ts": fullMetrics,
|
||||
"src/generated.ts": metrics(1, 0),
|
||||
},
|
||||
}),
|
||||
).not.toThrow();
|
||||
const result = evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: inventory(["src/a.ts"], ["src/generated.ts"]),
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: metrics(2, 1),
|
||||
"src/a.ts": fullMetrics,
|
||||
"src/generated.ts": metrics(1, 0),
|
||||
},
|
||||
});
|
||||
expect(
|
||||
result.results.find(
|
||||
({ scope, metric }) => scope === "total" && metric === "lines",
|
||||
),
|
||||
).toMatchObject({ received: 100, passed: true });
|
||||
});
|
||||
|
||||
it("requires complete internally consistent Istanbul counters", () => {
|
||||
const parsedPolicy = parseRiskCoveragePolicy(
|
||||
policy({ repositoryBaseline: 1, generatedPaths: [] }),
|
||||
{ now },
|
||||
);
|
||||
const base = {
|
||||
repositoryRoot: "/repository",
|
||||
inventory: inventory(["src/a.ts"]),
|
||||
policy: parsedPolicy,
|
||||
};
|
||||
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
...base,
|
||||
summary: { total: fullMetrics, "src/a.ts": { ...fullMetrics, lines: { pct: 100 } } },
|
||||
}),
|
||||
).toThrow(/lines.*total.*covered.*skipped/u);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
...base,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": {
|
||||
...fullMetrics,
|
||||
lines: { total: 2, covered: 1, skipped: 2, pct: 50 },
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toThrow(/covered plus skipped.*total/u);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
...base,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": {
|
||||
...fullMetrics,
|
||||
lines: { total: 3, covered: 2, skipped: 0, pct: 66.67 },
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toThrow(/pct.*66\.66/u);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
...base,
|
||||
summary: {
|
||||
total: metrics(0),
|
||||
"src/a.ts": { ...fullMetrics, lines: counter(0, 0, 0) },
|
||||
},
|
||||
}),
|
||||
).toThrow(/coverage total.*does not match/u);
|
||||
});
|
||||
|
||||
it("recomputes all global counters and requires an exact producer total", () => {
|
||||
const parsedPolicy = parseRiskCoveragePolicy(
|
||||
policy({ repositoryBaseline: 1, generatedPaths: [] }),
|
||||
{ now },
|
||||
);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: inventory(["src/a.ts"]),
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: metrics(2),
|
||||
"src/a.ts": fullMetrics,
|
||||
},
|
||||
}),
|
||||
).toThrow(/coverage total\.lines does not match recomputed inventory total/u);
|
||||
});
|
||||
|
||||
it("accepts and validates Vitest branchesTrue without evaluating it", () => {
|
||||
const parsedPolicy = parseRiskCoveragePolicy(
|
||||
policy({ repositoryBaseline: 1, generatedPaths: [] }),
|
||||
{ now },
|
||||
);
|
||||
const branchesTrue = counter(0);
|
||||
expect(
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: ["src/a.ts"],
|
||||
policy: { ...parsedPolicy, repositoryBaseline: 1 },
|
||||
inventory: inventory(["src/a.ts"]),
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: { ...fullMetrics, branchesTrue },
|
||||
"src/a.ts": fullMetrics,
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toMatchObject({ status: "PASS", selectedTotal: 1 });
|
||||
).toMatchObject({ status: "PASS" });
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: ["src/a.ts"],
|
||||
policy: { ...parsedPolicy, repositoryBaseline: 1 },
|
||||
inventory: inventory(["src/a.ts"]),
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: {
|
||||
...fullMetrics,
|
||||
branchesTrue: { ...branchesTrue, pct: Number.NaN },
|
||||
branchesTrue: { ...branchesTrue, pct: 0 },
|
||||
},
|
||||
"src/a.ts": fullMetrics,
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toThrow(/branchesTrue.*finite/u);
|
||||
).toThrow(/branchesTrue\.pct.*100/u);
|
||||
});
|
||||
|
||||
it("fails closed on an empty, unreadable, traversing, or symlinked inventory", async () => {
|
||||
it("opens regular files with O_NOFOLLOW and fails a deterministic symlink swap", async () => {
|
||||
const repositoryRoot = await repositoryFixture();
|
||||
let observedFlags = 0;
|
||||
await expect(
|
||||
buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
openFile: async (target, flags) => {
|
||||
observedFlags = flags;
|
||||
if (target.endsWith("src/a.ts")) {
|
||||
throw Object.assign(new Error("injected link swap"), { code: "ELOOP" });
|
||||
}
|
||||
return open(target, flags);
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/unreadable.*src\/a\.ts/u);
|
||||
expect(observedFlags & constants.O_NOFOLLOW).toBe(constants.O_NOFOLLOW);
|
||||
});
|
||||
|
||||
it("fails closed on empty, traversing, symlinked, or stale generated inventory", async () => {
|
||||
const repositoryRoot = await repositoryFixture();
|
||||
const emptyRoot = await mkdtemp(path.join(tmpdir(), "risk-coverage-empty-"));
|
||||
roots.push(emptyRoot);
|
||||
await mkdir(path.join(emptyRoot, "src"));
|
||||
|
||||
await expect(buildProductionModuleInventory({ repositoryRoot: emptyRoot })).rejects.toThrow(
|
||||
/inventory is empty/u,
|
||||
);
|
||||
await expect(
|
||||
buildProductionModuleInventory({ repositoryRoot: emptyRoot }),
|
||||
).rejects.toThrow(/inventory is empty/u);
|
||||
await expect(
|
||||
buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
assertReadable: async (target) => {
|
||||
if (target.endsWith("src/a.ts")) throw new Error("denied");
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/unreadable.*src\/a\.ts/u);
|
||||
await expect(
|
||||
buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
generatedPaths: ["../escape.ts"],
|
||||
}),
|
||||
buildProductionModuleInventory({ repositoryRoot, generatedPaths: ["../escape.ts"] }),
|
||||
).rejects.toThrow(/repository-relative POSIX/u);
|
||||
await expect(
|
||||
buildProductionModuleInventory({ repositoryRoot, generatedPaths: ["src/missing.ts"] }),
|
||||
).rejects.toThrow(/stale or not a production module/u);
|
||||
|
||||
const outside = await mkdtemp(path.join(tmpdir(), "risk-coverage-outside-"));
|
||||
roots.push(outside);
|
||||
await writeFile(path.join(outside, "linked.ts"), "export {};\n");
|
||||
await symlink(path.join(outside, "linked.ts"), path.join(repositoryRoot, "src/link.ts"));
|
||||
await expect(
|
||||
buildProductionModuleInventory({ repositoryRoot }),
|
||||
).rejects.toThrow(/symlink.*src\/link\.ts/u);
|
||||
await expect(buildProductionModuleInventory({ repositoryRoot })).rejects.toThrow(
|
||||
/symlink.*src\/link\.ts/u,
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects malformed totals, duplicate paths, invalid minimums, and weak ownership", () => {
|
||||
expect(() => parseRiskCoveragePolicy(null, { now })).toThrow(/policy/u);
|
||||
it("normalizes native Windows absolute producer paths but rejects POSIX backslashes", () => {
|
||||
expect(
|
||||
normalizeCoverageProducerPath({
|
||||
repositoryRoot: "C:\\repo",
|
||||
rawPath: "C:\\repo\\src\\nested\\a.ts",
|
||||
platform: "win32",
|
||||
}),
|
||||
).toBe("src/nested/a.ts");
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(policy({ repositoryBaseline: 0 }), { now }),
|
||||
).toThrow(/repositoryBaseline/u);
|
||||
normalizeCoverageProducerPath({
|
||||
repositoryRoot: "/repository",
|
||||
rawPath: "/repository/src\\a.ts",
|
||||
platform: "posix",
|
||||
}),
|
||||
).toThrow(/POSIX separators/u);
|
||||
expect(() =>
|
||||
normalizeCoverageProducerPath({
|
||||
repositoryRoot: "C:\\repo",
|
||||
rawPath: "D:\\outside\\a.ts",
|
||||
platform: "win32",
|
||||
}),
|
||||
).toThrow(/outside repository/u);
|
||||
});
|
||||
|
||||
it("requires four positive metrics and canonical team ownership", () => {
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(policy({ summary: { lines: 80 } }), { now }),
|
||||
).toThrow(/summary must define all/u);
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(policy({ summary: { lines: 80, statements: 78, functions: 85, branches: 0 } }), { now }),
|
||||
).toThrow(/greater than 0/u);
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(
|
||||
policy({
|
||||
criticalModules: [
|
||||
{ path: "src/a.ts", owner: "team", minimum: { lines: 101 } },
|
||||
{ path: "src/a.ts", owner: "Platform Runtime", minimum: policy().summary },
|
||||
],
|
||||
}),
|
||||
{ now },
|
||||
),
|
||||
).toThrow(/minimum/u);
|
||||
).toThrow(/canonical team id/u);
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(
|
||||
policy({
|
||||
criticalModules: [
|
||||
{ path: "src/a.ts", owner: " ", minimum: { lines: 80 } },
|
||||
{ path: "src/a.ts", owner: "platform-runtime", minimum: { lines: 80 } },
|
||||
],
|
||||
}),
|
||||
{ now },
|
||||
),
|
||||
).toThrow(/owner/u);
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(
|
||||
policy({ highRiskPaths: ["src/a.ts", "src/a.ts"] }),
|
||||
{ now },
|
||||
),
|
||||
).toThrow(/duplicate/u);
|
||||
|
||||
const parsedPolicy = parseRiskCoveragePolicy(policy(), { now });
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: ["src/a.ts"],
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": { ...fullMetrics, lines: { pct: Number.NaN } },
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toThrow(/finite/u);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: ["src/a.ts"],
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": {
|
||||
...fullMetrics,
|
||||
lines: {
|
||||
total: Number.NaN,
|
||||
covered: 1,
|
||||
skipped: 0,
|
||||
pct: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toThrow(/lines\.total.*nonnegative/u);
|
||||
expect(() =>
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: ["src/a.ts"],
|
||||
policy: parsedPolicy,
|
||||
summary: {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": { ...fullMetrics, conditions: { pct: 100 } },
|
||||
},
|
||||
changedFiles: [],
|
||||
now,
|
||||
}),
|
||||
).toThrow(/unknown.*metric/u);
|
||||
).toThrow(/minimum must define all/u);
|
||||
});
|
||||
|
||||
it("requires inventory-owned critical rows and exact owned future waivers", async () => {
|
||||
const repositoryRoot = await repositoryFixture();
|
||||
const inventory = await buildProductionModuleInventory({
|
||||
repositoryRoot,
|
||||
generatedPaths: ["src/generated.ts"],
|
||||
});
|
||||
const summary = {
|
||||
total: fullMetrics,
|
||||
"src/a.ts": fullMetrics,
|
||||
"src/nested/b.tsx": fullMetrics,
|
||||
};
|
||||
const waiverPolicy = parseRiskCoveragePolicy(
|
||||
it("enforces ALL_POLICY_HIGH_RISK ownership without changed-file input", () => {
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(
|
||||
policy({ highRiskPaths: ["src/a.ts", "src/nested/b.tsx"] }),
|
||||
{ now },
|
||||
),
|
||||
).toThrow(/high-risk module has no owner or waiver.*nested\/b/u);
|
||||
|
||||
const parsed = parseRiskCoveragePolicy(
|
||||
policy({
|
||||
highRiskPaths: ["src/a.ts", "src/nested/b.tsx"],
|
||||
waivers: [
|
||||
{
|
||||
path: "src/nested/b.tsx",
|
||||
owner: "runtime-security",
|
||||
reason: "Temporary branch instrumentation gap",
|
||||
reason: "Temporary instrumentation gap",
|
||||
expiresAt: "2026-08-03T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
}),
|
||||
{ now },
|
||||
);
|
||||
|
||||
expect(
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
policy: waiverPolicy,
|
||||
summary,
|
||||
changedFiles: ["src/nested/b.tsx"],
|
||||
now,
|
||||
}),
|
||||
).toMatchObject({ status: "PASS" });
|
||||
expect(
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
policy: parseRiskCoveragePolicy(
|
||||
policy({ highRiskPaths: ["src/a.ts", "src/nested/b.tsx"] }),
|
||||
{ now },
|
||||
),
|
||||
summary,
|
||||
changedFiles: ["src/nested/b.tsx"],
|
||||
now,
|
||||
}).failures,
|
||||
).toContain("changed high-risk module has no owner or waiver: src/nested/b.tsx");
|
||||
expect(
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory,
|
||||
policy: parseRiskCoveragePolicy(
|
||||
policy({ highRiskPaths: ["src/a.ts", "src/nested/b.tsx"] }),
|
||||
{ now },
|
||||
),
|
||||
summary,
|
||||
changedFiles: [],
|
||||
now,
|
||||
}).failures,
|
||||
).toContain("high-risk module has no owner or waiver: src/nested/b.tsx");
|
||||
expect(
|
||||
evaluateRiskCoverage({
|
||||
repositoryRoot,
|
||||
inventory: ["src/nested/b.tsx"],
|
||||
policy: parseRiskCoveragePolicy(policy(), { now }),
|
||||
summary,
|
||||
changedFiles: [],
|
||||
now,
|
||||
}).failures,
|
||||
).toContain("critical module is outside production inventory: src/a.ts");
|
||||
});
|
||||
|
||||
it("does not let the repository policy delete a required high-risk path", async () => {
|
||||
const rawPolicy = JSON.parse(
|
||||
await readFile("config/testing/risk-coverage.json", "utf8"),
|
||||
) as Record<string, unknown>;
|
||||
rawPolicy.highRiskPaths = (
|
||||
rawPolicy.highRiskPaths as string[]
|
||||
).filter((modulePath) => modulePath !== "src/adapters/http/http-execution-v3.ts");
|
||||
|
||||
expect(() => parseRepositoryRiskCoveragePolicy(rawPolicy, { now })).toThrow(
|
||||
/required high-risk path.*http-execution-v3/u,
|
||||
);
|
||||
const result = evaluateRiskCoverage({
|
||||
repositoryRoot: "/repository",
|
||||
inventory: inventory(
|
||||
["src/a.ts", "src/nested/b.tsx"],
|
||||
["src/generated.ts"],
|
||||
),
|
||||
policy: parsed,
|
||||
summary: {
|
||||
total: metrics(2),
|
||||
"src/a.ts": fullMetrics,
|
||||
"src/nested/b.tsx": fullMetrics,
|
||||
},
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
ownershipScope: "ALL_POLICY_HIGH_RISK",
|
||||
ownedHighRiskPaths: ["src/a.ts"],
|
||||
waivedHighRiskPaths: ["src/nested/b.tsx"],
|
||||
status: "PASS",
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ path: "src/*.ts", owner: "team", reason: "reason", expiresAt: "2026-08-03T00:00:00.000Z" }, /POSIX/u],
|
||||
[{ path: "src/a.ts", owner: "", reason: "reason", expiresAt: "2026-08-03T00:00:00.000Z" }, /owner/u],
|
||||
[{ path: "src/a.ts", owner: "team", reason: "", expiresAt: "2026-08-03T00:00:00.000Z" }, /reason/u],
|
||||
[{ path: "src/a.ts", owner: "team", reason: "reason", expiresAt: "2026-08-01T00:00:00.000Z" }, /expired/u],
|
||||
[{ path: "src/stale.ts", owner: "team", reason: "reason", expiresAt: "2026-08-03T00:00:00.000Z" }, /stale/u],
|
||||
] as const)("rejects invalid exact-path waivers %#", (waiver, message) => {
|
||||
[
|
||||
{ path: "src/a.ts", owner: "Runtime Team", reason: "Temporary instrumentation gap", expiresAt: "2026-08-03T00:00:00.000Z" },
|
||||
/canonical team id/u,
|
||||
],
|
||||
[
|
||||
{ path: "src/a.ts", owner: "runtime-team", reason: "too short", expiresAt: "2026-08-03T00:00:00.000Z" },
|
||||
/12 to 240/u,
|
||||
],
|
||||
[
|
||||
{ path: "src/a.ts", owner: "runtime-team", reason: "Temporary\ninstrumentation gap", expiresAt: "2026-08-03T00:00:00.000Z" },
|
||||
/control characters/u,
|
||||
],
|
||||
[
|
||||
{ path: "src/a.ts", owner: "runtime-team", reason: "Temporary instrumentation gap", expiresAt: "2026-08-03T00:00:00Z" },
|
||||
/canonical UTC ISO/u,
|
||||
],
|
||||
[
|
||||
{ path: "src/a.ts", owner: "runtime-team", reason: "Temporary instrumentation gap", expiresAt: "2026-11-01T00:00:00.000Z" },
|
||||
/90 days/u,
|
||||
],
|
||||
] as const)("rejects invalid waiver controls %#", (waiver, message) => {
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(
|
||||
policy({ highRiskPaths: ["src/a.ts"], waivers: [waiver] }),
|
||||
policy({
|
||||
criticalModules: [
|
||||
{
|
||||
path: "src/owned.ts",
|
||||
owner: "platform-runtime",
|
||||
minimum: policy().summary,
|
||||
},
|
||||
],
|
||||
highRiskPaths: ["src/owned.ts", "src/a.ts"],
|
||||
waivers: [waiver],
|
||||
}),
|
||||
{ now },
|
||||
),
|
||||
).toThrow(message);
|
||||
});
|
||||
|
||||
it("rejects simultaneous critical ownership and waiver", () => {
|
||||
expect(() =>
|
||||
parseRiskCoveragePolicy(
|
||||
policy({
|
||||
waivers: [
|
||||
{
|
||||
path: "src/a.ts",
|
||||
owner: "runtime-team",
|
||||
reason: "Temporary instrumentation gap",
|
||||
expiresAt: "2026-08-03T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
}),
|
||||
{ now },
|
||||
),
|
||||
).toThrow(/both a critical owner and waiver/u);
|
||||
});
|
||||
|
||||
it("does not let repository policy omit or generate-exclude a required high-risk path", async () => {
|
||||
const rawPolicy = JSON.parse(
|
||||
await readFile("config/testing/risk-coverage.json", "utf8"),
|
||||
) as Record<string, unknown>;
|
||||
const missingPolicy = structuredClone(rawPolicy);
|
||||
missingPolicy.highRiskPaths = (missingPolicy.highRiskPaths as string[]).filter(
|
||||
(modulePath) => modulePath !== "src/adapters/http/http-execution-v3.ts",
|
||||
);
|
||||
expect(() => parseRepositoryRiskCoveragePolicy(missingPolicy, { now })).toThrow(
|
||||
/required high-risk path.*http-execution-v3/u,
|
||||
);
|
||||
|
||||
const excludedPolicy = structuredClone(rawPolicy);
|
||||
excludedPolicy.generatedPaths = ["src/adapters/http/http-execution-v3.ts"];
|
||||
expect(() => parseRepositoryRiskCoveragePolicy(excludedPolicy, { now })).toThrow(
|
||||
/required high-risk path cannot be generated-excluded/u,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user