import { beforeEach, describe, expect, it, vi } from "vitest"; const poolConstructor = vi.hoisted(() => vi.fn()); const betterAuthMock = vi.hoisted(() => vi.fn()); const drizzleAdapterMock = vi.hoisted(() => vi.fn()); const serveMock = vi.hoisted(() => vi.fn()); const processOnSpy = vi.hoisted(() => vi.fn()); vi.mock("pg", () => ({ Pool: poolConstructor })); vi.mock("better-auth", () => ({ betterAuth: betterAuthMock })); vi.mock("better-auth/adapters/drizzle", () => ({ drizzleAdapter: drizzleAdapterMock })); vi.mock("@hono/node-server", () => ({ serve: serveMock })); describe("Ekklesia API import safety", () => { const originalEnv = { ...process.env }; const originalProcessOn = process.on.bind(process); beforeEach(() => { poolConstructor.mockClear(); betterAuthMock.mockClear(); drizzleAdapterMock.mockClear(); serveMock.mockClear(); processOnSpy.mockClear(); for (const key of Object.keys(process.env)) { if (!(key in originalEnv)) { delete process.env[key]; } } process.env = { ...originalEnv }; process.on = originalProcessOn; }); it("does not construct a database pool, Better Auth, or an HTTP server merely by importing the app factory", async () => { await import("../src/app.js"); expect(poolConstructor).not.toHaveBeenCalled(); expect(betterAuthMock).not.toHaveBeenCalled(); expect(serveMock).not.toHaveBeenCalled(); }); it("does not construct a database pool, Better Auth, or an HTTP server merely by importing configuration", async () => { await import("../src/config.js"); expect(poolConstructor).not.toHaveBeenCalled(); expect(betterAuthMock).not.toHaveBeenCalled(); expect(serveMock).not.toHaveBeenCalled(); }); it("does not read environment variables, connect to PostgreSQL, construct Better Auth, start a server, or install signal handlers merely by importing the executable server module", async () => { // Deliberately leave every runtime-required environment variable unset. If importing this // module performed any runtime composition, missing configuration would throw here. delete process.env.DATABASE_URL; delete process.env.EKKLESIA_IDENTITY_SECRET; delete process.env.EKKLESIA_IDENTITY_BASE_URL; delete process.env.EKKLESIA_IDENTITY_TRUSTED_ORIGINS; const onSpy = vi.spyOn(process, "on").mockImplementation(processOnSpy); await expect(import("../src/server.js")).resolves.toBeDefined(); expect(poolConstructor).not.toHaveBeenCalled(); expect(betterAuthMock).not.toHaveBeenCalled(); expect(drizzleAdapterMock).not.toHaveBeenCalled(); expect(serveMock).not.toHaveBeenCalled(); expect(processOnSpy).not.toHaveBeenCalled(); onSpy.mockRestore(); }); it("exposes startApi and main as explicit functions without invoking them at import time", async () => { const module = await import("../src/server.js"); expect(typeof module.startApi).toBe("function"); expect(typeof module.main).toBe("function"); expect(serveMock).not.toHaveBeenCalled(); }); });