返回 DeepSeek-Reasonix
group_queries.test.ts
根目录 / workers / crash-report / src / group_queries.test.ts
1 import { describe, expect, it } from "vitest";
2 // @ts-expect-error Node 22+ provides node:sqlite; Worker production code does not import it.
3 import { DatabaseSync } from "node:sqlite";
4 import { groupReportsStatement, loadD1GroupReports, loadGroupDiagnostics } from "./group_queries";
5 import { renderGroup, type Group } from "./group";
6 import freshSchemaSQL from "../schema.sql?raw";
7
8 describe("group report query bounds", () => {
9 it("returns only the first retained sample and the latest five reports", async () => {
10 const sqlite = new DatabaseSync(":memory:");
11 sqlite.exec(freshSchemaSQL);
12 const fingerprint = "a".repeat(64);
13 for (let id = 1; id <= 35_000; id++) {
14 sqlite.prepare(
15 `INSERT INTO reports (fingerprint, kind, version, arch, os, message, created_at)
16 VALUES (?1, 'crash', 'v1.0.0', 'amd64', 'windows', ?2, ?3)`,
17 ).run(fingerprint, `sample-${id}`, `2026-09-03T00:00:${String(id).padStart(5, "0")}Z`);
18 }
19 sqlite.prepare(
20 `INSERT INTO reports (fingerprint, kind, version, arch, os, message, created_at)
21 VALUES (?1, 'crash', 'v1.0.0', 'amd64', 'windows', 'other-group', '2026-09-30T00:00:00.000Z')`,
22 ).run("b".repeat(64));
23
24 const db = {
25 prepare(sql: string) {
26 const statement = sqlite.prepare(sql);
27 const wrapper: any = {
28 values: [] as unknown[],
29 bind(...values: unknown[]) { wrapper.values = values; return wrapper; },
30 async all() { return { results: statement.all(...wrapper.values) }; },
31 };
32 return wrapper;
33 },
34 } as unknown as D1Database;
35 try {
36 const rows = await groupReportsStatement(db, fingerprint, 5).all();
37 expect(rows.results.map((row) => String((row as { message: string }).message))).toEqual([
38 "sample-35000", "sample-34999", "sample-34998", "sample-34997", "sample-34996", "sample-1",
39 ]);
40 } finally {
41 sqlite.close();
42 }
43 });
44
45 it("renders a graceful notice when a detail query is unavailable", () => {
46 const group: Group = {
47 fingerprint: "c".repeat(64), kind: "crash", count: 35_000,
48 first_seen: "2026-01-01T00:00:00.000Z", last_seen: "2026-09-03T00:00:00.000Z",
49 first_version: "v1.0.0", last_version: "v1.36.0", status: "open", note: "",
50 title: "worker failure", source: "native.lifecycle", label: "", error_type: "Error", severity: "high",
51 top_frame: "", last_os: "windows", last_arch: "amd64", last_build_commit: "",
52 last_channel: "stable", resolved_in: "", resolved_at: "", regressed_at: "",
53 };
54 const html = renderGroup(
55 group,
56 [],
57 { id: 1, email: "admin@example.com", role: "admin", created_at: "", approved_at: null },
58 undefined,
59 undefined,
60 { samplesUnavailable: true, diagnosticsUnavailable: true },
61 );
62 expect(html).toContain("原始样本");
63 expect(html).toContain("技术分布");
64 expect(html).toContain("分组核心汇总仍可用");
65 });
66
67 it("turns D1 detail failures into bounded degradation instead of throwing", async () => {
68 const db = { prepare() { throw new Error("simulated D1 failure"); } } as unknown as D1Database;
69 const env = { DB: db } as any;
70 const observe = () => {};
71 await expect(loadD1GroupReports(env, "a".repeat(64), 5, observe)).resolves.toEqual({ reports: [], unavailable: true });
72 await expect(loadGroupDiagnostics(env, "a".repeat(64), observe)).resolves.toEqual({ unavailable: true });
73 });
74 });
75
75 lines TYPESCRIPT