You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
3.8 KiB

import { beforeEach, describe, expect, it, vi } from "vitest";
import { GET as listAlerts } from "@/app/api/alerts/route";
import { POST as resolveAlert } from "@/app/api/alerts/[id]/resolve/route";
vi.mock("@/lib/prisma", () => ({
prisma: {
alertLog: {
count: vi.fn(),
findMany: vi.fn(),
findUnique: vi.fn(),
update: vi.fn(),
},
},
}));
import { prisma } from "@/lib/prisma";
const ADMIN_HEADERS = {
"x-auth-type": "admin",
"x-user-id": "U_admin_demo",
"x-user-role": "admin",
};
const SERVICE_HEADERS = {
"x-auth-type": "service",
"x-customer-id": "CUST_001",
"x-permissions": "",
};
const SAMPLE_ALERT = {
id: BigInt(1),
alertType: "RPA_FAILED",
quoteId: "QTE_001",
cargoHash: "abc123",
detailJson: { reason: "timeout" },
status: "open",
resolverId: null,
resolvedAt: null,
createdAt: new Date("2026-06-16T08:00:00Z"),
};
beforeEach(() => {
vi.clearAllMocks();
});
describe("GET /api/alerts", () => {
it("admin + type 筛选 → 200", async () => {
vi.mocked(prisma.alertLog.count).mockResolvedValue(1);
vi.mocked(prisma.alertLog.findMany).mockResolvedValue([SAMPLE_ALERT as never]);
const response = await listAlerts(
new Request(
"http://localhost/api/alerts?page=1&size=20&type=RPA_FAILED&status=open",
{ headers: ADMIN_HEADERS },
),
);
const json = await response.json();
expect(response.status).toBe(200);
expect(json.code).toBe(0);
expect(json.data.list).toHaveLength(1);
expect(json.data.list[0].alert_type).toBe("RPA_FAILED");
expect(prisma.alertLog.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: { alertType: "RPA_FAILED", status: "open" },
}),
);
});
it("缺分页 → 400", async () => {
const response = await listAlerts(
new Request("http://localhost/api/alerts", { headers: ADMIN_HEADERS }),
);
expect(response.status).toBe(400);
});
it("宿主 token → 403", async () => {
const response = await listAlerts(
new Request("http://localhost/api/alerts?page=1&size=20", {
headers: SERVICE_HEADERS,
}),
);
expect(response.status).toBe(403);
});
});
describe("POST /api/alerts/{id}/resolve", () => {
it("admin 处理 → 200 + resolved", async () => {
vi.mocked(prisma.alertLog.findUnique).mockResolvedValue(
SAMPLE_ALERT as never,
);
vi.mocked(prisma.alertLog.update).mockResolvedValue({
...SAMPLE_ALERT,
status: "resolved",
resolverId: "U_admin_demo",
resolvedAt: new Date("2026-06-16T09:00:00Z"),
} as never);
const response = await resolveAlert(
new Request("http://localhost/api/alerts/1/resolve", {
method: "POST",
headers: { ...ADMIN_HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({ resolver_id: "U_admin_demo" }),
}),
{ params: Promise.resolve({ id: "1" }) },
);
const json = await response.json();
expect(response.status).toBe(200);
expect(json.data.status).toBe("resolved");
expect(json.data.resolver_id).toBe("U_admin_demo");
});
it("不存在 id → 404", async () => {
vi.mocked(prisma.alertLog.findUnique).mockResolvedValue(null);
const response = await resolveAlert(
new Request("http://localhost/api/alerts/999/resolve", {
method: "POST",
headers: ADMIN_HEADERS,
}),
{ params: Promise.resolve({ id: "999" }) },
);
expect(response.status).toBe(404);
});
it("非 admin → 403", async () => {
const response = await resolveAlert(
new Request("http://localhost/api/alerts/1/resolve", {
method: "POST",
headers: SERVICE_HEADERS,
}),
{ params: Promise.resolve({ id: "1" }) },
);
expect(response.status).toBe(403);
});
});