Submatter/packages/shared/src/convoy-lifecycle/state-machine.test.ts
Randall Stillwell 9405da8a45 feat(convoys): convoy lifecycle MCP and state machine (Epic 4)
Adds server-enforced convoy transitions with gate actor guards, convoy_events
logging, and six MCP tools (create, brief, transition, log, query, reconcile).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 14:51:19 -05:00

78 lines
1.9 KiB
TypeScript

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