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); vi.clearAllMocks(); mockPrismaService.withWorkspaceContext.mockImplementation( async (_userId: string, _workspaceId: string, fn: (client: unknown) => Promise) => { 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"), ]); }); }); });