88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
import { beforeEach, expect, it, vi } from "vitest";
|
|
|
|
const mock = vi.hoisted(() => ({
|
|
status: "ready",
|
|
exec: vi.fn(),
|
|
lrange: vi.fn(),
|
|
}));
|
|
vi.mock("@/lib/redis", () => ({
|
|
redis: {
|
|
get status() {
|
|
return mock.status;
|
|
},
|
|
multi() {
|
|
const chain = {
|
|
lpush: () => chain,
|
|
ltrim: () => chain,
|
|
expire: () => chain,
|
|
exec: mock.exec,
|
|
};
|
|
return chain;
|
|
},
|
|
lrange: mock.lrange,
|
|
},
|
|
}));
|
|
|
|
import { readPerformance, recordPerformance } from "./performance-store";
|
|
|
|
const sample = {
|
|
operationId: "op",
|
|
route: "/api/admin/test",
|
|
method: "GET",
|
|
status: 200,
|
|
at: Date.now(),
|
|
streaming: false,
|
|
metrics: {
|
|
durationMs: 1200,
|
|
database: { count: 2, durationMs: 1000, errors: 0 },
|
|
external: { count: 0, durationMs: 0, errors: 0 },
|
|
},
|
|
};
|
|
beforeEach(() => {
|
|
mock.status = "ready";
|
|
mock.exec.mockResolvedValue([]);
|
|
mock.lrange.mockResolvedValue([]);
|
|
});
|
|
it("does not fail a request when storage rejects writes", async () => {
|
|
mock.exec.mockRejectedValue(new Error("offline"));
|
|
expect(() => recordPerformance(sample)).not.toThrow();
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
});
|
|
it("falls back to bounded local samples when Redis is unavailable", async () => {
|
|
mock.status = "end";
|
|
for (let n = 0; n < 250; n++)
|
|
recordPerformance({ ...sample, operationId: String(n) });
|
|
const result = await readPerformance();
|
|
expect(result.shared).toBe(false);
|
|
expect(result.samples).toHaveLength(200);
|
|
expect(result.samples[0].operationId).toBe("249");
|
|
});
|
|
it("ignores expired and malformed shared records", async () => {
|
|
mock.lrange.mockResolvedValue([
|
|
"bad",
|
|
JSON.stringify({ ...sample, at: 1 }),
|
|
JSON.stringify(sample),
|
|
]);
|
|
const result = await readPerformance();
|
|
expect(result.shared).toBe(true);
|
|
expect(result.samples).toEqual([sample]);
|
|
});
|
|
|
|
it("keeps public traffic separate from retained HK operations", async () => {
|
|
mock.status = "end";
|
|
recordPerformance({ ...sample, operationId: "hk-retained" });
|
|
for (let n = 0; n < 230; n++)
|
|
recordPerformance({
|
|
...sample,
|
|
kind: "public-page",
|
|
outcome: "completed",
|
|
operationId: `public-${n}`,
|
|
});
|
|
const admin = await readPerformance();
|
|
const publicPages = await readPerformance("public-page");
|
|
expect(admin.samples[0].operationId).toBe("hk-retained");
|
|
expect(publicPages.samples).toHaveLength(200);
|
|
expect(publicPages.samples[0].operationId).toBe("public-229");
|
|
});
|