import { describe, it, expect } from "vitest"; import { config } from "../config/env"; describe("env validation", () => { it("has numeric port within valid range", () => { expect(typeof config.port).toBe("number"); expect(config.port).toBeGreaterThan(0); expect(config.port).toBeLessThanOrEqual(65535); }); it("has JWT_SECRET defined", () => { expect(config.jwt.secret).toBeTruthy(); expect(config.jwt.secret.length).toBeGreaterThanOrEqual(32); }); it("has TOTP_ENCRYPTION_KEY defined", () => { expect(config.totp.encryptionKey).toBeTruthy(); expect(config.totp.encryptionKey.length).toBeGreaterThanOrEqual(32); }); it("has positive JWT expiry values", () => { expect(config.jwt.accessTokenExpiry).toBeGreaterThan(0); expect(config.jwt.refreshTokenSessionExpiry).toBeGreaterThan(0); expect(config.jwt.refreshTokenRememberExpiry).toBeGreaterThan(0); }); it("has positive maxUploadSize", () => { expect(config.nas.maxUploadSize).toBeGreaterThan(0); }); it("has DATABASE_URL defined", () => { expect(config.db.url).toBeTruthy(); }); }); describe("env validation — failure path", () => { // The config singleton runs its validation at module-load time and is then // cached by the module system, so we cannot re-import it with bad env to // observe a throw without complex module-registry resetting. Instead we test // the exact predicates config/env.ts uses to reject bad input — this is the // logic that protects the boot, and the loaded config above proves the // happy path. (Predicates kept in sync with src/config/env.ts.) const HEX64_RE = /^[0-9a-fA-F]{64}$/; it("rejects a too-short / non-hex JWT_SECRET", () => { expect(HEX64_RE.test("short")).toBe(false); expect(HEX64_RE.test("a".repeat(63))).toBe(false); // one char short expect(HEX64_RE.test("z".repeat(64))).toBe(false); // 64 chars, not hex // The real config's secret must pass the very same check. expect(HEX64_RE.test(config.jwt.secret)).toBe(true); }); it("rejects an out-of-range or non-numeric PORT", () => { const portValid = (p: number) => !Number.isNaN(p) && p >= 1 && p <= 65535; expect(portValid(parseInt("0", 10))).toBe(false); expect(portValid(parseInt("70000", 10))).toBe(false); expect(portValid(parseInt("notaport", 10))).toBe(false); // NaN expect(portValid(config.port)).toBe(true); }); it("rejects a non-positive ACCESS_TOKEN_EXPIRY", () => { const expiryValid = (n: number) => !Number.isNaN(n) && n > 0; expect(expiryValid(0)).toBe(false); expect(expiryValid(-1)).toBe(false); expect(expiryValid(parseInt("oops", 10))).toBe(false); // NaN expect(expiryValid(config.jwt.accessTokenExpiry)).toBe(true); }); });