import { describe, it, expect, vi } from "vitest"; // Mock Prisma db so the pure deriveShotStatus function can be tested in isolation vi.mock("@/lib/db", () => ({ db: {} })); import { deriveShotStatus } from "./shot-status"; import type { TaskStatus } from "@prisma/client"; // Helper to build task arrays quickly const tasks = (...statuses: TaskStatus[]) => statuses.map((status) => ({ status })); describe("deriveShotStatus", () => { // ── WAITING ─────────────────────────────────────────────────────────────── it("returns WAITING when there are no tasks", () => { expect(deriveShotStatus([], "PENDING", false)).toBe("WAITING"); }); it("returns WAITING when no tasks and internally approved (edge case: no tasks)", () => { // With INTERNALLY_APPROVED + no tasks the approval fields take precedence expect(deriveShotStatus([], "INTERNALLY_APPROVED", false)).toBe("READY_FOR_CLIENT"); }); // ── IN_PROGRESS ─────────────────────────────────────────────────────────── it("returns IN_PROGRESS when a task is TODO", () => { expect(deriveShotStatus(tasks("TODO"), "PENDING", false)).toBe("IN_PROGRESS"); }); it("returns IN_PROGRESS when a task is IN_PROGRESS", () => { expect(deriveShotStatus(tasks("IN_PROGRESS"), "PENDING", false)).toBe("IN_PROGRESS"); }); it("returns IN_PROGRESS when tasks are mixed TODO and DONE", () => { expect(deriveShotStatus(tasks("TODO", "DONE"), "PENDING", false)).toBe("IN_PROGRESS"); }); it("returns IN_PROGRESS for INTERNAL_REVIEW task status (task in internal review)", () => { // Task is in INTERNAL_REVIEW — not TODO/IN_PROGRESS/CHANGES → falls through to INTERNAL_REVIEW shot status expect(deriveShotStatus(tasks("INTERNAL_REVIEW"), "PENDING", false)).toBe("INTERNAL_REVIEW"); }); // ── REVISIONS (highest priority) ───────────────────────────────────────── it("returns REVISIONS when any task is CHANGES", () => { expect(deriveShotStatus(tasks("CHANGES"), "PENDING", false)).toBe("REVISIONS"); }); it("returns REVISIONS even when some tasks are DONE", () => { expect(deriveShotStatus(tasks("DONE", "CHANGES"), "PENDING", false)).toBe("REVISIONS"); }); it("returns REVISIONS even when shot is INTERNALLY_APPROVED", () => { // If somehow a task is in CHANGES after approval, REVISIONS wins expect(deriveShotStatus(tasks("CHANGES"), "INTERNALLY_APPROVED", false)).toBe("REVISIONS"); }); it("returns REVISIONS even when shot is CLIENT_APPROVED", () => { expect(deriveShotStatus(tasks("CHANGES"), "CLIENT_APPROVED", false)).toBe("REVISIONS"); }); // ── INTERNAL_REVIEW ─────────────────────────────────────────────────────── it("returns INTERNAL_REVIEW when all tasks are DONE and approval is PENDING", () => { expect(deriveShotStatus(tasks("DONE", "DONE"), "PENDING", false)).toBe("INTERNAL_REVIEW"); }); it("returns INTERNAL_REVIEW when tasks are INTERNAL_REVIEW/CLIENT_REVIEW and approval is PENDING", () => { expect(deriveShotStatus(tasks("INTERNAL_REVIEW", "CLIENT_REVIEW"), "PENDING", false)).toBe("INTERNAL_REVIEW"); }); it("returns INTERNAL_REVIEW when all tasks are DONE, regardless of sharedWithClient", () => { // sharedWithClient has no effect when approval is still PENDING expect(deriveShotStatus(tasks("DONE"), "PENDING", true)).toBe("INTERNAL_REVIEW"); }); // ── READY_FOR_CLIENT ────────────────────────────────────────────────────── it("returns READY_FOR_CLIENT when internally approved and not shared", () => { expect(deriveShotStatus(tasks("DONE"), "INTERNALLY_APPROVED", false)).toBe("READY_FOR_CLIENT"); }); it("returns READY_FOR_CLIENT when internally approved and not shared (no tasks)", () => { expect(deriveShotStatus([], "INTERNALLY_APPROVED", false)).toBe("READY_FOR_CLIENT"); }); // ── CLIENT_REVIEW ───────────────────────────────────────────────────────── it("returns CLIENT_REVIEW when internally approved and shared", () => { expect(deriveShotStatus(tasks("DONE"), "INTERNALLY_APPROVED", true)).toBe("CLIENT_REVIEW"); }); it("returns CLIENT_REVIEW when internally approved, shared, and tasks still in progress", () => { // Approval field takes precedence over task statuses (except CHANGES) expect(deriveShotStatus(tasks("TODO"), "INTERNALLY_APPROVED", true)).toBe("CLIENT_REVIEW"); }); // ── COMPLETE ────────────────────────────────────────────────────────────── it("returns COMPLETE when client approved", () => { expect(deriveShotStatus(tasks("DONE"), "CLIENT_APPROVED", true)).toBe("COMPLETE"); }); it("returns COMPLETE when client approved even if not shared anymore", () => { expect(deriveShotStatus(tasks("DONE"), "CLIENT_APPROVED", false)).toBe("COMPLETE"); }); // ── State transition scenarios ──────────────────────────────────────────── describe("full workflow transitions", () => { it("Artist work → tasks TODO → IN_PROGRESS", () => { expect(deriveShotStatus(tasks("TODO"), "PENDING", false)).toBe("IN_PROGRESS"); }); it("Tasks in internal review → INTERNAL_REVIEW shot status", () => { expect(deriveShotStatus(tasks("INTERNAL_REVIEW"), "PENDING", false)).toBe("INTERNAL_REVIEW"); }); it("All tasks done → INTERNAL_REVIEW (awaiting supervisor)", () => { expect(deriveShotStatus(tasks("DONE", "DONE", "DONE"), "PENDING", false)).toBe("INTERNAL_REVIEW"); }); it("Supervisor approves → READY_FOR_CLIENT", () => { expect(deriveShotStatus(tasks("DONE"), "INTERNALLY_APPROVED", false)).toBe("READY_FOR_CLIENT"); }); it("Producer shares → CLIENT_REVIEW", () => { expect(deriveShotStatus(tasks("DONE"), "INTERNALLY_APPROVED", true)).toBe("CLIENT_REVIEW"); }); it("Producer unshares → READY_FOR_CLIENT", () => { expect(deriveShotStatus(tasks("DONE"), "INTERNALLY_APPROVED", false)).toBe("READY_FOR_CLIENT"); }); it("Client approves → COMPLETE", () => { expect(deriveShotStatus(tasks("DONE"), "CLIENT_APPROVED", false)).toBe("COMPLETE"); }); it("Client requests changes → approval reset to PENDING + task CHANGES → REVISIONS", () => { // When changes are requested: shotApprovalStatus → PENDING, sharedWithClient → false, task → CHANGES expect(deriveShotStatus(tasks("CHANGES"), "PENDING", false)).toBe("REVISIONS"); }); }); });