WREAD1 准备企微审批可读性门禁
这个提交包含在:
@@ -0,0 +1,220 @@
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
const path = require("node:path");
|
||||
|
||||
const {
|
||||
executeReadabilityGate,
|
||||
parseArgs,
|
||||
} = require("../../../src/scripts/check-wecom-approval-readability-gate");
|
||||
|
||||
const SENSITIVE_TEMPLATE_ID = "tpl_readability_full_identifier_must_not_leak";
|
||||
const SENSITIVE_SP_NO = "SPREADABILITYFULLIDENTIFIERMUSTNOTLEAK";
|
||||
const SENSITIVE_USER_ID = "readability_userid_full_must_not_leak";
|
||||
const SENSITIVE_FORM_VALUE = "这里是表单值,不应出现在报告里";
|
||||
|
||||
function tempDir() {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), "wread1-test-"));
|
||||
}
|
||||
|
||||
function writeSummary(root, dirname, payload) {
|
||||
const dir = path.join(root, dirname);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const filePath = path.join(dir, "summary.json");
|
||||
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2));
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function writeOkEvidence(root) {
|
||||
writeSummary(root, "wecom-approval-fixtures-20260519T053400Z", {
|
||||
status: "PASS_FIXTURE_CREATED",
|
||||
conclusion: "PASS_FIXTURE_CREATED",
|
||||
checks: {
|
||||
liveWrite: {
|
||||
createdTemplatePrefixes: {
|
||||
leave: "tpl-leav***",
|
||||
missed_punch: "tpl-miss***",
|
||||
},
|
||||
submittedApprovalPrefixes: {
|
||||
leave: "SPLEAVE***",
|
||||
missed_punch: "SPMISS***",
|
||||
},
|
||||
},
|
||||
autoRerun: {
|
||||
status: "OK",
|
||||
},
|
||||
},
|
||||
rawShouldNotCopy: SENSITIVE_TEMPLATE_ID,
|
||||
});
|
||||
writeSummary(root, "wecom-approval-callback-poc-20260519T092648Z", {
|
||||
status: "PASS_CALLBACK_EVENT_CONFIRMED",
|
||||
conclusion: "PASS_CALLBACK_EVENT_CONFIRMED",
|
||||
checks: {
|
||||
eventEvidence: {
|
||||
eventCount: 1,
|
||||
lastEvent: {
|
||||
approvalType: "missed_punch",
|
||||
statusLabel: "已通过",
|
||||
spNoPrefix: "SPMISS***",
|
||||
templateIdPrefix: "tpl-miss***",
|
||||
recordedAt: "2026-05-19T09:26:48.000Z",
|
||||
rawShouldNotCopy: SENSITIVE_SP_NO,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
writeSummary(root, "wecom-approval-compensation-poc-20260519T115944Z", {
|
||||
status: "PASS_WITH_FINDINGS",
|
||||
conclusion: "PASS_WITH_FINDINGS",
|
||||
checks: {
|
||||
templateDiscovery: { status: "OK", discovered: { leave: SENSITIVE_TEMPLATE_ID } },
|
||||
targets: [
|
||||
{
|
||||
target: "leave",
|
||||
status: "OK",
|
||||
detailCount: 1,
|
||||
statusSummary: { "审批中": 1 },
|
||||
details: [{ spNoPrefix: "SPLEAVE***", rawShouldNotCopy: SENSITIVE_FORM_VALUE }],
|
||||
},
|
||||
{
|
||||
target: "missed_punch",
|
||||
status: "OK",
|
||||
detailCount: 1,
|
||||
statusSummary: { "已通过": 1 },
|
||||
details: [{ spNoPrefix: "SPMISS***" }],
|
||||
},
|
||||
],
|
||||
callbackCompare: {
|
||||
findings: [
|
||||
{ id: "callback_missing_for_pulled_detail", target: "leave", spNoPrefix: "SPLEAVE***" },
|
||||
],
|
||||
},
|
||||
},
|
||||
rawUserId: SENSITIVE_USER_ID,
|
||||
});
|
||||
}
|
||||
|
||||
describe("check-wecom-approval-readability-gate", () => {
|
||||
it("parses manual results and report options", () => {
|
||||
const options = parseArgs([
|
||||
"--format=json",
|
||||
"--artifact-root=/tmp/wread1-artifacts",
|
||||
"--manual-result=leave:PASS_READABLE",
|
||||
"--manual-result=missed_punch:PASS_WITH_TEMPLATE_ADJUSTMENTS",
|
||||
"--manual-note=leave:卡片可读",
|
||||
"--confirmed-by=wangshunli",
|
||||
]);
|
||||
|
||||
expect(options.format).toBe("json");
|
||||
expect(options.artifactRoot).toBe("/tmp/wread1-artifacts");
|
||||
expect(options.manualResults).toEqual({
|
||||
leave: "PASS_READABLE",
|
||||
missed_punch: "PASS_WITH_TEMPLATE_ADJUSTMENTS",
|
||||
});
|
||||
expect(options.manualNotes.leave).toBe("卡片可读");
|
||||
expect(options.confirmedBy).toBe("wangshunli");
|
||||
expect(() => parseArgs(["--manual-result=leave:PASS"])).toThrow(/Unsupported manual result/);
|
||||
});
|
||||
|
||||
it("pauses for manual confirmation when existing evidence is available", async () => {
|
||||
const root = tempDir();
|
||||
writeOkEvidence(root);
|
||||
|
||||
const report = await executeReadabilityGate({
|
||||
...parseArgs(["--artifact-root", root]),
|
||||
reportDir: path.join(root, "out"),
|
||||
}, { generatedAt: "2026-05-20T00:00:00.000Z" });
|
||||
|
||||
expect(report.status).toBe("PAUSED_MANUAL_CONFIRMATION");
|
||||
expect(report.checks.sampleEvidence.status).toBe("OK");
|
||||
expect(report.checks.evidence.fixtures.status).toBe("OK");
|
||||
expect(report.checks.evidence.compensation.status).toBe("OK");
|
||||
expect(report.checks.manualResults.map((item) => item.status)).toEqual([
|
||||
"PENDING_MANUAL_CONFIRMATION",
|
||||
"PENDING_MANUAL_CONFIRMATION",
|
||||
]);
|
||||
});
|
||||
|
||||
it("accepts WACP3R compensation detail evidence when historical fixture artifacts are not persisted", async () => {
|
||||
const root = tempDir();
|
||||
writeSummary(root, "wecom-approval-compensation-poc-20260519T115944Z", {
|
||||
status: "PASS_WITH_FINDINGS",
|
||||
conclusion: "PASS_WITH_FINDINGS",
|
||||
checks: {
|
||||
templateDiscovery: { status: "OK" },
|
||||
targets: [
|
||||
{
|
||||
target: "leave",
|
||||
status: "OK",
|
||||
details: [{ spNoPrefix: "SPLEAVE***" }],
|
||||
},
|
||||
{
|
||||
target: "missed_punch",
|
||||
status: "OK",
|
||||
details: [{ spNoPrefix: "SPMISS***" }],
|
||||
},
|
||||
],
|
||||
callbackCompare: { findings: [] },
|
||||
},
|
||||
});
|
||||
|
||||
const report = await executeReadabilityGate(parseArgs(["--artifact-root", root]), {
|
||||
generatedAt: "2026-05-20T00:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PAUSED_MANUAL_CONFIRMATION");
|
||||
expect(report.checks.sampleEvidence.status).toBe("OK");
|
||||
expect(report.checks.sampleEvidence.reason).toBe("wacp3r_compensation_detail_evidence_available");
|
||||
expect(report.checks.evidence.fixtures.status).toBe("MISSING");
|
||||
});
|
||||
|
||||
it("returns PASS_WITH_TEMPLATE_ADJUSTMENTS when one template needs changes", async () => {
|
||||
const root = tempDir();
|
||||
writeOkEvidence(root);
|
||||
|
||||
const report = await executeReadabilityGate(parseArgs([
|
||||
"--artifact-root", root,
|
||||
"--manual-result=leave:PASS_READABLE",
|
||||
"--manual-result=missed_punch:PASS_WITH_TEMPLATE_ADJUSTMENTS",
|
||||
"--manual-note=missed_punch:快捷按钮容易误点,需要提醒审批人点详情",
|
||||
"--confirmed-by=wangshunli",
|
||||
]), { generatedAt: "2026-05-20T00:00:00.000Z" });
|
||||
|
||||
expect(report.status).toBe("PASS_WITH_TEMPLATE_ADJUSTMENTS");
|
||||
expect(report.confirmedBy).toBe("wangshunli");
|
||||
expect(report.checks.manualResults.find((item) => item.target === "missed_punch").note).toContain("快捷按钮");
|
||||
});
|
||||
|
||||
it("asks for a fresh fixture when existing sample evidence is missing", async () => {
|
||||
const root = tempDir();
|
||||
writeSummary(root, "wecom-approval-compensation-poc-20260519T115944Z", {
|
||||
status: "PASS_WITH_FINDINGS",
|
||||
checks: { targets: [] },
|
||||
});
|
||||
|
||||
const report = await executeReadabilityGate(parseArgs(["--artifact-root", root]), {
|
||||
generatedAt: "2026-05-20T00:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PARTIAL_READABILITY_NEEDS_FRESH_FIXTURE");
|
||||
expect(report.configGaps.map((item) => item.id)).toContain("readability_existing_sample");
|
||||
});
|
||||
|
||||
it("does not leak full template, sp_no, userid or form values", async () => {
|
||||
const root = tempDir();
|
||||
writeOkEvidence(root);
|
||||
|
||||
const report = await executeReadabilityGate(parseArgs([
|
||||
"--artifact-root", root,
|
||||
"--manual-result=leave:PASS_READABLE",
|
||||
"--manual-result=missed_punch:PASS_READABLE",
|
||||
"--manual-note=leave:不要泄露 SPREADABILITYFULLIDENTIFIERMUSTNOTLEAK",
|
||||
]), { generatedAt: "2026-05-20T00:00:00.000Z" });
|
||||
|
||||
const serialized = JSON.stringify(report);
|
||||
expect(report.status).toBe("PASS_READABLE");
|
||||
expect(serialized).not.toContain(SENSITIVE_TEMPLATE_ID);
|
||||
expect(serialized).not.toContain(SENSITIVE_SP_NO);
|
||||
expect(serialized).not.toContain(SENSITIVE_USER_ID);
|
||||
expect(serialized).not.toContain(SENSITIVE_FORM_VALUE);
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户