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
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);
|
|
});
|
|
});
|