config.unit.test.ts
| 1 | import { describe, expect, test } from "bun:test"; |
| 2 | |
| 3 | // config.ts reads process.env once at import time, so these run the same |
| 4 | // parsing logic against a fresh module instance per case. |
| 5 | async function loadConfig(env: Record<string, string>) { |
| 6 | const saved: Record<string, string | undefined> = {}; |
| 7 | for (const k of Object.keys(env)) { |
| 8 | saved[k] = process.env[k]; |
| 9 | process.env[k] = env[k]; |
| 10 | } |
| 11 | try { |
| 12 | // Cache-bust so the module body re-runs against the patched env. |
| 13 | const mod = await import(`../src/config.ts?t=${Math.random()}`); |
| 14 | return mod.default as typeof import("../src/config.ts").default; |
| 15 | } finally { |
| 16 | for (const [k, v] of Object.entries(saved)) { |
| 17 | if (v === undefined) delete process.env[k]; |
| 18 | else process.env[k] = v; |
| 19 | } |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | describe("config env parsing", () => { |
| 24 | test("unset falls back to the default", async () => { |
| 25 | const c = await loadConfig({}); |
| 26 | expect(c.MAX_TITLE_BYTES).toBe(500); |
| 27 | expect(c.CI_MAX_HISTORY).toBe(50); |
| 28 | }); |
| 29 | |
| 30 | test("empty string falls back to the default", async () => { |
| 31 | const c = await loadConfig({ MAX_TITLE_BYTES: "" }); |
| 32 | expect(c.MAX_TITLE_BYTES).toBe(500); |
| 33 | }); |
| 34 | |
| 35 | test("garbage falls back to the default", async () => { |
| 36 | const c = await loadConfig({ MAX_TITLE_BYTES: "abc" }); |
| 37 | expect(c.MAX_TITLE_BYTES).toBe(500); |
| 38 | }); |
| 39 | |
| 40 | test("a valid number is used", async () => { |
| 41 | const c = await loadConfig({ MAX_TITLE_BYTES: "120" }); |
| 42 | expect(c.MAX_TITLE_BYTES).toBe(120); |
| 43 | }); |
| 44 | |
| 45 | test("explicit 0 is honoured, not replaced by the default", async () => { |
| 46 | expect((await loadConfig({ MAX_RENDER_BYTES: "0" })).MAX_RENDER_BYTES).toBe(0); |
| 47 | expect((await loadConfig({ INLINE_MAX_BYTES: "0" })).INLINE_MAX_BYTES).toBe(0); |
| 48 | expect((await loadConfig({ CI_MAX_HISTORY: "0" })).CI_MAX_HISTORY).toBe(0); |
| 49 | }); |
| 50 | |
| 51 | test("BASE_URL default tracks PORT", async () => { |
| 52 | const c = await loadConfig({ PORT: "8080" }); |
| 53 | expect(c.PORT).toBe(8080); |
| 54 | expect(c.BASE_URL).toBe("http://localhost:8080"); |
| 55 | expect(c.PUBLIC_ORIGIN).toBe("http://localhost:8080"); |
| 56 | expect(c.PUBLIC_HTTPS).toBe(false); |
| 57 | }); |
| 58 | |
| 59 | test("an https BASE_URL sets the derived origin fields", async () => { |
| 60 | const c = await loadConfig({ BASE_URL: "https://forge.example.com" }); |
| 61 | expect(c.PUBLIC_HTTPS).toBe(true); |
| 62 | expect(c.PUBLIC_ORIGIN).toBe("https://forge.example.com"); |
| 63 | expect(c.COMMITTER_EMAIL).toContain("@forge.example.com"); |
| 64 | }); |
| 65 | }); |
| 66 |