Files
stack/apps/api/src/personalities/personalities.controller.spec.ts
Jason Woltje fe5bc4c16a
All checks were successful
ci/woodpecker/push/api Pipeline was successful
feat(api): implement personalities CRUD API with DTOs and Prisma model
- Add GET /api/personalities?isActive=true|false (list with optional filter)
- Add GET /api/personalities/default (default personality endpoint)
- Add GET /api/personalities/:id (single personality by ID)
- Add POST /api/personalities (create personality)
- Add PATCH /api/personalities/:id (update personality)
- Add DELETE /api/personalities/:id (delete personality)
- Add POST /api/personalities/:id/set-default (convenience set-default)
- Add tone and formalityLevel fields to Prisma Personality model
- Add migration 20260227000000_add_personality_tone_formality
- Map Prisma field names to frontend API contract (systemPrompt->systemPromptTemplate, isEnabled->isActive)
- Apply WorkspaceGuard + PermissionGuard per project patterns
- Return { success: true, data } wrapper for list endpoint
- Add PersonalityQueryDto for isActive filter support
- Update spec files to reflect new field mapping and response shape

Resolves frontend 404 on /api/personalities

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 04:36:55 -06:00

177 lines
6.0 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { PersonalitiesController } from "./personalities.controller";
import { PersonalitiesService } from "./personalities.service";
import type { CreatePersonalityDto } from "./dto/create-personality.dto";
import type { UpdatePersonalityDto } from "./dto/update-personality.dto";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { FormalityLevel } from "@prisma/client";
describe("PersonalitiesController", () => {
let controller: PersonalitiesController;
let service: PersonalitiesService;
const mockWorkspaceId = "workspace-123";
const mockPersonalityId = "personality-123";
/** API response shape (frontend field names) */
const mockPersonality = {
id: mockPersonalityId,
workspaceId: mockWorkspaceId,
name: "professional-assistant",
description: "A professional communication assistant",
tone: "professional",
formalityLevel: FormalityLevel.FORMAL,
systemPromptTemplate: "You are a professional assistant who helps with tasks.",
isDefault: true,
isActive: true,
createdAt: new Date("2026-01-01"),
updatedAt: new Date("2026-01-01"),
};
const mockPersonalitiesService = {
create: vi.fn(),
findAll: vi.fn(),
findOne: vi.fn(),
findByName: vi.fn(),
findDefault: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
setDefault: vi.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [PersonalitiesController],
providers: [
{
provide: PersonalitiesService,
useValue: mockPersonalitiesService,
},
],
})
.overrideGuard(AuthGuard)
.useValue({ canActivate: () => true })
.overrideGuard(WorkspaceGuard)
.useValue({
canActivate: (ctx: {
switchToHttp: () => { getRequest: () => { workspaceId: string } };
}) => {
const req = ctx.switchToHttp().getRequest();
req.workspaceId = mockWorkspaceId;
return true;
},
})
.overrideGuard(PermissionGuard)
.useValue({ canActivate: () => true })
.compile();
controller = module.get<PersonalitiesController>(PersonalitiesController);
service = module.get<PersonalitiesService>(PersonalitiesService);
vi.clearAllMocks();
});
describe("findAll", () => {
it("should return success response with personalities list", async () => {
const mockList = [mockPersonality];
mockPersonalitiesService.findAll.mockResolvedValue(mockList);
const result = await controller.findAll(mockWorkspaceId, {});
expect(result).toEqual({ success: true, data: mockList });
expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId, {});
});
it("should pass isActive query filter to service", async () => {
mockPersonalitiesService.findAll.mockResolvedValue([mockPersonality]);
await controller.findAll(mockWorkspaceId, { isActive: true });
expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId, { isActive: true });
});
});
describe("findDefault", () => {
it("should return the default personality", async () => {
mockPersonalitiesService.findDefault.mockResolvedValue(mockPersonality);
const result = await controller.findDefault(mockWorkspaceId);
expect(result).toEqual(mockPersonality);
expect(service.findDefault).toHaveBeenCalledWith(mockWorkspaceId);
});
});
describe("findOne", () => {
it("should return a personality by id", async () => {
mockPersonalitiesService.findOne.mockResolvedValue(mockPersonality);
const result = await controller.findOne(mockWorkspaceId, mockPersonalityId);
expect(result).toEqual(mockPersonality);
expect(service.findOne).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
});
});
describe("create", () => {
it("should create a new personality", async () => {
const createDto: CreatePersonalityDto = {
name: "casual-helper",
description: "A casual helper",
tone: "casual",
formalityLevel: FormalityLevel.CASUAL,
systemPromptTemplate: "You are a casual assistant.",
};
const created = { ...mockPersonality, ...createDto, isActive: true, isDefault: false };
mockPersonalitiesService.create.mockResolvedValue(created);
const result = await controller.create(mockWorkspaceId, createDto);
expect(result).toMatchObject({ name: createDto.name, tone: createDto.tone });
expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, createDto);
});
});
describe("update", () => {
it("should update a personality", async () => {
const updateDto: UpdatePersonalityDto = {
description: "Updated description",
tone: "enthusiastic",
};
const updated = { ...mockPersonality, ...updateDto };
mockPersonalitiesService.update.mockResolvedValue(updated);
const result = await controller.update(mockWorkspaceId, mockPersonalityId, updateDto);
expect(result).toMatchObject(updateDto);
expect(service.update).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId, updateDto);
});
});
describe("delete", () => {
it("should delete a personality", async () => {
mockPersonalitiesService.delete.mockResolvedValue(undefined);
await controller.delete(mockWorkspaceId, mockPersonalityId);
expect(service.delete).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
});
});
describe("setDefault", () => {
it("should set a personality as default", async () => {
const updated = { ...mockPersonality, isDefault: true };
mockPersonalitiesService.setDefault.mockResolvedValue(updated);
const result = await controller.setDefault(mockWorkspaceId, mockPersonalityId);
expect(result).toMatchObject({ isDefault: true });
expect(service.setDefault).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
});
});
});