feat(#82): implement Personality Module
- Add Personality model to Prisma schema with FormalityLevel enum - Create migration and seed with 6 default personalities - Implement CRUD API with TDD approach (97.67% coverage) * PersonalitiesService: findAll, findOne, findDefault, create, update, remove * PersonalitiesController: REST endpoints with auth guards * Comprehensive test coverage (21 passing tests) - Add Personality types to shared package - Create frontend components: * PersonalitySelector: dropdown for choosing personality * PersonalityPreview: preview personality style and system prompt * PersonalityForm: create/edit personalities with validation * Settings page: manage personalities with CRUD operations - Integrate with Ollama API: * Support personalityId in chat endpoint * Auto-inject system prompt from personality * Fall back to default personality if not specified - API client for frontend personality management All tests passing with 97.67% backend coverage (exceeds 85% requirement)
This commit is contained in:
168
apps/api/src/tasks/dto/query-tasks.dto.spec.ts
Normal file
168
apps/api/src/tasks/dto/query-tasks.dto.spec.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { validate } from "class-validator";
|
||||
import { plainToClass } from "class-transformer";
|
||||
import { QueryTasksDto } from "./query-tasks.dto";
|
||||
import { TaskStatus, TaskPriority } from "@prisma/client";
|
||||
import { SortOrder } from "../../common/dto";
|
||||
|
||||
describe("QueryTasksDto", () => {
|
||||
const validWorkspaceId = "123e4567-e89b-12d3-a456-426614174000";
|
||||
|
||||
it("should accept valid workspaceId", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
|
||||
it("should reject invalid workspaceId", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: "not-a-uuid",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
expect(errors.some(e => e.property === "workspaceId")).toBe(true);
|
||||
});
|
||||
|
||||
it("should accept valid status filter", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
status: TaskStatus.IN_PROGRESS,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(Array.isArray(dto.status)).toBe(true);
|
||||
expect(dto.status).toEqual([TaskStatus.IN_PROGRESS]);
|
||||
});
|
||||
|
||||
it("should accept multiple status filters", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
status: [TaskStatus.IN_PROGRESS, TaskStatus.NOT_STARTED],
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(Array.isArray(dto.status)).toBe(true);
|
||||
expect(dto.status).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should accept valid priority filter", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
priority: TaskPriority.HIGH,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(Array.isArray(dto.priority)).toBe(true);
|
||||
expect(dto.priority).toEqual([TaskPriority.HIGH]);
|
||||
});
|
||||
|
||||
it("should accept multiple priority filters", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
priority: [TaskPriority.HIGH, TaskPriority.LOW],
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(Array.isArray(dto.priority)).toBe(true);
|
||||
expect(dto.priority).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should accept search parameter", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
search: "test task",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(dto.search).toBe("test task");
|
||||
});
|
||||
|
||||
it("should accept sortBy parameter", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
sortBy: "priority,dueDate",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(dto.sortBy).toBe("priority,dueDate");
|
||||
});
|
||||
|
||||
it("should accept sortOrder parameter", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
sortOrder: SortOrder.ASC,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(dto.sortOrder).toBe(SortOrder.ASC);
|
||||
});
|
||||
|
||||
it("should accept domainId filter", async () => {
|
||||
const domainId = "123e4567-e89b-12d3-a456-426614174001";
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
domainId,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(Array.isArray(dto.domainId)).toBe(true);
|
||||
expect(dto.domainId).toEqual([domainId]);
|
||||
});
|
||||
|
||||
it("should accept multiple domainId filters", async () => {
|
||||
const domainIds = [
|
||||
"123e4567-e89b-12d3-a456-426614174001",
|
||||
"123e4567-e89b-12d3-a456-426614174002",
|
||||
];
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
domainId: domainIds,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
expect(Array.isArray(dto.domainId)).toBe(true);
|
||||
expect(dto.domainId).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should accept date range filters", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
dueDateFrom: "2024-01-01T00:00:00Z",
|
||||
dueDateTo: "2024-12-31T23:59:59Z",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
|
||||
it("should accept all filters combined", async () => {
|
||||
const dto = plainToClass(QueryTasksDto, {
|
||||
workspaceId: validWorkspaceId,
|
||||
status: [TaskStatus.IN_PROGRESS, TaskStatus.NOT_STARTED],
|
||||
priority: [TaskPriority.HIGH, TaskPriority.MEDIUM],
|
||||
search: "urgent task",
|
||||
sortBy: "priority,dueDate",
|
||||
sortOrder: SortOrder.ASC,
|
||||
page: 2,
|
||||
limit: 25,
|
||||
dueDateFrom: "2024-01-01T00:00:00Z",
|
||||
dueDateTo: "2024-12-31T23:59:59Z",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user