feat(api): implement personalities CRUD API (#537)
All checks were successful
ci/woodpecker/push/api Pipeline was successful
All checks were successful
ci/woodpecker/push/api Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #537.
This commit is contained in:
@@ -2,36 +2,32 @@ 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 { CreatePersonalityDto, UpdatePersonalityDto } from "./dto";
|
||||
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 mockUserId = "user-123";
|
||||
const mockPersonalityId = "personality-123";
|
||||
|
||||
/** API response shape (frontend field names) */
|
||||
const mockPersonality = {
|
||||
id: mockPersonalityId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
name: "professional-assistant",
|
||||
displayName: "Professional Assistant",
|
||||
description: "A professional communication assistant",
|
||||
systemPrompt: "You are a professional assistant who helps with tasks.",
|
||||
temperature: 0.7,
|
||||
maxTokens: 2000,
|
||||
llmProviderInstanceId: "provider-123",
|
||||
tone: "professional",
|
||||
formalityLevel: FormalityLevel.FORMAL,
|
||||
systemPromptTemplate: "You are a professional assistant who helps with tasks.",
|
||||
isDefault: true,
|
||||
isEnabled: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
const mockRequest = {
|
||||
user: { id: mockUserId },
|
||||
workspaceId: mockWorkspaceId,
|
||||
isActive: true,
|
||||
createdAt: new Date("2026-01-01"),
|
||||
updatedAt: new Date("2026-01-01"),
|
||||
};
|
||||
|
||||
const mockPersonalitiesService = {
|
||||
@@ -57,46 +53,43 @@ describe("PersonalitiesController", () => {
|
||||
})
|
||||
.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);
|
||||
|
||||
// Reset mocks
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("findAll", () => {
|
||||
it("should return all personalities", async () => {
|
||||
const mockPersonalities = [mockPersonality];
|
||||
mockPersonalitiesService.findAll.mockResolvedValue(mockPersonalities);
|
||||
it("should return success response with personalities list", async () => {
|
||||
const mockList = [mockPersonality];
|
||||
mockPersonalitiesService.findAll.mockResolvedValue(mockList);
|
||||
|
||||
const result = await controller.findAll(mockRequest);
|
||||
const result = await controller.findAll(mockWorkspaceId, {});
|
||||
|
||||
expect(result).toEqual(mockPersonalities);
|
||||
expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId);
|
||||
expect(result).toEqual({ success: true, data: mockList });
|
||||
expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId, {});
|
||||
});
|
||||
});
|
||||
|
||||
describe("findOne", () => {
|
||||
it("should return a personality by id", async () => {
|
||||
mockPersonalitiesService.findOne.mockResolvedValue(mockPersonality);
|
||||
it("should pass isActive query filter to service", async () => {
|
||||
mockPersonalitiesService.findAll.mockResolvedValue([mockPersonality]);
|
||||
|
||||
const result = await controller.findOne(mockRequest, mockPersonalityId);
|
||||
await controller.findAll(mockWorkspaceId, { isActive: true });
|
||||
|
||||
expect(result).toEqual(mockPersonality);
|
||||
expect(service.findOne).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
|
||||
});
|
||||
});
|
||||
|
||||
describe("findByName", () => {
|
||||
it("should return a personality by name", async () => {
|
||||
mockPersonalitiesService.findByName.mockResolvedValue(mockPersonality);
|
||||
|
||||
const result = await controller.findByName(mockRequest, "professional-assistant");
|
||||
|
||||
expect(result).toEqual(mockPersonality);
|
||||
expect(service.findByName).toHaveBeenCalledWith(mockWorkspaceId, "professional-assistant");
|
||||
expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId, { isActive: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,32 +97,40 @@ describe("PersonalitiesController", () => {
|
||||
it("should return the default personality", async () => {
|
||||
mockPersonalitiesService.findDefault.mockResolvedValue(mockPersonality);
|
||||
|
||||
const result = await controller.findDefault(mockRequest);
|
||||
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",
|
||||
displayName: "Casual Helper",
|
||||
description: "A casual helper",
|
||||
systemPrompt: "You are a casual assistant.",
|
||||
temperature: 0.8,
|
||||
maxTokens: 1500,
|
||||
tone: "casual",
|
||||
formalityLevel: FormalityLevel.CASUAL,
|
||||
systemPromptTemplate: "You are a casual assistant.",
|
||||
};
|
||||
|
||||
mockPersonalitiesService.create.mockResolvedValue({
|
||||
...mockPersonality,
|
||||
...createDto,
|
||||
});
|
||||
const created = { ...mockPersonality, ...createDto, isActive: true, isDefault: false };
|
||||
mockPersonalitiesService.create.mockResolvedValue(created);
|
||||
|
||||
const result = await controller.create(mockRequest, createDto);
|
||||
const result = await controller.create(mockWorkspaceId, createDto);
|
||||
|
||||
expect(result).toMatchObject(createDto);
|
||||
expect(result).toMatchObject({ name: createDto.name, tone: createDto.tone });
|
||||
expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, createDto);
|
||||
});
|
||||
});
|
||||
@@ -138,15 +139,13 @@ describe("PersonalitiesController", () => {
|
||||
it("should update a personality", async () => {
|
||||
const updateDto: UpdatePersonalityDto = {
|
||||
description: "Updated description",
|
||||
temperature: 0.9,
|
||||
tone: "enthusiastic",
|
||||
};
|
||||
|
||||
mockPersonalitiesService.update.mockResolvedValue({
|
||||
...mockPersonality,
|
||||
...updateDto,
|
||||
});
|
||||
const updated = { ...mockPersonality, ...updateDto };
|
||||
mockPersonalitiesService.update.mockResolvedValue(updated);
|
||||
|
||||
const result = await controller.update(mockRequest, mockPersonalityId, updateDto);
|
||||
const result = await controller.update(mockWorkspaceId, mockPersonalityId, updateDto);
|
||||
|
||||
expect(result).toMatchObject(updateDto);
|
||||
expect(service.update).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId, updateDto);
|
||||
@@ -157,7 +156,7 @@ describe("PersonalitiesController", () => {
|
||||
it("should delete a personality", async () => {
|
||||
mockPersonalitiesService.delete.mockResolvedValue(undefined);
|
||||
|
||||
await controller.delete(mockRequest, mockPersonalityId);
|
||||
await controller.delete(mockWorkspaceId, mockPersonalityId);
|
||||
|
||||
expect(service.delete).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
|
||||
});
|
||||
@@ -165,12 +164,10 @@ describe("PersonalitiesController", () => {
|
||||
|
||||
describe("setDefault", () => {
|
||||
it("should set a personality as default", async () => {
|
||||
mockPersonalitiesService.setDefault.mockResolvedValue({
|
||||
...mockPersonality,
|
||||
isDefault: true,
|
||||
});
|
||||
const updated = { ...mockPersonality, isDefault: true };
|
||||
mockPersonalitiesService.setDefault.mockResolvedValue(updated);
|
||||
|
||||
const result = await controller.setDefault(mockRequest, mockPersonalityId);
|
||||
const result = await controller.setDefault(mockWorkspaceId, mockPersonalityId);
|
||||
|
||||
expect(result).toMatchObject({ isDefault: true });
|
||||
expect(service.setDefault).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
|
||||
|
||||
Reference in New Issue
Block a user