import { describe, it, expect, vi, beforeEach } from "vitest"; import { Hono } from "hono"; // ─── Mock db module ─────────────────────────────────────────────────────────── let selectImpl: () => Promise; vi.mock("@groombook/db", () => { const staff = new Proxy( { _name: "staff" }, { get(_target, prop) { if (prop === "_name") return "staff"; return { table: "staff", column: prop }; }, } ); return { getDb: () => ({ select: (_fields: unknown) => ({ from: (_table: unknown) => ({ limit: (_n: number) => selectImpl(), }), }), }), staff, }; }); // ─── Build test app ─────────────────────────────────────────────────────────── async function makeApp() { const { getDb, staff } = await import("@groombook/db"); const app = new Hono(); app.get("/health/ready", async (c) => { try { await getDb().select({ id: staff.id }).from(staff).limit(1); return c.json({ status: "ready" }, 200); } catch (err) { const pgCode = (err as Record).code ?? "unknown"; console.error("[health/ready] DB check failed:", pgCode); return c.json({ status: "degraded" }, 503); } }); return app; } // ─── Tests ──────────────────────────────────────────────────────────────────── describe("GET /health/ready", () => { beforeEach(() => { vi.restoreAllMocks(); }); it("returns 200 {status:'ready'} when DB query succeeds", async () => { selectImpl = () => Promise.resolve([{ id: "staff-1" }]); const app = await makeApp(); const res = await app.request("/health/ready", { method: "GET" }); const body = (await res.json()) as Record; expect(res.status).toBe(200); expect(body.status).toBe("ready"); }); it("returns 503 {status:'degraded'} when DB query throws (schema dropped)", async () => { const schemaErr = Object.assign(new Error("relation \"staff\" does not exist"), { code: "42P01" }); selectImpl = () => Promise.reject(schemaErr); const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); const app = await makeApp(); const res = await app.request("/health/ready", { method: "GET" }); const body = (await res.json()) as Record; expect(res.status).toBe(503); expect(body.status).toBe("degraded"); // Must not leak SQL error details in the response body expect(JSON.stringify(body)).not.toContain("42P01"); expect(JSON.stringify(body)).not.toContain("relation"); consoleSpy.mockRestore(); }); it("returns 503 {status:'degraded'} on any DB connection error", async () => { selectImpl = () => Promise.reject(new Error("ECONNREFUSED")); const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); const app = await makeApp(); const res = await app.request("/health/ready", { method: "GET" }); const body = (await res.json()) as Record; expect(res.status).toBe(503); expect(body.status).toBe("degraded"); consoleSpy.mockRestore(); }); });