import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { codeHmac, demoSmsCodeEnabled, phoneHmac, resetAuthEnvironmentForTests, validateAuthEnvironment, webOrigin, } from "./auth.config.js"; const valid = { PHONE_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", PHONE_HMAC_KEY: "phone-hmac-key-that-is-at-least-32-bytes", VERIFICATION_CODE_HMAC_KEY: "code-hmac-key-that-is-at-least-32-bytes!", REFRESH_TOKEN_HMAC_KEY: "refresh-key-that-is-at-least-thirty-two-bytes", JWT_SECRET: "jwt-secret-that-is-at-least-thirty-two-bytes", WEB_ORIGIN: "http://localhost:3000", }; describe("auth environment", () => { const original = { ...process.env }; beforeEach(() => { process.env.NODE_ENV = "test"; Object.assign(process.env, valid); resetAuthEnvironmentForTests(); }); afterEach(() => { process.env = { ...original, NODE_ENV: "test" }; resetAuthEnvironmentForTests(); }); it.each(Object.keys(valid))("fails fast when %s is missing", (name) => { delete process.env[name]; expect(() => validateAuthEnvironment()).toThrow(`${name} is required`); }); it.each(Object.keys(valid))("fails fast when %s is too short", (name) => { process.env[name] = "short"; expect(() => validateAuthEnvironment()).toThrow(); }); it("rejects duplicate keys including the verification-code key", () => { process.env.VERIFICATION_CODE_HMAC_KEY = process.env.PHONE_HMAC_KEY; expect(() => validateAuthEnvironment()).toThrow( "Auth secrets must be independent", ); }); it("uses a verification-code key independent from the phone key", () => { validateAuthEnvironment(); const phoneDigest = phoneHmac("+8613800138000"); const first = codeHmac(phoneDigest, "123456"); resetAuthEnvironmentForTests(); process.env.VERIFICATION_CODE_HMAC_KEY = "different-code-key-that-is-at-least-32-bytes"; validateAuthEnvironment(); expect(phoneHmac("+8613800138000")).toBe(phoneDigest); expect(codeHmac(phoneDigest, "123456")).not.toBe(first); }); it("fails closed when WEB_ORIGIN is missing or is not one absolute HTTP origin", () => { for (const value of [ undefined, "localhost:3000", "https://a.test/path", "https://u:p@a.test", "ftp://a.test", ]) { resetAuthEnvironmentForTests(); if (value === undefined) delete process.env.WEB_ORIGIN; else process.env.WEB_ORIGIN = value; expect(() => validateAuthEnvironment()).toThrow(/WEB_ORIGIN/); } }); it("caches and exposes the validated web origin", () => { validateAuthEnvironment(); expect(webOrigin()).toBe("http://localhost:3000"); }); it("returns demo codes only in test or explicitly enabled local development", () => { delete process.env.DEMO_SMS_CODE_ENABLED; expect(demoSmsCodeEnabled()).toBe(false); process.env.DEMO_SMS_CODE_ENABLED = "true"; expect(demoSmsCodeEnabled()).toBe(true); process.env.NODE_ENV = "staging"; expect(demoSmsCodeEnabled()).toBe(false); process.env.NODE_ENV = "development"; expect(demoSmsCodeEnabled()).toBe(true); process.env.NODE_ENV = "production"; expect(demoSmsCodeEnabled()).toBe(false); }); });