import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { UserAgentService } from "./user-agent.service"; import { PrismaService } from "../prisma/prisma.service"; import { NotFoundException, ConflictException, ForbiddenException } from "@nestjs/common"; describe("UserAgentService", () => { let service: UserAgentService; let prisma: PrismaService; const mockPrismaService = { userAgent: { findMany: vi.fn(), findUnique: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), }, agentTemplate: { findUnique: vi.fn(), }, }; const mockUserId = "550e8400-e29b-41d4-a716-446655440001"; const mockAgentId = "550e8400-e29b-41d4-a716-446655440002"; const mockTemplateId = "550e8400-e29b-41d4-a716-446655440003"; const mockAgent = { id: mockAgentId, userId: mockUserId, templateId: null, name: "jarvis", displayName: "Jarvis", role: "orchestrator", personality: "Capable, direct, proactive.", primaryModel: "opus", fallbackModels: ["sonnet"], toolPermissions: ["all"], discordChannel: "jarvis", isActive: true, createdAt: new Date(), updatedAt: new Date(), }; const mockTemplate = { id: mockTemplateId, name: "builder", displayName: "Builder", role: "coding", personality: "Focused, thorough.", primaryModel: "codex", fallbackModels: ["sonnet"], toolPermissions: ["exec", "read", "write"], discordChannel: "builder", isActive: true, createdAt: new Date(), updatedAt: new Date(), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UserAgentService, { provide: PrismaService, useValue: mockPrismaService, }, ], }).compile(); service = module.get(UserAgentService); prisma = module.get(PrismaService); vi.clearAllMocks(); }); it("should be defined", () => { expect(service).toBeDefined(); }); describe("findAll", () => { it("should return all agents for a user", async () => { mockPrismaService.userAgent.findMany.mockResolvedValue([mockAgent]); const result = await service.findAll(mockUserId); expect(result).toEqual([mockAgent]); expect(mockPrismaService.userAgent.findMany).toHaveBeenCalledWith({ where: { userId: mockUserId }, orderBy: { createdAt: "asc" }, }); }); it("should return empty array if no agents", async () => { mockPrismaService.userAgent.findMany.mockResolvedValue([]); const result = await service.findAll(mockUserId); expect(result).toEqual([]); }); }); describe("findOne", () => { it("should return an agent by id", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); const result = await service.findOne(mockUserId, mockAgentId); expect(result).toEqual(mockAgent); }); it("should throw NotFoundException if agent not found", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue(null); await expect(service.findOne(mockUserId, mockAgentId)).rejects.toThrow(NotFoundException); }); it("should throw ForbiddenException if agent belongs to different user", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue({ ...mockAgent, userId: "different-user-id", }); await expect(service.findOne(mockUserId, mockAgentId)).rejects.toThrow(ForbiddenException); }); }); describe("findByName", () => { it("should return an agent by name", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); const result = await service.findByName(mockUserId, "jarvis"); expect(result).toEqual(mockAgent); expect(mockPrismaService.userAgent.findUnique).toHaveBeenCalledWith({ where: { userId_name: { userId: mockUserId, name: "jarvis" } }, }); }); it("should throw NotFoundException if agent not found", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue(null); await expect(service.findByName(mockUserId, "nonexistent")).rejects.toThrow( NotFoundException ); }); }); describe("create", () => { it("should create a new agent", async () => { const createDto = { name: "jarvis", displayName: "Jarvis", role: "orchestrator", personality: "Capable, direct, proactive.", }; mockPrismaService.userAgent.findUnique.mockResolvedValue(null); mockPrismaService.userAgent.create.mockResolvedValue(mockAgent); const result = await service.create(mockUserId, createDto); expect(result).toEqual(mockAgent); }); it("should throw ConflictException if agent name already exists", async () => { const createDto = { name: "jarvis", displayName: "Jarvis", role: "orchestrator", personality: "Capable, direct, proactive.", }; mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); await expect(service.create(mockUserId, createDto)).rejects.toThrow(ConflictException); }); it("should throw NotFoundException if templateId is invalid", async () => { const createDto = { name: "custom", displayName: "Custom", role: "custom", personality: "Custom agent", templateId: "nonexistent-template", }; mockPrismaService.userAgent.findUnique.mockResolvedValue(null); mockPrismaService.agentTemplate.findUnique.mockResolvedValue(null); await expect(service.create(mockUserId, createDto)).rejects.toThrow(NotFoundException); }); }); describe("createFromTemplate", () => { it("should create an agent from a template", async () => { mockPrismaService.agentTemplate.findUnique.mockResolvedValue(mockTemplate); mockPrismaService.userAgent.findUnique.mockResolvedValue(null); mockPrismaService.userAgent.create.mockResolvedValue({ ...mockAgent, templateId: mockTemplateId, name: mockTemplate.name, displayName: mockTemplate.displayName, role: mockTemplate.role, }); const result = await service.createFromTemplate(mockUserId, mockTemplateId); expect(result.name).toBe(mockTemplate.name); expect(result.displayName).toBe(mockTemplate.displayName); }); it("should throw NotFoundException if template not found", async () => { mockPrismaService.agentTemplate.findUnique.mockResolvedValue(null); await expect(service.createFromTemplate(mockUserId, mockTemplateId)).rejects.toThrow( NotFoundException ); }); it("should throw ConflictException if agent name already exists", async () => { mockPrismaService.agentTemplate.findUnique.mockResolvedValue(mockTemplate); mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); await expect(service.createFromTemplate(mockUserId, mockTemplateId)).rejects.toThrow( ConflictException ); }); }); describe("update", () => { it("should update an agent", async () => { const updateDto = { displayName: "Updated Jarvis" }; const updatedAgent = { ...mockAgent, ...updateDto }; mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); mockPrismaService.userAgent.update.mockResolvedValue(updatedAgent); const result = await service.update(mockUserId, mockAgentId, updateDto); expect(result.displayName).toBe("Updated Jarvis"); }); it("should throw ConflictException if new name already exists", async () => { const updateDto = { name: "existing-name" }; mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); // Second call checks for existing name mockPrismaService.userAgent.findUnique.mockResolvedValue({ ...mockAgent, id: "other-id" }); await expect(service.update(mockUserId, mockAgentId, updateDto)).rejects.toThrow( ConflictException ); }); }); describe("remove", () => { it("should delete an agent", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); mockPrismaService.userAgent.delete.mockResolvedValue(mockAgent); const result = await service.remove(mockUserId, mockAgentId); expect(result).toEqual(mockAgent); }); }); describe("getStatus", () => { it("should return agent status", async () => { mockPrismaService.userAgent.findUnique.mockResolvedValue(mockAgent); const result = await service.getStatus(mockUserId, mockAgentId); expect(result).toEqual({ id: mockAgentId, name: "jarvis", displayName: "Jarvis", role: "orchestrator", isActive: true, }); }); }); describe("getAllStatuses", () => { it("should return all agent statuses", async () => { mockPrismaService.userAgent.findMany.mockResolvedValue([mockAgent]); const result = await service.getAllStatuses(mockUserId); expect(result).toHaveLength(1); expect(result[0]).toEqual({ id: mockAgentId, name: "jarvis", displayName: "Jarvis", role: "orchestrator", isActive: true, }); }); }); });