import { describe, expect, it } from "vitest"; import { ConvoyTransitionError, validateConvoyTransition } from "./state-machine"; describe("validateConvoyTransition", () => { it("rejects draft → released", () => { expect(() => validateConvoyTransition({ from: "draft", to: "released", actor: "human-gate-1", }), ).toThrow(ConvoyTransitionError); try { validateConvoyTransition({ from: "draft", to: "released", actor: "human-gate-1", }); } catch (error) { expect(error).toBeInstanceOf(ConvoyTransitionError); expect((error as ConvoyTransitionError).code).toBe("INVALID_TRANSITION"); } }); it("rejects role-architect on human-gate-1 transition", () => { expect(() => validateConvoyTransition({ from: "convoy_review", to: "ready", actor: "role-architect", }), ).toThrow(ConvoyTransitionError); try { validateConvoyTransition({ from: "convoy_review", to: "ready", actor: "role-architect", }); } catch (error) { expect((error as ConvoyTransitionError).code).toBe("FORBIDDEN_ACTOR"); } }); it("allows human-gate-1 to approve plan", () => { expect(() => validateConvoyTransition({ from: "convoy_review", to: "ready", actor: "human-gate-1", }), ).not.toThrow(); }); it("allows role-architect to submit plan for review", () => { expect(() => validateConvoyTransition({ from: "draft", to: "convoy_review", actor: "role-architect", }), ).not.toThrow(); }); it("requires reason for blocked transitions", () => { try { validateConvoyTransition({ from: "ready", to: "blocked", actor: "role-implementer", }); } catch (error) { expect((error as ConvoyTransitionError).code).toBe("REASON_REQUIRED"); } }); });