Files
boss/tests/ops-repair-ticket-events.test.ts
2026-04-07 16:04:18 +08:00

122 lines
4.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
let runtimeRoot = "";
let readState: (typeof import("../src/lib/boss-data"))["readState"];
let writeState: (typeof import("../src/lib/boss-data"))["writeState"];
let approveRepairTicket: (typeof import("../src/lib/boss-data"))["approveRepairTicket"];
let verifyRepairTicket: (typeof import("../src/lib/boss-data"))["verifyRepairTicket"];
let subscribeBossEvents: (typeof import("../src/lib/boss-events"))["subscribeBossEvents"];
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-ops-events-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const data = await import("../src/lib/boss-data.ts");
const events = await import("../src/lib/boss-events.ts");
readState = data.readState;
writeState = data.writeState;
approveRepairTicket = data.approveRepairTicket;
verifyRepairTicket = data.verifyRepairTicket;
subscribeBossEvents = events.subscribeBossEvents;
}
async function resetOpsState() {
const state = await readState();
state.opsFaults = [
{
faultId: "fault-win-camera",
faultKey: "WIN_CAMERA_UPLOAD_DELAY",
severity: "high",
status: "open",
nodeId: "mac-studio",
serviceName: "boss-local-agent",
projectId: "master-agent",
threadRef: "thread-ops-1",
traceId: "trace-ops-1",
runbookId: "runbook-camera-sync",
firstSeenAt: "2026-03-25T11:34:00+08:00",
lastSeenAt: "2026-03-25T11:58:00+08:00",
summary: "摄像头证据上传延迟。",
suggestedNextAction: "重试本地 agent 上传。",
autoRepairable: true,
},
];
state.opsRepairTickets = [
{
ticketId: "ticket-win-camera",
faultId: "fault-win-camera",
title: "重试摄像头证据上传",
approvalStatus: "approved",
executionStatus: "running",
requestedBy: "运维 Agent",
approvedBy: "主 Agent",
targetNodeId: "mac-studio",
actionSummary: "重新触发本地 agent 心跳和摄像头证据上传。",
resultSummary: "等待复验中。",
createdAt: "2026-03-25T11:42:00+08:00",
updatedAt: "2026-03-25T11:57:00+08:00",
},
];
state.opsRepairVerifications = [
{
verificationId: "verify-win-camera",
ticketId: "ticket-win-camera",
verifier: "运维审计 Agent",
status: "watching",
summary: "等待关键帧落库后关闭工单。",
verifiedAt: "2026-03-25T11:58:00+08:00",
},
];
await writeState(state);
}
test.beforeEach(async () => {
await setup();
await resetOpsState();
});
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("approve repair ticket publishes project context risk event", async () => {
const events: Array<{ event: string; payload: { status?: string; note?: string } }> = [];
const unsubscribe = subscribeBossEvents((event, payload) => {
events.push({ event, payload });
});
await approveRepairTicket("ticket-win-camera");
unsubscribe();
const latest = events.at(-1);
assert.ok(latest);
assert.equal(latest.event, "project.context_risk.updated");
assert.equal(latest.payload.status, "approved");
assert.equal(latest.payload.note, "ticket-win-camera");
});
test("verify repair ticket publishes project context risk event", async () => {
const events: Array<{ event: string; payload: { status?: string; note?: string } }> = [];
const unsubscribe = subscribeBossEvents((event, payload) => {
events.push({ event, payload });
});
await verifyRepairTicket("ticket-win-camera");
unsubscribe();
const latest = events.at(-1);
assert.ok(latest);
assert.equal(latest.event, "project.context_risk.updated");
assert.equal(latest.payload.status, "verified");
assert.equal(latest.payload.note, "ticket-win-camera");
});