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
+52 -49
View File
@@ -63,18 +63,18 @@ export type ProductionModuleInventory = Readonly<{
files: readonly string[];
preExclusionTotal: number;
generatedExclusions: readonly string[];
executableModules: readonly string[];
nonExecutableModules: readonly string[];
counterBearingModules: readonly string[];
counterlessModules: readonly string[];
}>;
export type RiskCoverageResult = Readonly<{
status: "PASS" | "FAIL";
selectedTotal: number;
repositoryTotal: number;
executableTotal: number;
instrumentedExecutableTotal: number;
nonExecutableTotal: number;
nonExecutableModules: readonly string[];
counterBearingTotal: number;
instrumentedCounterBearingTotal: number;
counterlessTotal: number;
counterlessModules: readonly string[];
preExclusionTotal: number;
generatedExclusionCount: number;
generatedExclusions: readonly string[];
@@ -370,13 +370,12 @@ type TypeScriptAstNode = Readonly<{
type?: unknown;
body?: unknown;
declaration?: unknown;
specifiers?: unknown;
declare?: unknown;
const?: unknown;
importKind?: unknown;
}>;
function statementIsExecutable(value: unknown): boolean {
function statementIsCoverageCounterBearing(value: unknown): boolean {
if (!isRecord(value) || typeof value.type !== "string") {
throw new TypeError("TypeScript parser returned an invalid statement");
}
@@ -393,7 +392,7 @@ function statementIsExecutable(value: unknown): boolean {
return false;
}
if (statement.type === "ImportDeclaration") {
return Array.isArray(statement.specifiers) && statement.specifiers.length === 0;
return false;
}
if (
statement.type === "ExportAllDeclaration" ||
@@ -406,7 +405,7 @@ function statementIsExecutable(value: unknown): boolean {
statement.type === "ExportDefaultDeclaration"
) {
return statement.declaration !== null && statement.declaration !== undefined
? statementIsExecutable(statement.declaration)
? statementIsCoverageCounterBearing(statement.declaration)
: false;
}
if (
@@ -427,7 +426,7 @@ function statementIsExecutable(value: unknown): boolean {
return true;
}
export function hasExecutableTypeScriptStatements(
export function hasCoverageCounterBearingStatements(
source: string,
relativePath: string,
): boolean {
@@ -454,7 +453,7 @@ export function hasExecutableTypeScriptStatements(
if (!isRecord(parsed) || !Array.isArray(parsed.body)) {
throw new TypeError("TypeScript parser returned an invalid program");
}
return parsed.body.some(statementIsExecutable);
return parsed.body.some(statementIsCoverageCounterBearing);
}
export async function buildProductionModuleInventory(
@@ -481,8 +480,8 @@ export async function buildProductionModuleInventory(
}
const allModules: string[] = [];
const executableModules: string[] = [];
const nonExecutableModules: string[] = [];
const counterBearingModules: string[] = [];
const counterlessModules: string[] = [];
async function visit(relativeDirectory: string): Promise<void> {
const absoluteDirectory = path.join(repositoryRoot, relativeDirectory);
const entries = await readDirectory(absoluteDirectory);
@@ -543,10 +542,10 @@ export async function buildProductionModuleInventory(
await handle?.close();
}
allModules.push(relativeTarget);
if (hasExecutableTypeScriptStatements(source, relativeTarget)) {
executableModules.push(relativeTarget);
if (hasCoverageCounterBearingStatements(source, relativeTarget)) {
counterBearingModules.push(relativeTarget);
} else {
nonExecutableModules.push(relativeTarget);
counterlessModules.push(relativeTarget);
}
}
}
@@ -565,11 +564,11 @@ export async function buildProductionModuleInventory(
files: Object.freeze(inventory),
preExclusionTotal: allModules.length,
generatedExclusions: Object.freeze([...generatedPaths].sort()),
executableModules: Object.freeze(
executableModules.filter((file) => !generated.has(file)).sort(),
counterBearingModules: Object.freeze(
counterBearingModules.filter((file) => !generated.has(file)).sort(),
),
nonExecutableModules: Object.freeze(
nonExecutableModules.filter((file) => !generated.has(file)).sort(),
counterlessModules: Object.freeze(
counterlessModules.filter((file) => !generated.has(file)).sort(),
),
});
}
@@ -718,22 +717,22 @@ export function evaluateRiskCoverage(input: Readonly<{
if (new Set(inventory).size !== inventory.length) {
throw new TypeError("production module inventory contains a duplicate path");
}
const executableModules = input.inventory.executableModules.map((file) =>
exactSourcePath(file, "executable inventory path"),
const counterBearingModules = input.inventory.counterBearingModules.map((file) =>
exactSourcePath(file, "counter-bearing inventory path"),
);
const nonExecutableModules = input.inventory.nonExecutableModules.map((file) =>
exactSourcePath(file, "non-executable inventory path"),
const counterlessModules = input.inventory.counterlessModules.map((file) =>
exactSourcePath(file, "counterless inventory path"),
);
const executableSet = new Set(executableModules);
const nonExecutableSet = new Set(nonExecutableModules);
const partition = [...executableModules, ...nonExecutableModules].sort();
const counterBearingSet = new Set(counterBearingModules);
const counterlessSet = new Set(counterlessModules);
const partition = [...counterBearingModules, ...counterlessModules].sort();
if (
executableSet.size !== executableModules.length ||
nonExecutableSet.size !== nonExecutableModules.length ||
executableModules.some((file) => nonExecutableSet.has(file)) ||
counterBearingSet.size !== counterBearingModules.length ||
counterlessSet.size !== counterlessModules.length ||
counterBearingModules.some((file) => counterlessSet.has(file)) ||
partition.join("\n") !== [...inventory].sort().join("\n")
) {
throw new TypeError("production inventory executable provenance is inconsistent");
throw new TypeError("production inventory counter-bearing provenance is inconsistent");
}
const generatedExclusions = input.inventory.generatedExclusions.map((file) =>
exactSourcePath(file, "generated exclusion"),
@@ -780,14 +779,14 @@ export function evaluateRiskCoverage(input: Readonly<{
);
});
const zeroCoverageSet = new Set(zeroCoverageModules);
const zeroExecutableModules = zeroCoverageModules.filter((file) =>
executableSet.has(file),
const zeroCounterBearingModules = zeroCoverageModules.filter((file) =>
counterBearingSet.has(file),
);
const nonExecutableWithCounters = nonExecutableModules.filter((file) => {
const counterlessWithCounters = counterlessModules.filter((file) => {
const metrics = selected.get(file);
return metrics !== undefined && !zeroCoverageSet.has(file);
});
const instrumentedExecutableTotal = executableModules.filter((file) => {
const instrumentedCounterBearingTotal = counterBearingModules.filter((file) => {
const metrics = selected.get(file);
return metrics !== undefined && !zeroCoverageSet.has(file);
}).length;
@@ -827,18 +826,18 @@ export function evaluateRiskCoverage(input: Readonly<{
.filter(
(file) =>
!selected.has(file) ||
(executableSet.has(file) && zeroCoverageSet.has(file)),
(counterBearingSet.has(file) && zeroCoverageSet.has(file)),
)
.sort();
failures.push(
...inventory
.filter((file) => !selected.has(file))
.map((file) => `production module missing from coverage: ${file}`),
...zeroExecutableModules.map(
(file) => `production module has zero coverage totals: ${file}`,
...zeroCounterBearingModules.map(
(file) => `counter-bearing module has zero coverage totals: ${file}`,
),
...nonExecutableWithCounters.map(
(file) => `non-executable module has coverage counters: ${file}`,
...counterlessWithCounters.map(
(file) => `counterless module has coverage counters: ${file}`,
),
);
for (const modulePolicy of input.policy.criticalModules) {
@@ -846,8 +845,10 @@ export function evaluateRiskCoverage(input: Readonly<{
failures.push(`critical module is outside production inventory: ${modulePolicy.path}`);
continue;
}
if (nonExecutableSet.has(modulePolicy.path)) {
failures.push(`critical module cannot be non-executable: ${modulePolicy.path}`);
if (counterlessSet.has(modulePolicy.path)) {
failures.push(
`critical policy-sensitive module cannot be counterless: ${modulePolicy.path}`,
);
}
const actual = selected.get(modulePolicy.path);
if (!actual) {
@@ -863,8 +864,10 @@ export function evaluateRiskCoverage(input: Readonly<{
if (!inventorySet.has(highRiskPath)) {
failures.push(`high-risk module is outside production inventory: ${highRiskPath}`);
}
if (nonExecutableSet.has(highRiskPath)) {
failures.push(`high-risk module cannot be non-executable: ${highRiskPath}`);
if (counterlessSet.has(highRiskPath)) {
failures.push(
`high-risk policy-sensitive module cannot be counterless: ${highRiskPath}`,
);
}
}
for (const waiver of input.policy.waivers) {
@@ -881,10 +884,10 @@ export function evaluateRiskCoverage(input: Readonly<{
status: failures.length === 0 ? "PASS" : "FAIL",
selectedTotal: inventory.length - uncoveredModules.length,
repositoryTotal: inventory.length,
executableTotal: executableModules.length,
instrumentedExecutableTotal,
nonExecutableTotal: nonExecutableModules.length,
nonExecutableModules: Object.freeze([...nonExecutableModules].sort()),
counterBearingTotal: counterBearingModules.length,
instrumentedCounterBearingTotal,
counterlessTotal: counterlessModules.length,
counterlessModules: Object.freeze([...counterlessModules].sort()),
preExclusionTotal: input.inventory.preExclusionTotal,
generatedExclusionCount: generatedExclusions.length,
generatedExclusions: Object.freeze([...generatedExclusions].sort()),