import { beforeEach, describe, expect, it, vi } from "vitest"; import { hash } from "bcryptjs"; import { OnboardingService } from "./onboarding.service"; import { PrismaService } from "../prisma/prisma.service"; import { CryptoService } from "../crypto/crypto.service"; vi.mock("bcryptjs", () => ({ hash: vi.fn(), })); describe("OnboardingService", () => { let service: OnboardingService; const mockPrismaService = { systemConfig: { findUnique: vi.fn(), upsert: vi.fn(), }, breakglassUser: { count: vi.fn(), create: vi.fn(), findFirst: vi.fn(), }, llmProvider: { create: vi.fn(), }, }; const mockCryptoService = { encrypt: vi.fn(), }; beforeEach(() => { vi.clearAllMocks(); service = new OnboardingService( mockPrismaService as unknown as PrismaService, mockCryptoService as unknown as CryptoService ); }); it("isCompleted returns false when no config exists", async () => { mockPrismaService.systemConfig.findUnique.mockResolvedValue(null); await expect(service.isCompleted()).resolves.toBe(false); expect(mockPrismaService.systemConfig.findUnique).toHaveBeenCalledWith({ where: { key: "onboarding.completed" }, }); }); it("isCompleted returns true when completed", async () => { mockPrismaService.systemConfig.findUnique.mockResolvedValue({ id: "cfg-1", key: "onboarding.completed", value: "true", encrypted: false, updatedAt: new Date(), }); await expect(service.isCompleted()).resolves.toBe(true); }); it("createBreakglassUser hashes password and creates record", async () => { const mockedHash = vi.mocked(hash); mockedHash.mockResolvedValue("hashed-password"); mockPrismaService.breakglassUser.count.mockResolvedValue(0); mockPrismaService.breakglassUser.create.mockResolvedValue({ id: "breakglass-1", username: "admin", }); const result = await service.createBreakglassUser("admin", "supersecret123"); expect(mockedHash).toHaveBeenCalledWith("supersecret123", 12); expect(mockPrismaService.breakglassUser.create).toHaveBeenCalledWith({ data: { username: "admin", passwordHash: "hashed-password", }, select: { id: true, username: true, }, }); expect(result).toEqual({ id: "breakglass-1", username: "admin" }); }); it("createBreakglassUser rejects if user already exists", async () => { mockPrismaService.breakglassUser.count.mockResolvedValue(1); await expect(service.createBreakglassUser("admin", "supersecret123")).rejects.toThrow( "Breakglass user already exists" ); }); it("configureOidc encrypts secret and saves to SystemConfig", async () => { mockCryptoService.encrypt.mockReturnValue("enc:oidc-secret"); mockPrismaService.systemConfig.upsert.mockResolvedValue({ id: "cfg", key: "oidc.clientSecret", value: "enc:oidc-secret", encrypted: true, updatedAt: new Date(), }); await service.configureOidc("https://auth.example.com", "client-id", "client-secret"); expect(mockCryptoService.encrypt).toHaveBeenCalledWith("client-secret"); expect(mockPrismaService.systemConfig.upsert).toHaveBeenCalledTimes(3); expect(mockPrismaService.systemConfig.upsert).toHaveBeenCalledWith({ where: { key: "oidc.issuerUrl" }, create: { key: "oidc.issuerUrl", value: "https://auth.example.com", encrypted: false, }, update: { value: "https://auth.example.com", encrypted: false, }, }); expect(mockPrismaService.systemConfig.upsert).toHaveBeenCalledWith({ where: { key: "oidc.clientId" }, create: { key: "oidc.clientId", value: "client-id", encrypted: false, }, update: { value: "client-id", encrypted: false, }, }); expect(mockPrismaService.systemConfig.upsert).toHaveBeenCalledWith({ where: { key: "oidc.clientSecret" }, create: { key: "oidc.clientSecret", value: "enc:oidc-secret", encrypted: true, }, update: { value: "enc:oidc-secret", encrypted: true, }, }); }); it("addProvider encrypts apiKey and creates LlmProvider", async () => { mockCryptoService.encrypt.mockReturnValue("enc:api-key"); mockPrismaService.llmProvider.create.mockResolvedValue({ id: "provider-1", }); const result = await service.addProvider("breakglass-1", { name: "my-openai", displayName: "OpenAI", type: "openai", baseUrl: "https://api.openai.com/v1", apiKey: "sk-test", models: [{ id: "gpt-4o-mini", name: "GPT-4o Mini" }], }); expect(mockCryptoService.encrypt).toHaveBeenCalledWith("sk-test"); expect(mockPrismaService.llmProvider.create).toHaveBeenCalledWith({ data: { userId: "breakglass-1", name: "my-openai", displayName: "OpenAI", type: "openai", baseUrl: "https://api.openai.com/v1", apiKey: "enc:api-key", models: [{ id: "gpt-4o-mini", name: "GPT-4o Mini" }], }, select: { id: true, }, }); expect(result).toEqual({ id: "provider-1" }); }); it("complete sets SystemConfig flag", async () => { mockPrismaService.systemConfig.upsert.mockResolvedValue({ id: "cfg-1", key: "onboarding.completed", value: "true", encrypted: false, updatedAt: new Date(), }); await service.complete(); expect(mockPrismaService.systemConfig.upsert).toHaveBeenCalledWith({ where: { key: "onboarding.completed" }, create: { key: "onboarding.completed", value: "true", encrypted: false, }, update: { value: "true", encrypted: false, }, }); }); });