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>
252 lines
7.6 KiB
TypeScript
252 lines
7.6 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { Test, TestingModule } from "@nestjs/testing";
|
|
import { ProjectStatus, TaskPriority, TaskStatus } from "@prisma/client";
|
|
import { ImportService } from "./import.service";
|
|
import { PrismaService } from "../prisma/prisma.service";
|
|
|
|
describe("ImportService", () => {
|
|
let service: ImportService;
|
|
|
|
const mockPrismaService = {
|
|
withWorkspaceContext: vi.fn(),
|
|
domain: {
|
|
findUnique: vi.fn(),
|
|
create: vi.fn(),
|
|
},
|
|
project: {
|
|
findFirst: vi.fn(),
|
|
create: vi.fn(),
|
|
},
|
|
task: {
|
|
findFirst: vi.fn(),
|
|
create: vi.fn(),
|
|
},
|
|
};
|
|
|
|
const workspaceId = "550e8400-e29b-41d4-a716-446655440001";
|
|
const userId = "550e8400-e29b-41d4-a716-446655440002";
|
|
|
|
beforeEach(async () => {
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
ImportService,
|
|
{
|
|
provide: PrismaService,
|
|
useValue: mockPrismaService,
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
service = module.get<ImportService>(ImportService);
|
|
vi.clearAllMocks();
|
|
|
|
mockPrismaService.withWorkspaceContext.mockImplementation(
|
|
async (_userId: string, _workspaceId: string, fn: (client: unknown) => Promise<unknown>) => {
|
|
return fn(mockPrismaService);
|
|
}
|
|
);
|
|
});
|
|
|
|
it("should be defined", () => {
|
|
expect(service).toBeDefined();
|
|
});
|
|
|
|
describe("importTasks", () => {
|
|
it("maps status/priority/domain and imports a task", async () => {
|
|
mockPrismaService.task.findFirst.mockResolvedValue(null);
|
|
mockPrismaService.domain.findUnique.mockResolvedValue(null);
|
|
mockPrismaService.domain.create.mockResolvedValue({ id: "domain-id" });
|
|
mockPrismaService.project.findFirst.mockResolvedValue(null);
|
|
mockPrismaService.task.create.mockResolvedValue({ id: "task-id" });
|
|
|
|
const result = await service.importTasks(workspaceId, userId, [
|
|
{
|
|
id: "task-1",
|
|
title: "Import me",
|
|
domain: "Platform Ops",
|
|
status: "in-progress",
|
|
priority: "critical",
|
|
project: null,
|
|
related: [],
|
|
blocks: [],
|
|
blocked_by: [],
|
|
progress: 42,
|
|
due: "2026-03-15",
|
|
created: "2026-03-01T10:00:00.000Z",
|
|
updated: "2026-03-05T12:00:00.000Z",
|
|
assignee: null,
|
|
notes: "notes",
|
|
notes_nontechnical: "non technical",
|
|
},
|
|
]);
|
|
|
|
expect(result).toEqual({ imported: 1, skipped: 0, errors: [] });
|
|
expect(mockPrismaService.task.create).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
data: expect.objectContaining({
|
|
title: "Import me",
|
|
status: TaskStatus.IN_PROGRESS,
|
|
priority: TaskPriority.HIGH,
|
|
domainId: "domain-id",
|
|
}),
|
|
})
|
|
);
|
|
});
|
|
|
|
it("skips existing task by brainId", async () => {
|
|
mockPrismaService.task.findFirst.mockResolvedValue({ id: "existing-task-id" });
|
|
|
|
const result = await service.importTasks(workspaceId, userId, [
|
|
{
|
|
id: "task-1",
|
|
title: "Existing",
|
|
domain: null,
|
|
status: "pending",
|
|
priority: "medium",
|
|
project: null,
|
|
related: [],
|
|
blocks: [],
|
|
blocked_by: [],
|
|
progress: null,
|
|
due: null,
|
|
created: null,
|
|
updated: null,
|
|
assignee: null,
|
|
notes: null,
|
|
notes_nontechnical: null,
|
|
},
|
|
]);
|
|
|
|
expect(result.imported).toBe(0);
|
|
expect(result.skipped).toBe(1);
|
|
expect(mockPrismaService.task.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("collects mapping/missing-project errors while importing", async () => {
|
|
mockPrismaService.task.findFirst.mockResolvedValue(null);
|
|
mockPrismaService.project.findFirst.mockResolvedValue(null);
|
|
mockPrismaService.task.create.mockResolvedValue({ id: "task-id" });
|
|
|
|
const result = await service.importTasks(workspaceId, userId, [
|
|
{
|
|
id: "task-1",
|
|
title: "Needs project",
|
|
domain: null,
|
|
status: "mystery-status",
|
|
priority: "mystery-priority",
|
|
project: "brain-project-1",
|
|
related: [],
|
|
blocks: [],
|
|
blocked_by: [],
|
|
progress: null,
|
|
due: null,
|
|
created: null,
|
|
updated: null,
|
|
assignee: null,
|
|
notes: null,
|
|
notes_nontechnical: null,
|
|
},
|
|
]);
|
|
|
|
expect(result.imported).toBe(1);
|
|
expect(result.errors).toEqual(
|
|
expect.arrayContaining([
|
|
expect.stringContaining('Unknown task status "mystery-status"'),
|
|
expect.stringContaining('Unknown task priority "mystery-priority"'),
|
|
expect.stringContaining('referenced project "brain-project-1" not found'),
|
|
])
|
|
);
|
|
expect(mockPrismaService.task.create).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
data: expect.objectContaining({
|
|
status: TaskStatus.NOT_STARTED,
|
|
priority: TaskPriority.MEDIUM,
|
|
projectId: null,
|
|
}),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("importProjects", () => {
|
|
it("maps status/domain and imports a project", async () => {
|
|
mockPrismaService.project.findFirst.mockResolvedValue(null);
|
|
mockPrismaService.domain.findUnique.mockResolvedValue(null);
|
|
mockPrismaService.domain.create.mockResolvedValue({ id: "domain-id" });
|
|
mockPrismaService.project.create.mockResolvedValue({ id: "project-id" });
|
|
|
|
const result = await service.importProjects(workspaceId, userId, [
|
|
{
|
|
id: "project-1",
|
|
name: "Project One",
|
|
description: "desc",
|
|
domain: "Backend",
|
|
status: "in-progress",
|
|
priority: "high",
|
|
progress: 50,
|
|
repo: "git@example.com/repo",
|
|
branch: "main",
|
|
current_milestone: "MS21",
|
|
next_milestone: "MS22",
|
|
blocker: null,
|
|
owner: "owner",
|
|
docs_path: "docs/PRD.md",
|
|
created: "2026-03-01",
|
|
updated: "2026-03-05",
|
|
target_date: "2026-04-01",
|
|
notes: "notes",
|
|
notes_nontechnical: "non tech",
|
|
parent: null,
|
|
},
|
|
]);
|
|
|
|
expect(result).toEqual({ imported: 1, skipped: 0, errors: [] });
|
|
expect(mockPrismaService.project.create).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
data: expect.objectContaining({
|
|
name: "Project One",
|
|
status: ProjectStatus.ACTIVE,
|
|
domainId: "domain-id",
|
|
}),
|
|
})
|
|
);
|
|
});
|
|
|
|
it("captures create failures as errors", async () => {
|
|
mockPrismaService.project.findFirst.mockResolvedValue(null);
|
|
mockPrismaService.project.create.mockRejectedValue(new Error("db failed"));
|
|
|
|
const result = await service.importProjects(workspaceId, userId, [
|
|
{
|
|
id: "project-1",
|
|
name: "Project One",
|
|
description: null,
|
|
domain: null,
|
|
status: "planning",
|
|
priority: null,
|
|
progress: null,
|
|
repo: null,
|
|
branch: null,
|
|
current_milestone: null,
|
|
next_milestone: null,
|
|
blocker: null,
|
|
owner: null,
|
|
docs_path: null,
|
|
created: null,
|
|
updated: null,
|
|
target_date: null,
|
|
notes: null,
|
|
notes_nontechnical: null,
|
|
parent: null,
|
|
},
|
|
]);
|
|
|
|
expect(result.imported).toBe(0);
|
|
expect(result.skipped).toBe(1);
|
|
expect(result.errors).toEqual([
|
|
expect.stringContaining("project project-1: failed to import: db failed"),
|
|
]);
|
|
});
|
|
});
|
|
});
|