import { describe, it, expect } from "vitest"; import { BOARD_STAGES, canPerformAction, canReject, getAvailableActions, getTransition, } from "@/lib/application-pipeline"; // The gated 7-stage recruitment pipeline (Crewing-Implementation-Spec §5.1). describe("Application pipeline state machine", () => { it("has the 7 board stages in order", () => { expect(BOARD_STAGES).toEqual([ "SHORTLISTED", "COMPETENCY_AND_REFERENCES", "DOC_VERIFICATION", "SALARY_AGREEMENT", "PROPOSED", "INTERVIEW", "SELECTED", ]); }); describe("sourcing advances (MPO/Manager)", () => { it("MPO walks the early stages", () => { expect(getTransition("SHORTLISTED", "start_competency")?.to).toBe("COMPETENCY_AND_REFERENCES"); expect(canPerformAction("SHORTLISTED", "start_competency", "MANNING")).toBe(true); expect(getTransition("COMPETENCY_AND_REFERENCES", "verify_competency")?.to).toBe("DOC_VERIFICATION"); expect(getTransition("DOC_VERIFICATION", "verify_docs")?.to).toBe("SALARY_AGREEMENT"); expect(getTransition("PROPOSED", "propose_accepted")?.to).toBe("INTERVIEW"); }); }); describe("Manager-gated advances (spec §6)", () => { it("salary approval is Manager-only", () => { expect(canPerformAction("SALARY_AGREEMENT", "approve_salary", "MANAGER")).toBe(true); expect(canPerformAction("SALARY_AGREEMENT", "approve_salary", "SUPERUSER")).toBe(true); expect(canPerformAction("SALARY_AGREEMENT", "approve_salary", "MANNING")).toBe(false); expect(getTransition("SALARY_AGREEMENT", "approve_salary")?.to).toBe("PROPOSED"); }); it("selection is Manager-only", () => { expect(canPerformAction("INTERVIEW", "select", "MANAGER")).toBe(true); expect(canPerformAction("INTERVIEW", "select", "MANNING")).toBe(false); expect(getTransition("INTERVIEW", "select")?.to).toBe("SELECTED"); }); }); it("rejects actions on the wrong stage", () => { expect(getTransition("SHORTLISTED", "select")).toBeNull(); expect(getTransition("SELECTED", "approve_salary")).toBeNull(); }); it("offers MPO only sourcing actions, Manager the gated ones", () => { expect(getAvailableActions("SALARY_AGREEMENT", "MANNING")).toHaveLength(0); expect(getAvailableActions("SALARY_AGREEMENT", "MANAGER")).toEqual(["approve_salary"]); expect(getAvailableActions("SHORTLISTED", "SITE_STAFF")).toHaveLength(0); }); describe("rejection (orthogonal)", () => { it("MPO/Manager can reject from any active stage", () => { expect(canReject("COMPETENCY_AND_REFERENCES", "MANNING")).toBe(true); expect(canReject("INTERVIEW", "MANAGER")).toBe(true); }); it("cannot reject once selected/onboarded/already rejected", () => { expect(canReject("SELECTED", "MANAGER")).toBe(false); expect(canReject("ONBOARDED", "MANAGER")).toBe(false); expect(canReject("REJECTED", "MANAGER")).toBe(false); }); it("site staff cannot reject", () => { expect(canReject("SHORTLISTED", "SITE_STAFF")).toBe(false); }); }); });