refactor: align coverage counter provenance

This commit is contained in:
DongHyeonka
2026-08-02 09:31:28 +09:00
parent 6e05a35790
commit 5cecbb9820
4 changed files with 247 additions and 77 deletions
+51 -28
View File
@@ -48,16 +48,16 @@ const fullMetrics = metrics();
function inventory(
files: readonly string[],
generatedExclusions: readonly string[] = [],
nonExecutableModules: readonly string[] = [],
counterlessModules: readonly string[] = [],
): ProductionModuleInventory {
return {
files,
preExclusionTotal: files.length + generatedExclusions.length,
generatedExclusions,
executableModules: files.filter(
(file) => !nonExecutableModules.includes(file),
counterBearingModules: files.filter(
(file) => !counterlessModules.includes(file),
),
nonExecutableModules,
counterlessModules,
};
}
@@ -160,8 +160,8 @@ describe("repository-aware risk coverage", () => {
files: ["src/a.ts", "src/nested/b.tsx"],
preExclusionTotal: 3,
generatedExclusions: ["src/generated.ts"],
executableModules: ["src/a.ts", "src/nested/b.tsx"],
nonExecutableModules: [],
counterBearingModules: ["src/a.ts", "src/nested/b.tsx"],
counterlessModules: [],
});
expect(result).toMatchObject({
status: "FAIL",
@@ -347,7 +347,7 @@ describe("repository-aware risk coverage", () => {
uncoveredModules: ["src/a.ts"],
});
expect(result.failures).toContain(
"production module has zero coverage totals: src/a.ts",
"counter-bearing module has zero coverage totals: src/a.ts",
);
expect(result.failures).toEqual(
expect.arrayContaining([
@@ -359,7 +359,7 @@ describe("repository-aware risk coverage", () => {
);
});
it("accepts exact all-zero rows only for statically non-executable modules", () => {
it("accepts exact all-zero rows only for statically counterless modules", () => {
const parsedPolicy = parseRiskCoveragePolicy(
policy({ repositoryBaseline: 2, generatedPaths: [] }),
{ now },
@@ -384,10 +384,10 @@ describe("repository-aware risk coverage", () => {
status: "PASS",
selectedTotal: 2,
repositoryTotal: 2,
executableTotal: 1,
instrumentedExecutableTotal: 1,
nonExecutableTotal: 1,
nonExecutableModules: ["src/type-only.ts"],
counterBearingTotal: 1,
instrumentedCounterBearingTotal: 1,
counterlessTotal: 1,
counterlessModules: ["src/type-only.ts"],
uncoveredModules: [],
});
@@ -402,11 +402,11 @@ describe("repository-aware risk coverage", () => {
},
});
expect(mismatch.failures).toContain(
"non-executable module has coverage counters: src/type-only.ts",
"counterless module has coverage counters: src/type-only.ts",
);
});
it("forbids critical and high-risk modules from being non-executable", () => {
it("forbids policy-sensitive modules from being counterless", () => {
const parsedPolicy = parseRiskCoveragePolicy(
policy({ repositoryBaseline: 1, generatedPaths: [] }),
{ now },
@@ -420,8 +420,8 @@ describe("repository-aware risk coverage", () => {
expect(result.failures).toEqual(
expect.arrayContaining([
"critical module cannot be non-executable: src/a.ts",
"high-risk module cannot be non-executable: src/a.ts",
"critical policy-sensitive module cannot be counterless: src/a.ts",
"high-risk policy-sensitive module cannot be counterless: src/a.ts",
]),
);
});
@@ -564,7 +564,7 @@ describe("repository-aware risk coverage", () => {
).rejects.toThrow(/stable file identity unavailable/u);
});
it("classifies type-only and barrel modules separately from runtime statements", async () => {
it("matches the observed V8 counterless import and re-export syntax", async () => {
const repositoryRoot = await repositoryFixture();
await Promise.all([
writeFile(
@@ -584,9 +584,29 @@ describe("repository-aware risk coverage", () => {
"void globalThis;\n",
),
writeFile(
path.join(repositoryRoot, "src/side-effect-import.ts"),
path.join(repositoryRoot, "src/import-type-empty.ts"),
"import type {} from './a.ts';\n",
),
writeFile(
path.join(repositoryRoot, "src/import-value-empty.ts"),
"import {} from './a.ts';\n",
),
writeFile(
path.join(repositoryRoot, "src/import-side-effect.ts"),
"import './a.ts';\n",
),
writeFile(
path.join(repositoryRoot, "src/import-value.ts"),
"import { a } from './a.ts';\n",
),
writeFile(
path.join(repositoryRoot, "src/reexport-named.ts"),
"export { a } from './a.ts';\n",
),
writeFile(
path.join(repositoryRoot, "src/reexport-star.ts"),
"export * from './a.ts';\n",
),
]);
const productionInventory = await buildProductionModuleInventory({
@@ -594,19 +614,22 @@ describe("repository-aware risk coverage", () => {
generatedPaths: ["src/generated.ts"],
});
expect(productionInventory.nonExecutableModules).toEqual([
expect(productionInventory.counterlessModules).toEqual([
"src/barrel.ts",
"src/import-side-effect.ts",
"src/import-type-empty.ts",
"src/import-value-empty.ts",
"src/import-value.ts",
"src/reexport-named.ts",
"src/reexport-star.ts",
"src/type-only.ts",
]);
expect(productionInventory.executableModules).toEqual(
expect.arrayContaining([
"src/a.ts",
"src/nested/b.tsx",
"src/runtime-export.ts",
"src/side-effect-import.ts",
"src/side-effect.ts",
]),
);
expect(productionInventory.counterBearingModules).toEqual([
"src/a.ts",
"src/nested/b.tsx",
"src/runtime-export.ts",
"src/side-effect.ts",
]);
});
it("rejects a post-lstat file identity swap even without relying on O_NOFOLLOW", async () => {