import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { PersonalitiesService } from "./personalities.service"; import { PrismaService } from "../prisma/prisma.service"; import { CreatePersonalityDto, UpdatePersonalityDto } from "./dto"; import { NotFoundException, ConflictException } from "@nestjs/common"; describe("PersonalitiesService", () => { let service: PersonalitiesService; let prisma: PrismaService; const mockWorkspaceId = "workspace-123"; const mockUserId = "user-123"; const mockPersonalityId = "personality-123"; const mockPersonality = { id: mockPersonalityId, workspaceId: mockWorkspaceId, name: "Professional", description: "Professional communication style", tone: "professional", formalityLevel: "FORMAL" as const, systemPromptTemplate: "You are a professional assistant.", isDefault: true, isActive: true, createdAt: new Date(), updatedAt: new Date(), }; const mockPrismaService = { personality: { findMany: vi.fn(), findUnique: vi.fn(), findFirst: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), count: vi.fn(), }, $transaction: vi.fn((callback) => callback(mockPrismaService)), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ PersonalitiesService, { provide: PrismaService, useValue: mockPrismaService, }, ], }).compile(); service = module.get(PersonalitiesService); prisma = module.get(PrismaService); // Reset mocks vi.clearAllMocks(); }); describe("findAll", () => { it("should return all personalities for a workspace", async () => { const mockPersonalities = [mockPersonality]; mockPrismaService.personality.findMany.mockResolvedValue(mockPersonalities); const result = await service.findAll(mockWorkspaceId); expect(result).toEqual(mockPersonalities); expect(prisma.personality.findMany).toHaveBeenCalledWith({ where: { workspaceId: mockWorkspaceId, isActive: true }, orderBy: [{ isDefault: "desc" }, { name: "asc" }], }); }); it("should filter by active status", async () => { mockPrismaService.personality.findMany.mockResolvedValue([mockPersonality]); await service.findAll(mockWorkspaceId, false); expect(prisma.personality.findMany).toHaveBeenCalledWith({ where: { workspaceId: mockWorkspaceId, isActive: false }, orderBy: [{ isDefault: "desc" }, { name: "asc" }], }); }); }); describe("findOne", () => { it("should return a personality by id", async () => { mockPrismaService.personality.findUnique.mockResolvedValue(mockPersonality); const result = await service.findOne(mockWorkspaceId, mockPersonalityId); expect(result).toEqual(mockPersonality); expect(prisma.personality.findUnique).toHaveBeenCalledWith({ where: { id: mockPersonalityId, workspaceId: mockWorkspaceId, }, }); }); it("should throw NotFoundException when personality not found", async () => { mockPrismaService.personality.findUnique.mockResolvedValue(null); await expect(service.findOne(mockWorkspaceId, mockPersonalityId)).rejects.toThrow( NotFoundException, ); }); }); describe("findDefault", () => { it("should return the default personality", async () => { mockPrismaService.personality.findFirst.mockResolvedValue(mockPersonality); const result = await service.findDefault(mockWorkspaceId); expect(result).toEqual(mockPersonality); expect(prisma.personality.findFirst).toHaveBeenCalledWith({ where: { workspaceId: mockWorkspaceId, isDefault: true, isActive: true }, }); }); it("should throw NotFoundException when no default personality exists", async () => { mockPrismaService.personality.findFirst.mockResolvedValue(null); await expect(service.findDefault(mockWorkspaceId)).rejects.toThrow(NotFoundException); }); }); describe("create", () => { const createDto: CreatePersonalityDto = { name: "Casual", description: "Casual communication style", tone: "casual", formalityLevel: "CASUAL", systemPromptTemplate: "You are a casual assistant.", }; it("should create a new personality", async () => { mockPrismaService.personality.findFirst.mockResolvedValue(null); mockPrismaService.personality.create.mockResolvedValue({ ...mockPersonality, ...createDto, id: "new-personality-id", }); const result = await service.create(mockWorkspaceId, createDto); expect(result).toMatchObject(createDto); expect(prisma.personality.create).toHaveBeenCalledWith({ data: { workspaceId: mockWorkspaceId, ...createDto, }, }); }); it("should throw ConflictException when name already exists", async () => { mockPrismaService.personality.findFirst.mockResolvedValue(mockPersonality); await expect(service.create(mockWorkspaceId, createDto)).rejects.toThrow(ConflictException); }); it("should unset other defaults when creating a new default personality", async () => { const createDefaultDto = { ...createDto, isDefault: true }; // First call to findFirst checks for name conflict (should be null) // Second call to findFirst finds the existing default personality mockPrismaService.personality.findFirst .mockResolvedValueOnce(null) // No name conflict .mockResolvedValueOnce(mockPersonality); // Existing default mockPrismaService.personality.update.mockResolvedValue({ ...mockPersonality, isDefault: false, }); mockPrismaService.personality.create.mockResolvedValue({ ...mockPersonality, ...createDefaultDto, }); await service.create(mockWorkspaceId, createDefaultDto); expect(prisma.personality.update).toHaveBeenCalledWith({ where: { id: mockPersonalityId }, data: { isDefault: false }, }); }); }); describe("update", () => { const updateDto: UpdatePersonalityDto = { description: "Updated description", tone: "updated", }; it("should update a personality", async () => { mockPrismaService.personality.findUnique.mockResolvedValue(mockPersonality); mockPrismaService.personality.findFirst.mockResolvedValue(null); mockPrismaService.personality.update.mockResolvedValue({ ...mockPersonality, ...updateDto, }); const result = await service.update(mockWorkspaceId, mockPersonalityId, updateDto); expect(result).toMatchObject(updateDto); expect(prisma.personality.update).toHaveBeenCalledWith({ where: { id: mockPersonalityId }, data: updateDto, }); }); it("should throw NotFoundException when personality not found", async () => { mockPrismaService.personality.findUnique.mockResolvedValue(null); await expect( service.update(mockWorkspaceId, mockPersonalityId, updateDto), ).rejects.toThrow(NotFoundException); }); it("should throw ConflictException when updating to existing name", async () => { const updateNameDto = { name: "Existing Name" }; mockPrismaService.personality.findUnique.mockResolvedValue(mockPersonality); mockPrismaService.personality.findFirst.mockResolvedValue({ ...mockPersonality, id: "different-id", }); await expect( service.update(mockWorkspaceId, mockPersonalityId, updateNameDto), ).rejects.toThrow(ConflictException); }); }); describe("remove", () => { it("should delete a personality", async () => { mockPrismaService.personality.findUnique.mockResolvedValue(mockPersonality); mockPrismaService.personality.delete.mockResolvedValue(mockPersonality); const result = await service.remove(mockWorkspaceId, mockPersonalityId); expect(result).toEqual(mockPersonality); expect(prisma.personality.delete).toHaveBeenCalledWith({ where: { id: mockPersonalityId }, }); }); it("should throw NotFoundException when personality not found", async () => { mockPrismaService.personality.findUnique.mockResolvedValue(null); await expect(service.remove(mockWorkspaceId, mockPersonalityId)).rejects.toThrow( NotFoundException, ); }); }); });