feat: harden analytics dashboard

This commit is contained in:
2026-05-27 15:19:31 +08:00
parent 5e0ca6a504
commit 356039d9cf
34 changed files with 1424 additions and 879 deletions

View File

@@ -16,7 +16,7 @@ mock.module("./db", () => ({
query: queryMock,
}));
const { getTrends, getUserDetail } = await import("./queries");
const { getLogs, getTrends, getUserDetail } = await import("./queries");
describe("getTrends", () => {
beforeEach(() => {
@@ -123,3 +123,38 @@ describe("getUserDetail", () => {
expect(queryMock.mock.calls.some(([sql]) => String(sql).includes("token_name"))).toBe(true);
});
});
describe("getLogs", () => {
beforeEach(() => {
queryMock.mockClear();
queryMock.mockImplementation(async (sql: string) => {
if (sql.includes("COUNT(*)::int as total")) {
return [{ total: 0 }];
}
if (sql.includes("SELECT id, display_name FROM users")) {
return [];
}
if (sql.includes("SELECT id, name FROM channels")) {
return [];
}
return [];
});
});
test("caps page size for direct query callers", async () => {
const result = await getLogs({ page: 1, pageSize: 10000 });
const dataQuery = queryMock.mock.calls.find(([sql]) => String(sql).includes("ORDER BY id DESC"));
expect(result.page_size).toBe(200);
expect(dataQuery?.[1]).toEqual([200, 0]);
});
test("normalizes non-finite direct pagination inputs", async () => {
const result = await getLogs({ page: Number.NaN, pageSize: Number.POSITIVE_INFINITY });
const dataQuery = queryMock.mock.calls.find(([sql]) => String(sql).includes("ORDER BY id DESC"));
expect(result.page).toBe(1);
expect(result.page_size).toBe(100);
expect(dataQuery?.[1]).toEqual([100, 0]);
});
});