建立每日自动异机备份基线(07-27-automatic-offhost-backup)
这个提交包含在:
@@ -5,6 +5,7 @@ const {
|
||||
evaluateAlertDecision,
|
||||
executeOpsAlert,
|
||||
parseArgs,
|
||||
readSummary,
|
||||
shouldSendForSummary,
|
||||
} = require("../../../src/scripts/send-wecom-ops-alert");
|
||||
|
||||
@@ -75,6 +76,72 @@ describe("send-wecom-ops-alert", () => {
|
||||
expect(message.body).not.toContain("手机号");
|
||||
});
|
||||
|
||||
it("reads stdin summary without preserving an injected absolute report path", () => {
|
||||
const summary = readSummary("-", JSON.stringify({
|
||||
issueId: "AUTO_BACKUP",
|
||||
status: "FAIL_AUTOMATIC_BACKUP",
|
||||
findingCount: 1,
|
||||
reportDir: `/private/${SENSITIVE_TOKEN}/state`,
|
||||
}));
|
||||
|
||||
expect(summary.issueId).toBe("AUTO_BACKUP");
|
||||
expect(summary.reportDir).toBe("stdin-summary");
|
||||
expect(JSON.stringify(summary)).not.toContain(SENSITIVE_TOKEN);
|
||||
expect(JSON.stringify(summary)).not.toContain("/private/");
|
||||
});
|
||||
|
||||
it("keeps AUTO_BACKUP issue id and uses backup-specific failure guidance", async () => {
|
||||
const summary = baseSummary({
|
||||
issueId: "AUTO_BACKUP",
|
||||
status: "FAIL_AUTOMATIC_BACKUP",
|
||||
reportDir: "host-backup-state",
|
||||
});
|
||||
const message = buildAlertMessage(summary);
|
||||
const report = await executeOpsAlert(baseOptions(), {
|
||||
summary,
|
||||
recipientRows: recipientRows(),
|
||||
sendMessageImpl: vi.fn(),
|
||||
env: {},
|
||||
config: {},
|
||||
});
|
||||
|
||||
expect(message.title).toContain("自动备份");
|
||||
expect(message.body).toContain("异机传输链路");
|
||||
expect(report.issueId).toBe("AUTO_BACKUP");
|
||||
expect(report.summary.issueId).toBe("AUTO_BACKUP");
|
||||
});
|
||||
|
||||
it("uses manual ARM shadow-restore guidance for quarterly reminder", () => {
|
||||
const summary = baseSummary({
|
||||
issueId: "AUTO_BACKUP",
|
||||
findings: [{ detail: "quarterly_shadow_restore_due_2026-Q3" }],
|
||||
reportDir: "host-backup-state",
|
||||
});
|
||||
const message = buildAlertMessage(summary);
|
||||
const firstDecision = evaluateAlertDecision(summary, baseOptions(), null);
|
||||
const repeatedDecision = evaluateAlertDecision(summary, baseOptions(), buildAlertState(summary));
|
||||
|
||||
expect(message.body).toContain("ARM 隔离目标执行人工影子恢复");
|
||||
expect(firstDecision.shouldSend).toBe(true);
|
||||
expect(firstDecision.reason).toBe("quarterly_restore_reminder");
|
||||
expect(repeatedDecision.shouldSend).toBe(false);
|
||||
});
|
||||
|
||||
it("sends the first automatic-backup failure and suppresses an identical repeat", () => {
|
||||
const summary = baseSummary({
|
||||
issueId: "AUTO_BACKUP",
|
||||
status: "FAIL_AUTOMATIC_BACKUP",
|
||||
findings: [{ source: "automatic_backup", status: "FAIL_AUTOMATIC_BACKUP", detail: "phase_offhost_transfer_failed" }],
|
||||
});
|
||||
const firstDecision = evaluateAlertDecision(summary, baseOptions(), null);
|
||||
const repeatedDecision = evaluateAlertDecision(summary, baseOptions(), buildAlertState(summary));
|
||||
|
||||
expect(firstDecision.shouldSend).toBe(true);
|
||||
expect(firstDecision.reason).toBe("blocked_or_failed");
|
||||
expect(repeatedDecision.shouldSend).toBe(false);
|
||||
expect(repeatedDecision.reason).toBe("repeated_automatic_backup_failure");
|
||||
});
|
||||
|
||||
it("keeps PASS without findings as no-send unless forced", () => {
|
||||
const summary = baseSummary({ status: "PASS_ARCHIVE_OPS_READY", findingCount: 0 });
|
||||
expect(shouldSendForSummary(summary, baseOptions())).toBe(false);
|
||||
|
||||
在新工单中引用
屏蔽一个用户