Files
boss/tests/admin-notification-dispatch-route.test.ts

134 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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";
import { NextRequest } from "next/server";
let runtimeRoot = "";
let data: typeof import("../src/lib/boss-data");
let authCookie = "";
let postDispatch: (typeof import("../src/app/api/v1/admin/notifications/dispatch/route"))["POST"];
let getOverview: (typeof import("../src/app/api/v1/admin/overview/route"))["GET"];
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-admin-notification-dispatch-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
process.env.BOSS_ADMIN_NOTIFICATION_MODE = "disabled";
const [dataModule, authModule, dispatchRoute, overviewRoute] = await Promise.all([
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-auth.ts"),
import("../src/app/api/v1/admin/notifications/dispatch/route.ts"),
import("../src/app/api/v1/admin/overview/route.ts"),
]);
data = dataModule;
authCookie = authModule.AUTH_SESSION_COOKIE;
postDispatch = dispatchRoute.POST;
getOverview = overviewRoute.GET;
}
test.after(async () => {
if (runtimeRoot) await rm(runtimeRoot, { recursive: true, force: true });
});
test.beforeEach(async () => {
await setup();
const now = "2026-04-27T18:20:00+08:00";
const state = await data.readState();
await data.writeState({
...state,
adminCompanies: [
{
companyId: "tenant-a",
name: "Tenant A",
ownerAccount: "owner@tenant-a.com",
successOwnerAccount: "cs@platform.com",
status: "active",
createdAt: now,
updatedAt: now,
},
],
authAccounts: [
{
id: "account-platform",
account: "platform@example.com",
passwordHash: "hash",
displayName: "平台管理员",
role: "highest_admin",
createdAt: now,
updatedAt: now,
},
{
id: "account-owner",
account: "owner@tenant-a.com",
passwordHash: "hash",
displayName: "客户负责人",
role: "admin",
companyId: "tenant-a",
createdAt: now,
updatedAt: now,
},
],
adminNotifications: [
{
notificationId: "risk-sla-overdue:ops-fault:fault-a",
kind: "risk_sla_overdue",
severity: "critical",
companyId: "tenant-a",
riskId: "ops-fault:fault-a",
title: "风险 SLA 已超时local-agent",
body: "local-agent 离线超过 SLA",
status: "open",
createdAt: now,
},
],
adminRiskTimeline: [],
});
});
async function adminRequest(url: string, init: RequestInit = {}) {
const session = await data.createAuthSession({
account: "platform@example.com",
role: "highest_admin",
displayName: "平台管理员",
loginMethod: "password",
});
return new NextRequest(url, {
...init,
headers: {
"content-type": "application/json",
...(init.headers ?? {}),
cookie: `${authCookie}=${session.sessionToken}`,
},
});
}
test("highest admin can dispatch open risk notifications and persist delivery status", async () => {
const response = await postDispatch(await adminRequest("http://127.0.0.1:3000/api/v1/admin/notifications/dispatch", {
method: "POST",
}));
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.results.length, 1);
assert.equal(payload.results[0].notificationId, "risk-sla-overdue:ops-fault:fault-a");
assert.equal(payload.results[0].status, "disabled");
const state = await data.readState();
assert.equal(state.adminNotifications[0]?.deliveryStatus, "disabled");
assert.equal(state.adminNotifications[0]?.deliveryTarget?.includes("owner@tenant-a.com"), true);
assert.equal(state.adminRiskTimeline.some((event) => event.action === "notification_dispatch_disabled"), true);
});
test("admin overview exposes recent risk timeline events", async () => {
await postDispatch(await adminRequest("http://127.0.0.1:3000/api/v1/admin/notifications/dispatch", { method: "POST" }));
const response = await getOverview(await adminRequest("http://127.0.0.1:3000/api/v1/admin/overview"));
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.riskTimeline.length > 0, true);
assert.equal(payload.riskTimeline[0].riskId, "ops-fault:fault-a");
});