文件
hyb-oa/scripts/qa/full-local-coverage.mjs
wangsli 1f8d5b658a 完善借款、编号、付款与预算并发及验收(09-06-release-gate-integrity)
汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、PWA/采购事务、错误原因与日期校验及ERP退役。
统一共享预算池锁序和锁后判断,恢复差旅原预算选择,补原因码固定夹具及反向完整性检查。
纳入配套迁移、测试、受管QA、分层验收合同、规约及任务记录;原始证据留在ignored artifacts。

关联任务:09-17-company-loan-principal-repayment、09-17-erp-exclusive-retirement、09-18-reimbursement-periodic-numbering、09-08-reimbursement-payment-closeout、09-18-local-concurrency-product-fixes、09-19-budget-pool-concurrency、09-05-error-explainability-payment-readiness。
预算补审由Claude定向验证2文件928项通过,原4446/28及r6结果仍仅对应各自历史输入;本轮未重跑测试。
完整新候选门禁、R1真实入口接入与12项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
2026-09-19 13:11:04 +08:00

131 行
8.1 KiB
JavaScript

#!/usr/bin/env node
import { createHash } from "node:crypto";
import { readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { verifyFullLocalDiscovery } from "./verify-full-local-discovery.mjs";
import { sourceSnapshot } from "./full-local-diagnostic.mjs";
const hash = value => createHash("sha256").update(value).digest("hex");
export const caseKey = entry => JSON.stringify([entry.file, entry.id, entry.title, entry.projectName ?? "", entry.repeatEachIndex ?? 0]);
const sameCandidate = (a, b) => Boolean(a && b && /^[a-f0-9]{40}$/.test(a.commit ?? "") && /^[a-f0-9]{64}$/.test(a.digest ?? "") && a.commit === b.commit && a.digest === b.digest);
export const localSpecArgs = plan => plan.localSpecs.map(file => `${file.slice(7).replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}$`);
export function coveragePlan(root, discovery, contract, candidate) {
const full = verifyFullLocalDiscovery(root, discovery);
const issues = [...full.issues];
const exceptions = contract?.exceptions ?? [];
if (contract?.schemaVersion !== 1 || !Array.isArray(contract?.exceptions) || !contract.approvalReference) issues.push("invalid_coverage_contract");
if (!sameCandidate(candidate, candidate)) issues.push("invalid_candidate");
const keys = full.cases.map(caseKey);
if (new Set(keys).size !== keys.length) issues.push("duplicate_discovery_case");
const registered = new Map();
for (const entry of exceptions) {
const key = caseKey(entry);
if (registered.has(key)) issues.push("duplicate_exception");
registered.set(key, entry);
if (!keys.includes(key)) issues.push("exception_identity_drift");
if (!["post-deploy", "frozen"].includes(entry.kind) || !entry.reason || !entry.entry || !entry.responsibilityRole || !entry.requiredEvidence) issues.push("invalid_exception_obligation");
if (!full.expectedSpecs.includes(entry.file)) issues.push("exception_source_unknown");
else if (hash(readFileSync(path.join(root, entry.file))) !== entry.sourceSha256) issues.push("exception_source_changed");
if (entry.kind === "frozen" && !full.cases.some(row => row.file === entry.replacementFile && row.title === entry.replacementTitle && !exceptions.some(other => caseKey(other) === caseKey(row)))) issues.push("frozen_protection_missing");
}
const exceptionFiles = new Set(exceptions.map(entry => entry.file));
const localCases = full.cases.filter(entry => !registered.has(caseKey(entry)));
if (localCases.some(entry => exceptionFiles.has(entry.file))) issues.push("unregistered_case_in_exception_file");
if (!localCases.length) issues.push("empty_local_coverage");
const localSpecs = [...new Set(localCases.map(entry => entry.file))].sort();
return {
schemaVersion: 1, kind: "LAYERED_LOCAL_COVERAGE_PLAN", status: issues.length ? "FAIL" : "PASS",
candidate: { commit: candidate?.commit, digest: candidate?.digest }, contractSha256: hash(JSON.stringify(contract)),
discoverySha256: hash(JSON.stringify(discovery)), localSpecs, localCases,
obligations: exceptions.map(entry => ({ ...entry, status: entry.kind === "post-deploy" ? "NOT_RUN_LOCAL" : "FROZEN_PROTECTION_REQUIRED" })),
totalCases: full.cases.length, issues: [...new Set(issues)], normalReleaseEligible: false,
};
}
function reportCases(report) {
const cases = [];
function visit(suites, inherited = "") {
for (const suite of suites ?? []) {
for (const spec of suite.specs ?? []) {
let file = (spec.file ?? suite.file ?? inherited).replaceAll("\\", "/");
if (!file.startsWith("client/tests/e2e/")) file = `client/tests/e2e/${file.replace(/^tests\/e2e\//u, "")}`;
for (const test of spec.tests ?? []) cases.push({ file, id: spec.id, title: spec.title, projectName: test.projectName ?? "", repeatEachIndex: test.repeatEachIndex ?? 0, ...test });
}
visit(suite.suites, suite.file ?? inherited);
}
}
visit(report?.suites);
return cases;
}
export function verifyCaseExecution(expected, report) {
const issues = [];
if (!Array.isArray(report?.suites) || !Array.isArray(report?.errors)) issues.push("invalid_execution_report");
if (report?.errors?.length) issues.push("execution_errors");
const actual = reportCases(report);
const byKey = new Map();
for (const entry of actual) {
const key = caseKey(entry);
if (byKey.has(key)) issues.push("duplicate_execution_case");
byKey.set(key, entry);
}
const cases = expected.map(entry => {
const observed = byKey.get(caseKey(entry));
byKey.delete(caseKey(entry));
const passed = observed?.expectedStatus === "passed" && observed?.status === "expected" && observed?.results?.length === 1 && observed.results[0].status === "passed" && observed.results[0].retry === 0;
if (!passed) issues.push(observed ? "case_not_clean_pass" : "missing_execution_case");
return { ...entry, status: passed ? "PASS" : "FAIL", observedStatus: observed?.results?.at(-1)?.status ?? "NOT_RUN" };
});
if (byKey.size) issues.push("unexpected_execution_case");
return { status: issues.length ? "FAIL" : "PASS", cases, issues: [...new Set(issues)] };
}
export function verifyLocalCoverage(plan, report, candidate) {
const result = verifyCaseExecution(plan.localCases, report);
if (plan.status !== "PASS") result.issues.push("coverage_plan_invalid");
if (!sameCandidate(plan.candidate, candidate)) result.issues.push("candidate_changed");
result.status = result.issues.length ? "FAIL" : "PASS";
return { ...result, kind: "LOCAL_BROWSER_COVERAGE_ONLY", candidate: plan.candidate,
obligations: plan.obligations.map(entry => ({ ...entry, status: entry.kind === "post-deploy" ? "NOT_RUN_LOCAL" : result.cases.some(row => row.file === entry.replacementFile && row.title === entry.replacementTitle && row.status === "PASS") ? "FROZEN_PROTECTION_PASS" : "FROZEN_PROTECTION_FAIL" })),
deliveryStatus: "INCOMPLETE", normalReleaseEligible: false };
}
// Pure offline verifier. This does not run public tests or authorize deployment.
export function verifyPostDeployCoverage(plan, evidence) {
const expected = plan.obligations.filter(entry => entry.kind === "post-deploy");
const result = verifyCaseExecution(expected, evidence?.report);
if (plan.status !== "PASS" || !expected.length) result.issues.push("coverage_plan_invalid");
if (evidence?.profile !== "post-deploy-live" || evidence?.mode !== "live") result.issues.push("live_profile_required");
if (!sameCandidate(plan.candidate, evidence?.candidate) || !sameCandidate(plan.candidate, evidence?.deployedCandidate)) result.issues.push("deployed_candidate_mismatch");
result.status = result.issues.length ? "FAIL" : "PASS";
return { ...result, normalReleaseEligible: false };
}
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
try {
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const [mode, directory] = process.argv.slice(2);
const read = file => JSON.parse(readFileSync(path.join(directory, file), "utf8"));
const contract = JSON.parse(readFileSync(path.join(root, "scripts/qa/full-local-coverage-contract.json"), "utf8"));
const current = coveragePlan(root, read("discovery.json"), contract, sourceSnapshot(root));
if (mode === "plan") {
console.log(JSON.stringify(current, null, 2));
if (current.status === "PASS") writeFileSync(path.join(directory, "local-spec-args.txt"), localSpecArgs(current).join("\n") + "\n", { flag: "wx" });
process.exitCode = current.status === "PASS" ? 0 : 1;
} else if (mode === "verify-local") {
const saved = read("coverage-plan.json");
const result = verifyLocalCoverage(saved, read("playwright-results.json"), current.candidate);
if (JSON.stringify(saved) !== JSON.stringify(current)) result.issues.push("coverage_plan_changed");
result.status = result.issues.length ? "FAIL" : "PASS";
console.log(JSON.stringify(result, null, 2));
process.exitCode = result.status === "PASS" ? 0 : 1;
} else throw new Error("unknown_mode");
} catch {
console.log(JSON.stringify({ status: "FAIL", issues: ["coverage_input_invalid"], normalReleaseEligible: false }));
process.exitCode = 1;
}
}