| 1 | import { describe, expect, it } from "vitest"; |
| 2 | import { Timing, timedEnv } from "../../src/lib/timing.js"; |
| 3 | import type { Env } from "../../src/env.js"; |
| 4 | |
| 5 | class Statement { |
| 6 | constructor(readonly sql: string, readonly params: unknown[] = []) {} |
| 7 | bind(...params: unknown[]) { |
| 8 | return new Statement(this.sql, params); |
| 9 | } |
| 10 | async first() { |
| 11 | return { sql: this.sql, params: this.params }; |
| 12 | } |
| 13 | async all() { |
| 14 | return { results: [], meta: { served_by_region: "WEUR", served_by_primary: true } }; |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | const fakeDb = (batched: unknown[][]) => ({ |
| 19 | prepare: (q: string) => new Statement(q), |
| 20 | batch: async (s: Statement[]) => (batched.push(s), s.map(() => ({ results: [] }))), |
| 21 | }); |
| 22 | |
| 23 | const fakeNamespace = () => ({ |
| 24 | idFromName: (n: string) => ({ name: n }), |
| 25 | get: (id: { name: string }) => ({ id, name: id.name, info: async (x: number) => x * 2, fetch: async () => new Response("ok") }), |
| 26 | }); |
| 27 | |
| 28 | describe("Server-Timing", () => { |
| 29 | it("counts D1 queries and Durable Object calls without changing their results", async () => { |
| 30 | const t = new Timing(); |
| 31 | const batched: unknown[][] = []; |
| 32 | const env = timedEnv({ DB: fakeDb(batched), REPO: fakeNamespace() } as unknown as Env, t); |
| 33 | const row = await env.DB.prepare("SELECT ?").bind(1).first(); |
| 34 | expect(row).toEqual({ sql: "SELECT ?", params: [1] }); |
| 35 | await env.DB.prepare("SELECT 2").all(); |
| 36 | // Batches still get the real statements. |
| 37 | const s = env.DB.prepare("SELECT 3"); |
| 38 | await env.DB.batch([s]); |
| 39 | expect(batched[0]![0]).toBeInstanceOf(Statement); |
| 40 | const stub = env.REPO.get(env.REPO.idFromName("r")) as unknown as { name: string; info(x: number): Promise<number>; fetch(): Promise<Response> }; |
| 41 | expect(stub.name).toBe("r"); |
| 42 | expect(await stub.info(21)).toBe(42); |
| 43 | expect(await (await stub.fetch()).text()).toBe("ok"); |
| 44 | await Promise.resolve(); |
| 45 | const h = t.header("IST"); |
| 46 | expect(h).toMatch(/^total;dur=\d+, /); |
| 47 | expect(h).toMatch(/d1;dur=\d+;desc="3 queries, WEUR primary"/); |
| 48 | expect(h).toMatch(/do;dur=\d+;desc="2 calls"/); |
| 49 | expect(h).toContain('colo;desc="IST"'); |
| 50 | expect(t.header("not a colo")).not.toContain("colo"); |
| 51 | }); |
| 52 | |
| 53 | it("leaves missing bindings alone", () => { |
| 54 | const env = timedEnv({} as Env, new Timing()); |
| 55 | expect(env.DB).toBeUndefined(); |
| 56 | expect(env.REPO).toBeUndefined(); |
| 57 | }); |
| 58 | }); |