import assert from "node:assert/strict"; import test from "node:test"; import type { Pool } from "pg"; import { clearLocalPostgresMetadataCacheForTests, closeLocalPostgresDataPool, createLocalPostgresDataClient, installLocalPostgresPoolForTests, setLocalPostgresMetadataClockForTests, type LocalDatabaseRole, } from "../src/lib/db/local-postgres-client-core.ts"; type QueryCall = { sql: string; params: readonly unknown[] }; function tag(sql: string): string { const compact = sql.replace(/\s+/g, " ").trim().toLowerCase(); if (compact === "begin") return "begin"; if (compact === "commit") return "commit"; if (compact === "rollback") return "rollback"; if (compact.startsWith("set local role")) return "set local role"; if (compact.includes("set_config('role'")) return "set_config(role)"; if (compact.includes("set_config('request.jwt.claim.sub'")) return "set_config"; if (compact.includes("information_schema.columns")) return "information_schema"; if (compact.includes("from pg_index")) return "primary_key"; if (compact.includes("from pg_proc")) return "pg_proc"; return "business"; } function installFakePool( url: string, options: { columns?: ReadonlyArray<{ column_name: string; udt_name: string }> | null; primaryKeys?: readonly string[] | null; functions?: ReadonlyArray> | null; failBusinessCode?: () => string | null; } = {}, ): QueryCall[] { const calls: QueryCall[] = []; const client = { async query(sql: string, params: readonly unknown[] = []) { calls.push({ sql, params }); const label = tag(sql); if (label === "information_schema") { const rows = options.columns === null ? [] : [...(options.columns ?? [{ column_name: "id", udt_name: "uuid" }])]; return { rows, rowCount: rows.length }; } if (label === "primary_key") { const names = options.primaryKeys === null ? [] : [...(options.primaryKeys ?? ["id"])]; const rows = names.map((column_name) => ({ column_name })); return { rows, rowCount: rows.length }; } if (label === "pg_proc") { const rows = options.functions === null ? [] : [...(options.functions ?? [{ proretset: false, return_type: "text", argument_names: ["left_name", "right_name"], argument_types: ["text", "text"], }])]; return { rows, rowCount: rows.length }; } if (label === "business") { const code = options.failBusinessCode?.() ?? null; if (code) { const error = new Error("business sql failed") as Error & { code: string }; error.code = code; throw error; } if (sql.toLowerCase().includes(" as value")) return { rows: [{ value: "ok" }], rowCount: 1 }; } return { rows: [], rowCount: 0 }; }, release() {}, }; const pool = { async connect() { return client; }, async end() {}, }; installLocalPostgresPoolForTests(url, pool as unknown as Pool); return calls; } async function withSwitches( values: Record, run: () => Promise, ): Promise { const previous = new Map(); for (const [key, value] of Object.entries(values)) { previous.set(key, process.env[key]); if (value === undefined) delete process.env[key]; else process.env[key] = value; } clearLocalPostgresMetadataCacheForTests(); setLocalPostgresMetadataClockForTests(null); try { await run(); } finally { for (const [key, value] of previous) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } clearLocalPostgresMetadataCacheForTests(); setLocalPostgresMetadataClockForTests(null); } } const identity = { id: "018f4e6d-7a11-7000-8000-0000000000aa", email: "person@example.com" }; function clientFor(url: string, role: LocalDatabaseRole | null = "authenticated") { return createLocalPostgresDataClient(url, role === "service_role" ? null : identity, role); } test("metadata cache and merged session setup stay off the baseline query sequence", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "off", JYOTISHA_PG_MERGED_SESSION_SETUP: "off", }, async () => { const url = "postgresql://roundtrip-off@127.0.0.1:1/jyotisha"; const calls = installFakePool(url); const client = clientFor(url); const first = await client.from("profiles").select("id").eq("id", identity.id); assert.equal(first.error, null); assert.deepEqual(calls.map((call) => tag(call.sql)), [ "begin", "set local role", "set_config", "information_schema", "business", "commit", ]); await client.from("profiles").select("id").eq("id", identity.id); assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 2); }); }); test("cached metadata and merged session setup drop a repeat select and rpc to four roundtrips", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "on", JYOTISHA_PG_MERGED_SESSION_SETUP: "on", }, async () => { const url = "postgresql://roundtrip-on@127.0.0.1:1/jyotisha"; const calls = installFakePool(url); const client = clientFor(url); await client.from("profiles").select("id").eq("id", identity.id); calls.length = 0; await client.from("profiles").select("id").eq("id", identity.id); assert.deepEqual(calls.map((call) => tag(call.sql)), [ "begin", "set_config(role)", "business", "commit", ]); await client.rpc("sample_probe", { left_name: "a", right_name: "b" }); calls.length = 0; const rpc = await client.rpc("sample_probe", { left_name: "a", right_name: "b" }); assert.equal(rpc.error, null); assert.deepEqual(calls.map((call) => tag(call.sql)), [ "begin", "set_config(role)", "business", "commit", ]); await client.from("profiles").upsert({ id: identity.id }); calls.length = 0; await client.from("profiles").upsert({ id: identity.id }); assert.deepEqual(calls.map((call) => tag(call.sql)), [ "begin", "set_config(role)", "business", "commit", ]); }); }); test("column cache is per role and function cache is per argument names", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "on", JYOTISHA_PG_MERGED_SESSION_SETUP: "on", }, async () => { const url = "postgresql://roundtrip-keys@127.0.0.1:1/jyotisha"; const calls = installFakePool(url); const authenticated = clientFor(url, "authenticated"); const service = clientFor(url, "service_role"); await authenticated.from("profiles").select("id"); calls.length = 0; await service.from("profiles").select("id"); assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1); await authenticated.rpc("sample_probe", { left_name: "a" }); calls.length = 0; await authenticated.rpc("sample_probe", { right_name: "b" }); assert.equal(calls.filter((call) => tag(call.sql) === "pg_proc").length, 1); }); }); test("undefined column, expiry, pool close, and empty metadata all miss the cache", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "on", JYOTISHA_PG_MERGED_SESSION_SETUP: "off", }, async () => { const url = "postgresql://roundtrip-invalidate@127.0.0.1:1/jyotisha"; let failuresLeft = 1; const calls = installFakePool(url, { failBusinessCode: () => { if (failuresLeft <= 0) return null; failuresLeft -= 1; return "42703"; }, }); const client = clientFor(url); const failed = await client.from("profiles").select("id"); assert.equal(failed.error?.code, "42703"); calls.length = 0; const retried = await client.from("profiles").select("id"); assert.equal(retried.error, null); assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1); }); }); test("metadata older than 300 seconds is queried again", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "on", JYOTISHA_PG_MERGED_SESSION_SETUP: "off", }, async () => { const url = "postgresql://roundtrip-ttl@127.0.0.1:1/jyotisha"; let now = 10_000; setLocalPostgresMetadataClockForTests(() => now); const calls = installFakePool(url); const client = clientFor(url); await client.from("profiles").select("id"); calls.length = 0; await client.from("profiles").select("id"); assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 0); now += 300_000; calls.length = 0; await client.from("profiles").select("id"); assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1); }); }); test("an empty column list and a missing function are not cached", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "on", JYOTISHA_PG_MERGED_SESSION_SETUP: "off", }, async () => { const url = "postgresql://roundtrip-empty@127.0.0.1:1/jyotisha"; const calls = installFakePool(url, { columns: null, functions: null, primaryKeys: null }); const client = clientFor(url); await client.from("profiles").select("id"); calls.length = 0; await client.from("profiles").select("id"); assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1); const missing = await client.rpc("missing_probe", { left_name: "a" }); assert.match(missing.error?.message ?? "", /not found/); calls.length = 0; await client.rpc("missing_probe", { left_name: "a" }); assert.equal(calls.filter((call) => tag(call.sql) === "pg_proc").length, 1); }); }); test("closing the pool drops that url from the metadata cache", async () => { await withSwitches({ JYOTISHA_PG_METADATA_CACHE: "on", JYOTISHA_PG_MERGED_SESSION_SETUP: "off", }, async () => { const url = "postgresql://roundtrip-close@127.0.0.1:1/jyotisha"; const calls = installFakePool(url); const client = clientFor(url); await client.from("profiles").select("id"); await closeLocalPostgresDataPool(url); const reopenedCalls = installFakePool(url); const reopened = clientFor(url); await reopened.from("profiles").select("id"); assert.equal(reopenedCalls.filter((call) => tag(call.sql) === "information_schema").length, 1); assert.ok(calls.length > 0); }); });