Files
stack/apps/api/src/import/import.service.spec.ts
Jason Woltje f99107fbfc
All checks were successful
ci/woodpecker/push/api Pipeline was successful
feat(api): add admin bulk import endpoints (MS21-MIG-004) (#567)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-28 19:55:01 +00:00

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"),
]);
});
});
});