All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
207 lines
5.8 KiB
TypeScript
207 lines
5.8 KiB
TypeScript
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,
|
|
},
|
|
});
|
|
});
|
|
});
|