import { NotFoundException } from "@nestjs/common"; import { compare } from "bcryptjs"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { FleetSettingsService } from "./fleet-settings.service"; import type { PrismaService } from "../prisma/prisma.service"; import type { CryptoService } from "../crypto/crypto.service"; describe("FleetSettingsService", () => { let service: FleetSettingsService; const mockPrisma = { llmProvider: { findMany: vi.fn(), findFirst: vi.fn(), findUnique: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), }, userAgentConfig: { findUnique: vi.fn(), upsert: vi.fn(), }, systemConfig: { findMany: vi.fn(), upsert: vi.fn(), deleteMany: vi.fn(), }, breakglassUser: { findUnique: vi.fn(), update: vi.fn(), }, }; const mockCrypto = { encrypt: vi.fn((value: string) => `enc:${value}`), }; beforeEach(() => { vi.clearAllMocks(); service = new FleetSettingsService( mockPrisma as unknown as PrismaService, mockCrypto as unknown as CryptoService ); }); it("listProviders returns only providers for the given userId", async () => { mockPrisma.llmProvider.findMany.mockResolvedValue([ { id: "prov-1", name: "openai-main", displayName: "OpenAI", type: "openai", baseUrl: "https://api.openai.com/v1", isActive: true, models: [{ id: "gpt-4.1" }], }, ]); const result = await service.listProviders("user-1"); expect(mockPrisma.llmProvider.findMany).toHaveBeenCalledWith({ where: { userId: "user-1" }, select: { id: true, name: true, displayName: true, type: true, baseUrl: true, isActive: true, models: true, }, orderBy: { createdAt: "asc" }, }); expect(result).toEqual([ { id: "prov-1", name: "openai-main", displayName: "OpenAI", type: "openai", baseUrl: "https://api.openai.com/v1", isActive: true, models: [{ id: "gpt-4.1" }], }, ]); }); it("createProvider encrypts apiKey", async () => { mockPrisma.llmProvider.create.mockResolvedValue({ id: "prov-2", }); const result = await service.createProvider("user-1", { name: "zai-main", displayName: "Z.ai", type: "zai", apiKey: "plaintext-key", models: [], }); expect(mockCrypto.encrypt).toHaveBeenCalledWith("plaintext-key"); expect(mockPrisma.llmProvider.create).toHaveBeenCalledWith({ data: { userId: "user-1", name: "zai-main", displayName: "Z.ai", type: "zai", baseUrl: null, apiKey: "enc:plaintext-key", apiType: "openai-completions", models: [], }, select: { id: true, }, }); expect(result).toEqual({ id: "prov-2" }); }); it("updateProvider rejects if not owned by user", async () => { mockPrisma.llmProvider.findFirst.mockResolvedValue(null); await expect( service.updateProvider("user-1", "provider-1", { displayName: "New Name", }) ).rejects.toBeInstanceOf(NotFoundException); expect(mockPrisma.llmProvider.update).not.toHaveBeenCalled(); }); it("deleteProvider rejects if not owned by user", async () => { mockPrisma.llmProvider.findFirst.mockResolvedValue(null); await expect(service.deleteProvider("user-1", "provider-1")).rejects.toBeInstanceOf( NotFoundException ); expect(mockPrisma.llmProvider.delete).not.toHaveBeenCalled(); }); it("getOidcConfig never returns clientSecret", async () => { mockPrisma.systemConfig.findMany.mockResolvedValue([ { key: "oidc.issuerUrl", value: "https://issuer.example.com", }, { key: "oidc.clientId", value: "client-id-1", }, { key: "oidc.clientSecret", value: "enc:very-secret", }, ]); const result = await service.getOidcConfig(); expect(result).toEqual({ issuerUrl: "https://issuer.example.com", clientId: "client-id-1", configured: true, }); expect(result).not.toHaveProperty("clientSecret"); }); it("updateOidcConfig encrypts clientSecret", async () => { await service.updateOidcConfig({ issuerUrl: "https://issuer.example.com", clientId: "client-id-1", clientSecret: "super-secret", }); expect(mockCrypto.encrypt).toHaveBeenCalledWith("super-secret"); expect(mockPrisma.systemConfig.upsert).toHaveBeenCalledTimes(3); expect(mockPrisma.systemConfig.upsert).toHaveBeenCalledWith({ where: { key: "oidc.clientSecret" }, update: { value: "enc:super-secret", encrypted: true }, create: { key: "oidc.clientSecret", value: "enc:super-secret", encrypted: true }, }); }); it("resetBreakglassPassword hashes new password", async () => { mockPrisma.breakglassUser.findUnique.mockResolvedValue({ id: "bg-1", username: "admin", passwordHash: "old-hash", }); await service.resetBreakglassPassword("admin", "new-password-123"); expect(mockPrisma.breakglassUser.update).toHaveBeenCalledOnce(); const updateCall = mockPrisma.breakglassUser.update.mock.calls[0]?.[0]; const newHash = updateCall?.data?.passwordHash; expect(newHash).toBeTypeOf("string"); expect(newHash).not.toBe("new-password-123"); expect(await compare("new-password-123", newHash as string)).toBe(true); }); });