feat(#37-41): Add domains, ideas, relationships, agents, widgets schema
Schema additions for issues #37-41: New models: - Domain (#37): Life domains (work, marriage, homelab, etc.) - Idea (#38): Brain dumps with pgvector embeddings - Relationship (#39): Generic entity linking (blocks, depends_on) - Agent (#40): ClawdBot agent tracking with metrics - AgentSession (#40): Conversation session tracking - WidgetDefinition (#41): HUD widget registry - UserLayout (#41): Per-user dashboard configuration Updated models: - Task, Event, Project: Added domainId foreign key - User, Workspace: Added new relations New enums: - IdeaStatus: CAPTURED, PROCESSING, ACTIONABLE, ARCHIVED, DISCARDED - RelationshipType: BLOCKS, BLOCKED_BY, DEPENDS_ON, etc. - AgentStatus: IDLE, WORKING, WAITING, ERROR, TERMINATED - EntityType: Added IDEA, DOMAIN Migration: 20260129182803_add_domains_ideas_agents_widgets
This commit is contained in:
348
apps/api/src/activity/dto/create-activity-log.dto.spec.ts
Normal file
348
apps/api/src/activity/dto/create-activity-log.dto.spec.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { validate } from "class-validator";
|
||||
import { plainToInstance } from "class-transformer";
|
||||
import { CreateActivityLogDto } from "./create-activity-log.dto";
|
||||
import { ActivityAction, EntityType } from "@prisma/client";
|
||||
|
||||
describe("CreateActivityLogDto", () => {
|
||||
describe("required fields validation", () => {
|
||||
it("should pass with all required fields valid", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should fail when workspaceId is missing", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const workspaceIdError = errors.find((e) => e.property === "workspaceId");
|
||||
expect(workspaceIdError).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fail when userId is missing", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const userIdError = errors.find((e) => e.property === "userId");
|
||||
expect(userIdError).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fail when action is missing", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const actionError = errors.find((e) => e.property === "action");
|
||||
expect(actionError).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fail when entityType is missing", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const entityTypeError = errors.find((e) => e.property === "entityType");
|
||||
expect(entityTypeError).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fail when entityId is missing", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const entityIdError = errors.find((e) => e.property === "entityId");
|
||||
expect(entityIdError).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("UUID validation", () => {
|
||||
it("should fail with invalid workspaceId UUID", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "invalid-uuid",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
expect(errors[0].property).toBe("workspaceId");
|
||||
});
|
||||
|
||||
it("should fail with invalid userId UUID", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "not-a-uuid",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const userIdError = errors.find((e) => e.property === "userId");
|
||||
expect(userIdError).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fail with invalid entityId UUID", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "bad-entity-id",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const entityIdError = errors.find((e) => e.property === "entityId");
|
||||
expect(entityIdError).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("enum validation", () => {
|
||||
it("should pass with all valid ActivityAction values", async () => {
|
||||
const actions = Object.values(ActivityAction);
|
||||
|
||||
for (const action of actions) {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("should fail with invalid action value", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: "INVALID_ACTION",
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const actionError = errors.find((e) => e.property === "action");
|
||||
expect(actionError?.constraints?.isEnum).toBeDefined();
|
||||
});
|
||||
|
||||
it("should pass with all valid EntityType values", async () => {
|
||||
const entityTypes = Object.values(EntityType);
|
||||
|
||||
for (const entityType of entityTypes) {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("should fail with invalid entityType value", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: "INVALID_TYPE",
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const entityTypeError = errors.find((e) => e.property === "entityType");
|
||||
expect(entityTypeError?.constraints?.isEnum).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("optional fields validation", () => {
|
||||
it("should pass with valid details object", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.UPDATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
details: {
|
||||
field: "status",
|
||||
oldValue: "TODO",
|
||||
newValue: "IN_PROGRESS",
|
||||
},
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should fail with non-object details", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.UPDATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
details: "not an object",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const detailsError = errors.find((e) => e.property === "details");
|
||||
expect(detailsError?.constraints?.isObject).toBeDefined();
|
||||
});
|
||||
|
||||
it("should pass with valid ipAddress", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
ipAddress: "192.168.1.1",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should pass with valid IPv6 address", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
ipAddress: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should fail when ipAddress exceeds max length", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
ipAddress: "a".repeat(46),
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const ipError = errors.find((e) => e.property === "ipAddress");
|
||||
expect(ipError?.constraints?.maxLength).toBeDefined();
|
||||
});
|
||||
|
||||
it("should pass with valid userAgent", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should fail when userAgent exceeds max length", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
userAgent: "a".repeat(501),
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
const userAgentError = errors.find((e) => e.property === "userAgent");
|
||||
expect(userAgentError?.constraints?.maxLength).toBeDefined();
|
||||
});
|
||||
|
||||
it("should pass when optional fields are not provided", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.CREATED,
|
||||
entityType: EntityType.TASK,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("complete validation", () => {
|
||||
it("should pass with all fields valid", async () => {
|
||||
const dto = plainToInstance(CreateActivityLogDto, {
|
||||
workspaceId: "550e8400-e29b-41d4-a716-446655440000",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
action: ActivityAction.UPDATED,
|
||||
entityType: EntityType.PROJECT,
|
||||
entityId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
details: {
|
||||
changes: ["status", "priority"],
|
||||
metadata: { source: "web-app" },
|
||||
},
|
||||
ipAddress: "10.0.0.1",
|
||||
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
|
||||
});
|
||||
|
||||
const errors = await validate(dto);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user