/** * Task #242 (B1) — lifecycle transition tests. * * Unit-half: pure isLegalTransition matrix. * DB-half: transitionTo persists row when DATABASE_URL is set. */ import { test } from "node:test"; import assert from "node:assert/strict"; import { isLegalTransition, assertLegalTransition, IllegalLifecycleTransitionError, ALL_LIFECYCLE_STATES, type CapabilityLifecycleState, } from "../lifecycle.ts"; const LEGAL: ReadonlyArray<[CapabilityLifecycleState, CapabilityLifecycleState]> = [ ["created", "cold_start"], ["cold_start", "graduated"], ["cold_start", "degraded"], ["graduated", "live"], ["graduated", "degraded"], ["live", "degraded"], ["degraded", "cold_start"], ["degraded", "live"], ]; test("lifecycle: isLegalTransition matches expected matrix", () => { for (const [from, to] of LEGAL) { assert.equal(isLegalTransition(from, to), true, `${from}→${to} should be legal`); } // self-transition is not legal for (const s of ALL_LIFECYCLE_STATES) { assert.equal(isLegalTransition(s, s), false, `self ${s}→${s} should not be legal`); } // a known illegal jump assert.equal(isLegalTransition("created", "live"), false); assert.equal(isLegalTransition("created", "degraded"), false); assert.equal(isLegalTransition("graduated", "cold_start"), false); assert.equal(isLegalTransition("live", "created"), false); }); test("lifecycle: assertLegalTransition throws IllegalLifecycleTransitionError on bad jump", () => { assert.throws( () => assertLegalTransition("created", "live"), (err: unknown) => err instanceof IllegalLifecycleTransitionError, ); }); const SKIP_DB = !process.env["DATABASE_URL"]; test( "lifecycle: transitionTo persists capability_lifecycle_events row", { skip: SKIP_DB && "DATABASE_URL not set" }, async () => { const { transitionTo } = await import("../lifecycle.ts"); const { db, capabilityLifecycleEvents } = await import("@workspace/db"); const { eq } = await import("drizzle-orm"); const capabilityId = `cap_b1test_${Math.random().toString(16).slice(2, 8)}`; const r = await transitionTo({ capabilityId, from: "created", to: "cold_start", reason: "b1_test", payload: { foo: "bar" }, }); assert.ok(r.eventId); const rows = await db .select() .from(capabilityLifecycleEvents) .where(eq(capabilityLifecycleEvents.id, r.eventId)); assert.equal(rows.length, 1); assert.equal(rows[0]!.capabilityId, capabilityId); assert.equal(rows[0]!.toState, "cold_start"); assert.equal(rows[0]!.reason, "b1_test"); // cleanup await db .delete(capabilityLifecycleEvents) .where(eq(capabilityLifecycleEvents.id, r.eventId)); }, ); test( "lifecycle: transitionTo throws on illegal transition (no row written)", { skip: SKIP_DB && "DATABASE_URL not set" }, async () => { const { transitionTo } = await import("../lifecycle.ts"); await assert.rejects( () => transitionTo({ capabilityId: "cap_b1test_illegal", from: "created", to: "live", reason: "should_fail", }), (err: unknown) => err instanceof IllegalLifecycleTransitionError, ); }, );