import { describe, expect, it, jest } from "@jest/globals"; import { SettingsService } from "../../../src/domain/app-settings/app-settings.service.ts"; import type { ISettingsRepository } from "../../../src/domain/app-settings/app-settings-repository.interface.ts"; import type { ValidatedEnv } from "../../../src/config/envValidation.ts"; import type { Settings } from "../../../src/domain/app-settings/app-settings.type.ts"; // ── Helpers ────────────────────────────────────────────────────────────────── const makeEnv = (overrides?: Partial): ValidatedEnv => ({ JWT_SECRET: "test-secret", TOKEN_TTL: "99d", NODE_ENV: "development", LOG_LEVEL: "debug", CLIENT_HOST: "http://localhost:5173", DB_CONNECTION_STRING: "mongodb://localhost:27017/test_db", DB_TYPE: "mongodb", STATUS_PAGE_THEMES_ENABLED: false, ENCRYPTION_KEY: [], ...overrides, }) as ValidatedEnv; const makeSettings = (overrides?: Partial): Settings => ({ id: "settings-1", checkTTL: 30, language: "en", systemEmailSecure: false, systemEmailPool: false, systemEmailIgnoreTLS: false, systemEmailRequireTLS: false, systemEmailRejectUnauthorized: false, showURL: true, singleton: true, version: 1, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z", ...overrides, }) as Settings; const createSettingsRepo = () => ({ create: jest.fn().mockResolvedValue(makeSettings()), findSingleton: jest.fn().mockResolvedValue(makeSettings()), update: jest.fn().mockResolvedValue(makeSettings()), deleteLegacy: jest.fn().mockResolvedValue(true), }) as unknown as jest.Mocked; const createService = (envOverrides?: Partial) => { const env = makeEnv(envOverrides); const service = new SettingsService(env); const settingsRepository = createSettingsRepo(); service.setRepository(settingsRepository); return { service, settingsRepository, env }; }; // ── Tests ──────────────────────────────────────────────────────────────────── describe("SettingsService", () => { // ── getSettings ───────────────────────────────────────────────────────── describe("getSettings", () => { it("returns env config mapped from constructor", () => { const { service } = createService(); const settings = service.getSettings(); expect(settings).toEqual({ jwtSecret: "test-secret", jwtTTL: "99d", nodeEnv: "development", logLevel: "debug", clientHost: "http://localhost:5173", dbConnectionString: "mongodb://localhost:27017/test_db", dbType: "mongodb", statusPageThemesEnabled: false, clientConfig: {}, }); }); it("maps only set CLIENT_CONFIG_* env vars into clientConfig", () => { const { service } = createService({ CLIENT_CONFIG_API_BASE_URL: "https://api.example.com/api/v1", CLIENT_CONFIG_LOG_LEVEL: "warn", }); expect(service.getSettings().clientConfig).toEqual({ apiBaseUrl: "https://api.example.com/api/v1", logLevel: "warn", }); }); it("reflects custom env values", () => { const { service } = createService({ NODE_ENV: "production", LOG_LEVEL: "error" }); const settings = service.getSettings(); expect(settings.nodeEnv).toBe("production"); expect(settings.logLevel).toBe("error"); }); }); // ── areStatusPageThemesEnabled ────────────────────────────────────────── describe("areStatusPageThemesEnabled", () => { it("returns false when STATUS_PAGE_THEMES_ENABLED env is false", () => { const { service } = createService({ STATUS_PAGE_THEMES_ENABLED: false }); expect(service.areStatusPageThemesEnabled()).toBe(false); }); it("returns true when STATUS_PAGE_THEMES_ENABLED env is true", () => { const { service } = createService({ STATUS_PAGE_THEMES_ENABLED: true }); expect(service.areStatusPageThemesEnabled()).toBe(true); }); }); // ── setRepository ─────────────────────────────────────────────────────── describe("setRepository", () => { it("allows DB methods to work after being called", async () => { const env = makeEnv(); const service = new SettingsService(env); const repo = createSettingsRepo(); service.setRepository(repo); const settings = await service.getDBSettings(); expect(settings).toEqual(makeSettings()); }); }); // ── getDBSettings ─────────────────────────────────────────────────────── describe("getDBSettings", () => { it("deletes legacy settings and returns singleton", async () => { const { service, settingsRepository } = createService(); const expected = makeSettings(); (settingsRepository.findSingleton as jest.Mock).mockResolvedValue(expected); const result = await service.getDBSettings(); expect(settingsRepository.deleteLegacy).toHaveBeenCalled(); expect(settingsRepository.findSingleton).toHaveBeenCalled(); expect(result).toBe(expected); }); it("creates default settings when singleton is null, then returns them", async () => { const { service, settingsRepository } = createService(); const created = makeSettings(); (settingsRepository.findSingleton as jest.Mock).mockResolvedValueOnce(null).mockResolvedValueOnce(created); const result = await service.getDBSettings(); expect(settingsRepository.create).toHaveBeenCalledWith({}); expect(settingsRepository.findSingleton).toHaveBeenCalledTimes(2); expect(result).toBe(created); }); it("throws when settings are still null after creation attempt", async () => { const { service, settingsRepository } = createService(); (settingsRepository.findSingleton as jest.Mock).mockResolvedValue(null); await expect(service.getDBSettings()).rejects.toThrow("Settings not found"); }); it("throws when repository is not set", async () => { const env = makeEnv(); const service = new SettingsService(env); await expect(service.getDBSettings()).rejects.toThrow("Settings repository not initialized"); }); }); // ── updateDbSettings ──────────────────────────────────────────────────── describe("updateDbSettings", () => { it("delegates to repository and returns updated settings", async () => { const updated = makeSettings({ checkTTL: 60 }); const { service, settingsRepository } = createService(); (settingsRepository.update as jest.Mock).mockResolvedValue(updated); const result = await service.updateDbSettings({ checkTTL: 60 }); expect(result).toBe(updated); expect(settingsRepository.update).toHaveBeenCalledWith({ checkTTL: 60 }); }); it("throws when repository is not set", async () => { const env = makeEnv(); const service = new SettingsService(env); await expect(service.updateDbSettings({ checkTTL: 60 })).rejects.toThrow("Settings repository not initialized"); }); }); });