import { SettingsService, invalidEmails, parseEmailList } from "./settings.service"; /** * The db → env → default ladder is the whole contract of this service: it is * what lets the setting move out of the environment without changing how any * existing deployment behaves. */ function build(row: { value: string } | null, env?: string) { const prisma = { appSetting: { findUnique: jest.fn().mockResolvedValue( row ? { key: "k", updatedAt: new Date("2026-08-02"), updatedById: "u1", ...row } : null, ), upsert: jest.fn().mockResolvedValue({}), }, }; const config = { get: jest.fn().mockReturnValue(env) }; return { service: new SettingsService(prisma as never, config as never), prisma, }; } describe("notification admin emails resolve db > env > default", () => { it("prefers the stored row", async () => { const { service } = build({ value: "a@x.com,b@x.com" }, "env@x.com"); await expect(service.notificationAdminEmails()).resolves.toMatchObject({ value: ["a@x.com", "b@x.com"], source: "db", updatedById: "u1", }); }); it("falls back to the environment when nothing is stored", async () => { const { service } = build(null, "env@x.com, other@x.com"); await expect(service.notificationAdminEmails()).resolves.toMatchObject({ value: ["env@x.com", "other@x.com"], source: "env", }); }); it("falls back to the built-in defaults when neither is set", async () => { const { service } = build(null, undefined); const resolved = await service.notificationAdminEmails(); expect(resolved.source).toBe("default"); expect(resolved.value).toHaveLength(2); }); it("treats a stored empty list as 'nobody', not as unset", async () => { // The regression this guards: falling through to env/defaults here would // keep mailing people who were deliberately removed. const { service } = build({ value: "" }, "env@x.com"); await expect(service.notificationAdminEmails()).resolves.toMatchObject({ value: [], source: "db", }); }); it("writes the list back as CSV", async () => { const { service, prisma } = build({ value: "" }); await service.setNotificationAdminEmails(["a@x.com", "b@x.com"], "user-9"); expect(prisma.appSetting.upsert).toHaveBeenCalledWith( expect.objectContaining({ create: expect.objectContaining({ value: "a@x.com,b@x.com", updatedById: "user-9" }), update: expect.objectContaining({ value: "a@x.com,b@x.com", updatedById: "user-9" }), }), ); }); }); describe("email list parsing", () => { it("trims and drops blanks", () => { expect(parseEmailList(" a@x.com , ,b@x.com ")).toEqual(["a@x.com", "b@x.com"]); }); it("rejects entries that are not addresses at all", () => { expect(invalidEmails(["ok@x.com", "nope", "also@bad"])).toEqual([ "nope", "also@bad", ]); }); });