| 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 worker, { |
| 5 | CLI_TELEMETRY_SCHEMA_SQL, |
| 6 | Report, |
| 7 | } from "./index"; |
| 8 | import { isDevelopmentReport, newestReleaseVersion } from "./report_classification"; |
| 9 | import { |
| 10 | crashGroups, |
| 11 | diagnosticFacets, |
| 12 | diagnosticWindowWhere, |
| 13 | groupDiagnosticSummary, |
| 14 | reportAggregateStatements, |
| 15 | } from "./diagnostics_v2"; |
| 16 | import type { Env } from "./env"; |
| 17 | import diagnosticsMigrationSQL from "../migrate-diagnostics-v2.sql?raw"; |
| 18 | import freshSchemaSQL from "../schema.sql?raw"; |
| 19 | import firebaseCrashMigrationSQL from "../migrate-firebase-crash.sql?raw"; |
| 20 | import firebaseCrashCapacityMigrationSQL from "../migrate-firebase-crash-capacity.sql?raw"; |
| 21 | import { |
| 22 | classifyDiagnosticsV2Schema, |
| 23 | diagnosticsV2SchemaEntries, |
| 24 | diagnosticsV2SchemaQuery, |
| 25 | parseWranglerRows, |
| 26 | } from "../scripts/apply-diagnostics-v2.mjs"; |
| 27 | import { |
| 28 | classifyFirebaseCrashSchema, |
| 29 | firebaseCrashSchemaQuery, |
| 30 | } from "../scripts/apply-firebase-crash.mjs"; |
| 31 | |
| 32 | const oldReport = { |
| 33 | kind: "crash", |
| 34 | version: "v1.19.0", |
| 35 | os: "windows", |
| 36 | arch: "amd64", |
| 37 | message: "legacy payload", |
| 38 | } as const; |
| 39 | |
| 40 | describe("diagnostics v2 compatibility and privacy", () => { |
| 41 | it("fails closed on active partial state and ignores retired metric-user tables", () => { |
| 42 | expect(classifyDiagnosticsV2Schema([]).state).toBe("absent"); |
| 43 | const completeRows = (diagnosticsV2SchemaEntries as readonly string[]).map((entry: string) => { |
| 44 | const split = entry.indexOf(":"); |
| 45 | return { kind: entry.slice(0, split), name: entry.slice(split + 1) }; |
| 46 | }); |
| 47 | expect(classifyDiagnosticsV2Schema(completeRows).state).toBe("complete"); |
| 48 | const partial = classifyDiagnosticsV2Schema(completeRows.slice(1)); |
| 49 | expect(partial.state).toBe("partial"); |
| 50 | expect(partial.missing).toEqual([diagnosticsV2SchemaEntries[0]]); |
| 51 | expect(parseWranglerRows(JSON.stringify([{ results: completeRows }]))).toEqual(completeRows); |
| 52 | expect(classifyDiagnosticsV2Schema([ |
| 53 | ...completeRows, |
| 54 | { kind: "column", name: "metric_users.arch" }, |
| 55 | { kind: "column", name: "cli_metric_users.arch" }, |
| 56 | ]).state).toBe("complete"); |
| 57 | expect(diagnosticsV2SchemaEntries.some((entry) => entry.includes("metric_users"))).toBe(false); |
| 58 | expect(diagnosticsV2SchemaQuery).not.toMatch(/metric_users/); |
| 59 | }); |
| 60 | |
| 61 | it("accepts old reports plus Windows and Linux runtime diagnostics", () => { |
| 62 | expect(Report.safeParse(oldReport).success).toBe(true); |
| 63 | expect(Report.safeParse({ |
| 64 | ...oldReport, |
| 65 | kind: "exception", |
| 66 | schemaVersion: 3, |
| 67 | source: "desktop.session_migration", |
| 68 | label: "transcript.initialization", |
| 69 | errorType: "TranscriptInitializationError", |
| 70 | errorMessage: "Transcript initialization failed during legacy session migration.", |
| 71 | topFrame: "internal/transcript.NewProjection", |
| 72 | fingerprintHint: "desktop.session_migration.transcript_initialization.duplicate_record_identity", |
| 73 | message: "[transcript initialization]\n\nstage: legacy_import\nclassification: duplicate_record_identity", |
| 74 | }).success).toBe(true); |
| 75 | expect(Report.safeParse({ |
| 76 | ...oldReport, |
| 77 | installId: "b".repeat(32), |
| 78 | channel: "stable", |
| 79 | device: { osVersion: "Windows 10", osBuild: 17763, osRevision: 6293 }, |
| 80 | webview2: { |
| 81 | kind: "browser_process_exited", |
| 82 | reason: "integrity_failure", |
| 83 | exitCode: -1073740760, |
| 84 | processDescription: "Browser", |
| 85 | failureSourceModule: "inject.dll", |
| 86 | runtimeVersion: "132.0.2957.140", |
| 87 | gpuDisabled: false, |
| 88 | recovery: "not_applicable", |
| 89 | }, |
| 90 | }).success).toBe(true); |
| 91 | expect(Report.safeParse({ |
| 92 | ...oldReport, |
| 93 | os: "linux", |
| 94 | device: { |
| 95 | distroId: "ubuntu", distroVersion: "24.04", kernelVersion: "6.8.0", |
| 96 | sessionType: "wayland", |
| 97 | }, |
| 98 | webRuntime: { |
| 99 | engine: "webkitgtk", kind: "web_process_terminated", reason: "crashed", |
| 100 | runtimeVersion: "2.44.2", gpuMode: "always", recovery: "reload_succeeded", |
| 101 | }, |
| 102 | }).success).toBe(true); |
| 103 | }); |
| 104 | |
| 105 | it("rejects full failure-source paths and puts channel=test in development", () => { |
| 106 | expect(Report.safeParse({ |
| 107 | ...oldReport, |
| 108 | webview2: { |
| 109 | kind: "browser_process_exited", |
| 110 | reason: "integrity_failure", |
| 111 | failureSourceModule: "C:\\Users\\alice\\inject.dll", |
| 112 | runtimeVersion: "132", |
| 113 | gpuDisabled: false, |
| 114 | recovery: "not_applicable", |
| 115 | }, |
| 116 | }).success).toBe(false); |
| 117 | expect(isDevelopmentReport({ |
| 118 | ...oldReport, |
| 119 | source: "frontend.global", |
| 120 | label: "window.error", |
| 121 | errorType: "Error", |
| 122 | errorMessage: "boom", |
| 123 | topFrame: "at render (assets/index.js:1:2)", |
| 124 | version: "v1.23.0", |
| 125 | channel: "test", |
| 126 | })).toBe(true); |
| 127 | }); |
| 128 | |
| 129 | it("keeps fresh, migrated, and runtime-bootstrap schemas aligned", () => { |
| 130 | const legacy = ` |
| 131 | CREATE TABLE reports (id INTEGER PRIMARY KEY); |
| 132 | CREATE TABLE pings ( |
| 133 | date TEXT NOT NULL, install_id TEXT NOT NULL, version TEXT NOT NULL, os TEXT NOT NULL, |
| 134 | arch TEXT NOT NULL, os_version TEXT NOT NULL DEFAULT '', opens INTEGER NOT NULL DEFAULT 1, |
| 135 | PRIMARY KEY (date, install_id) |
| 136 | ); |
| 137 | CREATE TABLE cli_pings ( |
| 138 | date TEXT NOT NULL, install_id TEXT NOT NULL, version TEXT NOT NULL, os TEXT NOT NULL, |
| 139 | arch TEXT NOT NULL, os_version TEXT NOT NULL DEFAULT '', opens INTEGER NOT NULL DEFAULT 1, |
| 140 | PRIMARY KEY (date, install_id) |
| 141 | ); |
| 142 | `; |
| 143 | const columns = (db: DatabaseSync, table: string) => |
| 144 | db.prepare(`PRAGMA table_info(${table})`).all().map((row: Record<string, unknown>) => String(row.name)); |
| 145 | const fresh = new DatabaseSync(":memory:"); |
| 146 | const migrated = new DatabaseSync(":memory:"); |
| 147 | const runtimeBootstrap = new DatabaseSync(":memory:"); |
| 148 | try { |
| 149 | fresh.exec(freshSchemaSQL); |
| 150 | migrated.exec(legacy); |
| 151 | migrated.exec(diagnosticsMigrationSQL); |
| 152 | runtimeBootstrap.exec(CLI_TELEMETRY_SCHEMA_SQL.join(";\n")); |
| 153 | const additiveColumns: Record<string, string[]> = { |
| 154 | reports: ["webview2", "web_runtime"], |
| 155 | pings: ["os_build", "os_revision", "distro_id", "session_type", "runtime_engine", "gpu_mode"], |
| 156 | cli_pings: ["os_build", "os_revision", "distro_id", "session_type", "runtime_engine", "gpu_mode"], |
| 157 | }; |
| 158 | for (const [table, expected] of Object.entries(additiveColumns)) { |
| 159 | expect(columns(migrated, table)).toEqual(expect.arrayContaining(expected)); |
| 160 | expect(columns(fresh, table)).toEqual(expect.arrayContaining(expected)); |
| 161 | } |
| 162 | expect(columns(fresh, "reports")).not.toContain("install_id"); |
| 163 | for (const table of ["report_daily", "report_installations", "report_event_dimensions", "diagnostics_meta"]) { |
| 164 | expect(columns(migrated, table)).toEqual(columns(fresh, table)); |
| 165 | } |
| 166 | for (const table of ["cli_pings"]) { |
| 167 | expect(columns(runtimeBootstrap, table)).toEqual(columns(fresh, table)); |
| 168 | } |
| 169 | expect(classifyDiagnosticsV2Schema( |
| 170 | migrated.prepare(diagnosticsV2SchemaQuery).all(), |
| 171 | ).state).toBe("complete"); |
| 172 | expect(classifyDiagnosticsV2Schema( |
| 173 | fresh.prepare(diagnosticsV2SchemaQuery).all(), |
| 174 | ).state).toBe("complete"); |
| 175 | } finally { |
| 176 | fresh.close(); |
| 177 | migrated.close(); |
| 178 | runtimeBootstrap.close(); |
| 179 | } |
| 180 | expect(diagnosticsMigrationSQL).not.toMatch(/\bDROP\b/); |
| 181 | expect(diagnosticsMigrationSQL).not.toMatch(/ALTER TABLE (?:metric_users|cli_metric_users)\b/); |
| 182 | }); |
| 183 | |
| 184 | it("uses a date-leading index for platform impact denominators", () => { |
| 185 | const db = new DatabaseSync(":memory:"); |
| 186 | try { |
| 187 | db.exec(freshSchemaSQL); |
| 188 | const plan = db.prepare( |
| 189 | "EXPLAIN QUERY PLAN SELECT COUNT(DISTINCT install_id) FROM pings WHERE date >= date('now', '-29 day') AND os = 'linux' AND distro_id = 'ubuntu'", |
| 190 | ).all().map((row: Record<string, unknown>) => String(row.detail)).join("\n"); |
| 191 | expect(plan).toMatch(/SEARCH pings USING INDEX/); |
| 192 | expect(plan).not.toMatch(/SCAN pings/); |
| 193 | } finally { |
| 194 | db.close(); |
| 195 | } |
| 196 | }); |
| 197 | |
| 198 | it("keeps the Firebase outbox migration additive and aligned with fresh installs", () => { |
| 199 | const migrated = new DatabaseSync(":memory:"); |
| 200 | const fresh = new DatabaseSync(":memory:"); |
| 201 | try { |
| 202 | migrated.exec("CREATE TABLE groups (fingerprint TEXT PRIMARY KEY, last_seen TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'open')"); |
| 203 | migrated.exec(firebaseCrashMigrationSQL); |
| 204 | expect(classifyFirebaseCrashSchema(migrated.prepare(firebaseCrashSchemaQuery).all()).state).toBe("partial"); |
| 205 | migrated.exec(firebaseCrashCapacityMigrationSQL); |
| 206 | fresh.exec(freshSchemaSQL); |
| 207 | expect(classifyFirebaseCrashSchema(migrated.prepare(firebaseCrashSchemaQuery).all()).state).toBe("complete"); |
| 208 | expect(classifyFirebaseCrashSchema(fresh.prepare(firebaseCrashSchemaQuery).all()).state).toBe("complete"); |
| 209 | expect(firebaseCrashMigrationSQL).not.toMatch(/\b(?:DROP|ALTER)\b/); |
| 210 | expect(firebaseCrashCapacityMigrationSQL).not.toMatch(/\b(?:DROP|ALTER)\b/); |
| 211 | } finally { |
| 212 | migrated.close(); |
| 213 | fresh.close(); |
| 214 | } |
| 215 | }); |
| 216 | }); |
| 217 | |
| 218 | describe("stats window and release baseline", () => { |
| 219 | it("uses an inclusive calendar window for diagnostic groups", () => { |
| 220 | expect(diagnosticWindowWhere(7)).toBe("date(last_seen) >= date('now', '-6 day')"); |
| 221 | expect(diagnosticWindowWhere(30)).toBe("date(last_seen) >= date('now', '-29 day')"); |
| 222 | }); |
| 223 | |
| 224 | it("does not promote prerelease or synthetic non-semver labels", () => { |
| 225 | expect(newestReleaseVersion(["v1.19.4", "v1.20.0-beta.1", "dev", "v9.9.9-test"])).toBe("v1.19.4"); |
| 226 | }); |
| 227 | }); |
| 228 | |
| 229 | describe("diagnostics v2 storage consistency", () => { |
| 230 | it("commits every report write through one D1 batch", async () => { |
| 231 | let batchCalls = 0; |
| 232 | let directRuns = 0; |
| 233 | let committed: Array<{ sql: string }> = []; |
| 234 | const db = { |
| 235 | prepare(sql: string) { |
| 236 | const statement = { |
| 237 | sql, |
| 238 | bind() { return statement; }, |
| 239 | async first() { return null; }, |
| 240 | async run() { directRuns++; return {}; }, |
| 241 | }; |
| 242 | return statement; |
| 243 | }, |
| 244 | async batch(statements: Array<{ sql: string }>) { |
| 245 | batchCalls++; |
| 246 | committed = statements; |
| 247 | return []; |
| 248 | }, |
| 249 | } as unknown as D1Database; |
| 250 | const env = { |
| 251 | DB: db, |
| 252 | RATE_LIMITER: { async limit() { return { success: true }; } }, |
| 253 | } as unknown as Env; |
| 254 | const body = JSON.stringify({ |
| 255 | installId: "a".repeat(32), kind: "crash", version: "v1.23.0", |
| 256 | os: "windows", arch: "amd64", message: "browser process exited", |
| 257 | }); |
| 258 | const response = await worker.fetch(new Request("https://crash.reasonix.io/v1/report", { |
| 259 | method: "POST", |
| 260 | headers: { |
| 261 | "content-type": "application/json", |
| 262 | "content-length": String(new TextEncoder().encode(body).byteLength), |
| 263 | "cf-connecting-ip": "127.0.0.1", |
| 264 | }, |
| 265 | body, |
| 266 | }), env); |
| 267 | expect(response.status).toBe(202); |
| 268 | expect(batchCalls).toBe(1); |
| 269 | expect(directRuns).toBe(0); |
| 270 | expect(committed.map((statement) => statement.sql)).toEqual([ |
| 271 | expect.stringContaining("INSERT INTO report_events"), |
| 272 | expect.stringContaining("automatic_regression"), |
| 273 | expect.stringContaining("INSERT INTO groups"), |
| 274 | expect.stringContaining("INSERT INTO reports"), |
| 275 | expect.stringContaining("INSERT INTO report_daily"), |
| 276 | expect.stringContaining("INSERT INTO report_installations"), |
| 277 | expect.stringContaining("INSERT INTO report_event_dimensions"), |
| 278 | expect.stringContaining("INSERT INTO report_attribution_daily"), |
| 279 | expect.stringContaining("DELETE FROM reports"), |
| 280 | ]); |
| 281 | expect(committed.every((statement) => !statement.sql.includes("firebase_crash_"))).toBe(true); |
| 282 | }); |
| 283 | |
| 284 | it("projects a repeated D1 eventId only once", async () => { |
| 285 | const projected = new Set<string>(); |
| 286 | let batchCalls = 0; |
| 287 | const db = { |
| 288 | prepare(sql: string) { |
| 289 | let binds: unknown[] = []; |
| 290 | const statement = { |
| 291 | sql, |
| 292 | bind(...values: unknown[]) { binds = values; return statement; }, |
| 293 | async first() { |
| 294 | return sql.includes("SELECT event_id FROM report_events") && projected.has(String(binds[0])) |
| 295 | ? { event_id: binds[0] } |
| 296 | : null; |
| 297 | }, |
| 298 | async run() { return {}; }, |
| 299 | binds() { return binds; }, |
| 300 | }; |
| 301 | return statement; |
| 302 | }, |
| 303 | async batch(statements: Array<{ sql: string; binds(): unknown[] }>) { |
| 304 | batchCalls++; |
| 305 | const event = statements.find((statement) => statement.sql.includes("INSERT INTO report_events")); |
| 306 | if (event) projected.add(String(event.binds()[0])); |
| 307 | return []; |
| 308 | }, |
| 309 | } as unknown as D1Database; |
| 310 | const env = { |
| 311 | DB: db, |
| 312 | RATE_LIMITER: { async limit() { return { success: true }; } }, |
| 313 | } as unknown as Env; |
| 314 | const body = JSON.stringify({ |
| 315 | eventId: "e".repeat(32), installId: "a".repeat(32), kind: "crash", |
| 316 | version: "v1.23.0", os: "windows", arch: "amd64", message: "same event", |
| 317 | }); |
| 318 | const request = () => new Request("https://crash.reasonix.io/v1/report", { |
| 319 | method: "POST", |
| 320 | headers: { |
| 321 | "content-type": "application/json", |
| 322 | "content-length": String(new TextEncoder().encode(body).byteLength), |
| 323 | "cf-connecting-ip": "127.0.0.1", |
| 324 | }, |
| 325 | body, |
| 326 | }); |
| 327 | |
| 328 | expect((await worker.fetch(request(), env)).status).toBe(202); |
| 329 | expect((await worker.fetch(request(), env)).status).toBe(202); |
| 330 | expect(batchCalls).toBe(1); |
| 331 | expect(projected).toEqual(new Set(["e".repeat(32)])); |
| 332 | }); |
| 333 | |
| 334 | it("preserves separate GPU and runtime event dimensions for one installation", () => { |
| 335 | type BoundStatement = { sql: string; binds: unknown[] }; |
| 336 | const statements: BoundStatement[] = []; |
| 337 | const d1 = { |
| 338 | prepare(sql: string) { |
| 339 | const statement = { |
| 340 | sql, |
| 341 | binds: [] as unknown[], |
| 342 | bind(...binds: unknown[]) { statement.binds = binds; statements.push(statement); return statement; }, |
| 343 | }; |
| 344 | return statement; |
| 345 | }, |
| 346 | } as unknown as D1Database; |
| 347 | const report = { |
| 348 | installId: "a".repeat(32), version: "v1.23.0", os: "windows", arch: "amd64", |
| 349 | device: { osBuild: 17763, osRevision: 6293 }, |
| 350 | }; |
| 351 | const webview = { |
| 352 | engine: "webview2", |
| 353 | runtimeVersion: "132", kind: "gpu_process_exited", reason: "unexpected", |
| 354 | exitCode: 1, recovery: "not_applicable", gpuMode: "enabled", |
| 355 | }; |
| 356 | reportAggregateStatements(d1, report, "f".repeat(64), "stable", webview); |
| 357 | reportAggregateStatements(d1, report, "f".repeat(64), "stable", { |
| 358 | ...webview, runtimeVersion: "133", gpuMode: "disabled", |
| 359 | }); |
| 360 | const facts = statements.filter((statement) => statement.sql.includes("INSERT INTO report_event_dimensions")); |
| 361 | const db = new DatabaseSync(":memory:"); |
| 362 | try { |
| 363 | db.exec(freshSchemaSQL); |
| 364 | for (const fact of facts) db.prepare(fact.sql).run(...fact.binds as []); |
| 365 | expect(db.prepare( |
| 366 | "SELECT runtime_version, gpu_mode, events FROM report_event_dimensions ORDER BY runtime_version", |
| 367 | ).all()).toEqual([ |
| 368 | { runtime_version: "132", gpu_mode: "enabled", events: 1 }, |
| 369 | { runtime_version: "133", gpu_mode: "disabled", events: 1 }, |
| 370 | ]); |
| 371 | } finally { |
| 372 | db.close(); |
| 373 | } |
| 374 | }); |
| 375 | |
| 376 | it("preserves unidentified event dimensions without inventing an affected installation", () => { |
| 377 | type BoundStatement = { sql: string; binds: unknown[] }; |
| 378 | const statements: BoundStatement[] = []; |
| 379 | const d1 = { |
| 380 | prepare(sql: string) { |
| 381 | const statement = { |
| 382 | sql, |
| 383 | binds: [] as unknown[], |
| 384 | bind(...binds: unknown[]) { statement.binds = binds; statements.push(statement); return statement; }, |
| 385 | }; |
| 386 | return statement; |
| 387 | }, |
| 388 | } as unknown as D1Database; |
| 389 | reportAggregateStatements(d1, { |
| 390 | version: "v1.23.0", os: "linux", arch: "amd64", |
| 391 | device: { distroId: "ubuntu", distroVersion: "24.04", sessionType: "wayland" }, |
| 392 | }, "f".repeat(64), "stable", { |
| 393 | engine: "webkitgtk", runtimeVersion: "2.44", kind: "web_process_terminated", reason: "crashed", |
| 394 | recovery: "reload_failed", gpuMode: "always", |
| 395 | }); |
| 396 | const db = new DatabaseSync(":memory:"); |
| 397 | try { |
| 398 | db.exec(freshSchemaSQL); |
| 399 | for (const statement of statements) db.prepare(statement.sql).run(...statement.binds as []); |
| 400 | expect(db.prepare("SELECT events, identified_events FROM report_daily").get()).toEqual({ |
| 401 | events: 1, identified_events: 0, |
| 402 | }); |
| 403 | expect(db.prepare( |
| 404 | "SELECT install_id, distro_id, recovery, events FROM report_event_dimensions", |
| 405 | ).get()).toEqual({ install_id: "", distro_id: "ubuntu", recovery: "reload_failed", events: 1 }); |
| 406 | expect(db.prepare("SELECT COUNT(*) AS count FROM report_installations").get()).toEqual({ count: 0 }); |
| 407 | } finally { |
| 408 | db.close(); |
| 409 | } |
| 410 | }); |
| 411 | |
| 412 | it("uses the same dimensions for filtered events, identity coverage, and installations", async () => { |
| 413 | let querySQL = ""; |
| 414 | let queryBinds: unknown[] = []; |
| 415 | const row = { |
| 416 | fingerprint: "f".repeat(64), status: "open", severity: "high", regressed_at: "", |
| 417 | first_version: "v1.23.0", count: 2, seen: "2026-08-10", title: "renderer exited", |
| 418 | last_version: "v1.23.0", last_channel: "stable", affected_installs: 1, |
| 419 | window_events: 2, identified_events: 1, active_build_installs: 10, |
| 420 | dimension_base_installs: 10, dimension_covered_installs: 10, kind: "exception", |
| 421 | source: "web.runtime.native", label: "renderer_process_exited", error_type: "", top_frame: "", |
| 422 | last_os: "windows", last_arch: "amd64", |
| 423 | }; |
| 424 | const db = { |
| 425 | prepare(sql: string) { |
| 426 | querySQL = sql; |
| 427 | const statement = { |
| 428 | bind(...binds: unknown[]) { queryBinds = binds; return statement; }, |
| 429 | async all() { return { results: [row] }; }, |
| 430 | }; |
| 431 | return statement; |
| 432 | }, |
| 433 | } as unknown as D1Database; |
| 434 | const result = await crashGroups({ DB: db } as unknown as Env, { |
| 435 | status: "", source: "", version: "", os: "", platform: "", osBuild: "17763", arch: "", |
| 436 | channel: "", runtimeVersion: "", failureKind: "", failureReason: "", recovery: "", gpu: "", |
| 437 | newLatest: false, regressed: false, windowDays: 7, |
| 438 | }, ""); |
| 439 | expect(querySQL).toContain("COUNT(DISTINCT NULLIF(install_id, '')) AS affected_installs"); |
| 440 | expect(querySQL).toContain("SUM(events) AS window_events"); |
| 441 | expect(querySQL).toContain("SUM(CASE WHEN install_id <> '' THEN events ELSE 0 END) AS identified_events"); |
| 442 | expect(querySQL).toContain("os_build = ?"); |
| 443 | expect(querySQL).toContain("COALESCE(diagnostics.window_events, 0) > 0"); |
| 444 | expect(querySQL).not.toContain("FROM report_daily WHERE"); |
| 445 | expect(queryBinds).toContain(17763); |
| 446 | expect(result.results[0]).toMatchObject({ identity_coverage: 0.5, impact_rate: 0.1 }); |
| 447 | }); |
| 448 | |
| 449 | it("orders the SQL limit and returned groups by affected installations first", async () => { |
| 450 | let querySQL = ""; |
| 451 | const row = (fingerprint: string, severity: string, affectedInstalls: number) => ({ |
| 452 | fingerprint, status: "open", severity, regressed_at: "", first_version: "v1.23.0", |
| 453 | count: 100, seen: "2026-08-10", title: "browser process exited", last_version: "v1.23.0", |
| 454 | last_channel: "stable", affected_installs: affectedInstalls, window_events: 100, |
| 455 | identified_events: 100, active_build_installs: 0, kind: "crash", source: "desktop.webview2", |
| 456 | label: "browser_process_exited", error_type: "", top_frame: "", last_os: "windows", last_arch: "amd64", |
| 457 | }); |
| 458 | const db = { |
| 459 | prepare(sql: string) { |
| 460 | querySQL = sql; |
| 461 | return { async all() { return { results: [row("b".repeat(64), "critical", 1), row("a".repeat(64), "low", 20)] }; } }; |
| 462 | }, |
| 463 | } as unknown as D1Database; |
| 464 | const result = await crashGroups({ DB: db } as unknown as Env, { |
| 465 | status: "", source: "", version: "", os: "", platform: "", osBuild: "", arch: "", channel: "", |
| 466 | runtimeVersion: "", failureKind: "", failureReason: "", recovery: "", gpu: "", |
| 467 | newLatest: false, regressed: false, windowDays: 7, |
| 468 | }, ""); |
| 469 | expect(querySQL).toContain("FROM report_daily"); |
| 470 | expect(querySQL.indexOf("affected_installs DESC")).toBeLessThan(querySQL.indexOf("CASE WHEN status = 'open'")); |
| 471 | expect(result.results.map((group) => group.fingerprint)).toEqual(["a".repeat(64), "b".repeat(64)]); |
| 472 | }); |
| 473 | |
| 474 | it("qualifies the development fingerprint in the joined diagnostics query", async () => { |
| 475 | const sqlite = new DatabaseSync(":memory:"); |
| 476 | try { |
| 477 | sqlite.exec(freshSchemaSQL); |
| 478 | const db = { |
| 479 | prepare(sql: string) { |
| 480 | const statement = sqlite.prepare(sql); |
| 481 | return { async all() { return { results: statement.all() }; } }; |
| 482 | }, |
| 483 | } as unknown as D1Database; |
| 484 | await expect(crashGroups({ DB: db } as unknown as Env, { |
| 485 | status: "", source: "", version: "", os: "", platform: "", osBuild: "", arch: "", channel: "", |
| 486 | runtimeVersion: "", failureKind: "", failureReason: "", recovery: "", gpu: "", |
| 487 | newLatest: false, regressed: false, windowDays: 30, |
| 488 | }, "")).resolves.toMatchObject({ results: [] }); |
| 489 | } finally { |
| 490 | sqlite.close(); |
| 491 | } |
| 492 | }); |
| 493 | |
| 494 | it("shares the unfiltered ping denominator across diagnostics metrics", async () => { |
| 495 | let querySQL = ""; |
| 496 | const db = { |
| 497 | prepare(sql: string) { |
| 498 | querySQL = sql; |
| 499 | return { async all() { return { results: [] }; } }; |
| 500 | }, |
| 501 | } as unknown as D1Database; |
| 502 | await crashGroups({ DB: db } as unknown as Env, { |
| 503 | status: "", source: "", version: "", os: "", platform: "", osBuild: "", arch: "", channel: "", |
| 504 | runtimeVersion: "", failureKind: "", failureReason: "", recovery: "", gpu: "", |
| 505 | newLatest: false, regressed: false, windowDays: 30, |
| 506 | }, ""); |
| 507 | expect(querySQL.match(/FROM pings WHERE/g)).toHaveLength(1); |
| 508 | expect(querySQL).toContain("CROSS JOIN (SELECT COUNT(DISTINCT install_id) AS installs FROM pings"); |
| 509 | }); |
| 510 | |
| 511 | it("caches diagnostic facets per D1 binding and reports query timing labels", async () => { |
| 512 | let prepareCalls = 0; |
| 513 | const observations: string[] = []; |
| 514 | const db = { |
| 515 | prepare() { |
| 516 | prepareCalls++; |
| 517 | return { async all() { return { results: [] }; } }; |
| 518 | }, |
| 519 | } as unknown as D1Database; |
| 520 | const env = { DB: db } as unknown as Env; |
| 521 | await diagnosticFacets(env, 30, (label) => observations.push(label)); |
| 522 | const firstCallCount = prepareCalls; |
| 523 | await diagnosticFacets(env, 30, (label) => observations.push(label)); |
| 524 | expect(firstCallCount).toBe(17); |
| 525 | expect(prepareCalls).toBe(firstCallCount); |
| 526 | expect(observations).toContain("diagnostic_facets.cache"); |
| 527 | }); |
| 528 | |
| 529 | it("uses daily crash rollups when no diagnostic dimensions are filtered", async () => { |
| 530 | const sqlite = new DatabaseSync(":memory:"); |
| 531 | sqlite.exec(freshSchemaSQL); |
| 532 | const fingerprint = "a".repeat(64); |
| 533 | sqlite.prepare( |
| 534 | `INSERT INTO groups (fingerprint, kind, count, first_seen, last_seen, first_version, last_version, title) |
| 535 | VALUES (?1, 'crash', 4, datetime('now'), datetime('now'), 'v1.0.0', 'v1.0.0', 'boom')`, |
| 536 | ).run(fingerprint); |
| 537 | sqlite.prepare( |
| 538 | `INSERT INTO report_daily (date, fingerprint, events, identified_events) |
| 539 | VALUES (date('now'), ?1, 4, 3)`, |
| 540 | ).run(fingerprint); |
| 541 | sqlite.prepare( |
| 542 | `INSERT INTO report_installations (date, fingerprint, install_id, version, os, arch) |
| 543 | VALUES (date('now'), ?1, 'install-1', 'v1.0.0', 'windows', 'amd64')`, |
| 544 | ).run(fingerprint); |
| 545 | const d1 = { |
| 546 | prepare(sql: string) { |
| 547 | const statement = sqlite.prepare(sql); |
| 548 | const wrapper: any = { |
| 549 | bind(...values: unknown[]) { wrapper.values = values; return wrapper; }, |
| 550 | values: [] as unknown[], |
| 551 | async all() { return { results: statement.all(...wrapper.values) }; }, |
| 552 | }; |
| 553 | return wrapper; |
| 554 | }, |
| 555 | } as unknown as D1Database; |
| 556 | try { |
| 557 | const result = await crashGroups({ DB: d1 } as unknown as Env, { |
| 558 | status: "", source: "", version: "", os: "", platform: "", osBuild: "", arch: "", channel: "", |
| 559 | runtimeVersion: "", failureKind: "", failureReason: "", recovery: "", gpu: "", |
| 560 | newLatest: false, regressed: false, windowDays: 30, |
| 561 | }, ""); |
| 562 | expect(result.results[0]).toMatchObject({ |
| 563 | fingerprint, affected_installs: 1, window_events: 4, identified_events: 3, |
| 564 | }); |
| 565 | } finally { |
| 566 | sqlite.close(); |
| 567 | } |
| 568 | }); |
| 569 | |
| 570 | it("materializes one group detail window for all diagnostic distributions", async () => { |
| 571 | let distributionSQL = ""; |
| 572 | const db = { |
| 573 | prepare(sql: string) { |
| 574 | if (sql.includes("WITH window AS MATERIALIZED")) distributionSQL = sql; |
| 575 | const statement = { |
| 576 | bind() { return statement; }, |
| 577 | async first() { return { window_events: 1, identified_events: 1, affected_installs: 1 }; }, |
| 578 | async all() { return { results: [] }; }, |
| 579 | }; |
| 580 | return statement; |
| 581 | }, |
| 582 | } as unknown as D1Database; |
| 583 | await groupDiagnosticSummary({ DB: db } as unknown as Env, "b".repeat(64)); |
| 584 | expect(distributionSQL).toContain("WITH window AS MATERIALIZED"); |
| 585 | expect(distributionSQL.match(/FROM report_event_dimensions/g)).toHaveLength(1); |
| 586 | expect(distributionSQL).toContain("ROW_NUMBER() OVER (PARTITION BY facet"); |
| 587 | expect(distributionSQL).toContain("WHERE rank <= 20"); |
| 588 | }); |
| 589 | |
| 590 | it("bounds each group facet and reads totals from the daily rollups", async () => { |
| 591 | const sqlite = new DatabaseSync(":memory:"); |
| 592 | sqlite.exec(freshSchemaSQL); |
| 593 | const fingerprint = "c".repeat(64); |
| 594 | sqlite.prepare( |
| 595 | `INSERT INTO report_daily (date, fingerprint, events, identified_events) |
| 596 | VALUES (date('now'), ?1, 40, 40)`, |
| 597 | ).run(fingerprint); |
| 598 | sqlite.prepare( |
| 599 | `INSERT INTO report_installations (date, fingerprint, install_id, version, os, arch) |
| 600 | VALUES (date('now'), ?1, 'install-1', 'v1.0.0', 'windows', 'amd64')`, |
| 601 | ).run(fingerprint); |
| 602 | const fact = sqlite.prepare( |
| 603 | `INSERT INTO report_event_dimensions |
| 604 | (date, fingerprint, install_id, version, os, arch, os_build, os_revision, |
| 605 | distro_id, distro_version, kernel_version, session_type, channel, |
| 606 | runtime_engine, runtime_version, failure_kind, failure_reason, exit_code, recovery, gpu_mode, events) |
| 607 | VALUES (date('now'), ?1, ?2, 'v1.0.0', 'windows', 'amd64', 1, 1, '', '', '', '', 'stable', |
| 608 | 'webview2', ?3, 'browser_process_exited', 'unexpected', '1', '', 'unknown', 1)`, |
| 609 | ); |
| 610 | for (let i = 0; i < 40; i++) fact.run(fingerprint, `install-${i}`, `runtime-${i}`); |
| 611 | const db = { |
| 612 | prepare(sql: string) { |
| 613 | const statement = sqlite.prepare(sql); |
| 614 | const wrapper: any = { |
| 615 | values: [] as unknown[], |
| 616 | bind(...values: unknown[]) { wrapper.values = values; return wrapper; }, |
| 617 | async first() { return statement.get(...wrapper.values); }, |
| 618 | async all() { return { results: statement.all(...wrapper.values) }; }, |
| 619 | }; |
| 620 | return wrapper; |
| 621 | }, |
| 622 | } as unknown as D1Database; |
| 623 | try { |
| 624 | const result = await groupDiagnosticSummary({ DB: db } as unknown as Env, fingerprint); |
| 625 | expect(result).toMatchObject({ windowEvents: 40, identifiedEvents: 40, affectedInstalls: 1 }); |
| 626 | const byFacet = new Map<string, number>(); |
| 627 | for (const row of result.distributions) byFacet.set(row.facet, (byFacet.get(row.facet) ?? 0) + 1); |
| 628 | expect(Math.max(...byFacet.values())).toBeLessThanOrEqual(20); |
| 629 | } finally { |
| 630 | sqlite.close(); |
| 631 | } |
| 632 | }); |
| 633 | }); |
| 634 |