汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、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项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
131 行
8.1 KiB
JavaScript
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;
|
|
}
|
|
}
|