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>
301 lines
9.2 KiB
TypeScript
301 lines
9.2 KiB
TypeScript
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>(UserAgentService);
|
|
prisma = module.get<PrismaService>(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,
|
|
});
|
|
});
|
|
});
|
|
});
|