import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import test from "node:test"; import ts from "typescript"; const repoRoot = path.resolve(import.meta.dirname, "../../.."); const sourcePath = path.join(repoRoot, "web/protocol/user-action-state.ts"); const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: sourcePath, reportDiagnostics: true, }); assert.deepEqual(transpiled.diagnostics, []); const moduleUrl = `data:text/javascript;base64,${Buffer.from(transpiled.outputText).toString("base64")}`; const { USER_ACTION_KINDS, createInitialUserActionStates, reduceUserActionStates, transitionUserAction, } = await import(moduleUrl); function identity(kind, sequence = 1) { return { kind, actionId: `${kind}:${sequence}` }; } test("M7-01 initializes all five user actions at IDLE", () => { const states = createInitialUserActionStates(); assert.deepEqual(Object.keys(states), USER_ACTION_KINDS); for (const kind of USER_ACTION_KINDS) { assert.deepEqual(states[kind], { kind, status: "IDLE", identity: null, errorCode: null }); } }); test("M7-01 records deterministic RUNNING and SUCCEEDED transitions for every action", () => { let states = createInitialUserActionStates(); for (const kind of USER_ACTION_KINDS) { const currentIdentity = identity(kind); states = reduceUserActionStates(states, { type: "START", identity: currentIdentity }); assert.deepEqual(states[kind], { kind, status: "RUNNING", identity: currentIdentity, errorCode: null }); states = reduceUserActionStates(states, { type: "SUCCEED", identity: currentIdentity }); assert.deepEqual(states[kind], { kind, status: "SUCCEEDED", identity: currentIdentity, errorCode: null }); } }); test("M7-01 records stable failure and cancellation codes", () => { const failedIdentity = identity("OPEN"); let failed = transitionUserAction(createInitialUserActionStates().OPEN, { type: "START", identity: failedIdentity }); assert.equal(failed.ok, true); failed = transitionUserAction(failed.state, { type: "FAIL", identity: failedIdentity, errorCode: "OPEN_ENGINE_FAILED" }); assert.deepEqual(failed, { ok: true, state: { kind: "OPEN", status: "FAILED", identity: failedIdentity, errorCode: "OPEN_ENGINE_FAILED" }, }); const cancelledIdentity = identity("IMPORT"); let cancelled = transitionUserAction(createInitialUserActionStates().IMPORT, { type: "START", identity: cancelledIdentity }); assert.equal(cancelled.ok, true); cancelled = transitionUserAction(cancelled.state, { type: "CANCEL", identity: cancelledIdentity }); assert.deepEqual(cancelled, { ok: true, state: { kind: "IMPORT", status: "CANCELLED", identity: cancelledIdentity, errorCode: "USER_ACTION_CANCELLED" }, }); }); test("M7-01 rejects wrong identities, reused identities and invalid transitions without mutation", () => { const first = identity("SAVE"); const other = identity("SAVE", 2); const initial = createInitialUserActionStates().SAVE; const beforeStart = transitionUserAction(initial, { type: "SUCCEED", identity: first }); assert.deepEqual(beforeStart, { ok: false, state: initial, errorCode: "USER_ACTION_INVALID_TRANSITION" }); const running = transitionUserAction(initial, { type: "START", identity: first }); assert.equal(running.ok, true); assert.deepEqual(transitionUserAction(running.state, { type: "FAIL", identity: other, errorCode: "SAVE_FAILED" }), { ok: false, state: running.state, errorCode: "USER_ACTION_IDENTITY_MISMATCH", }); assert.deepEqual(transitionUserAction(running.state, { type: "FAIL", identity: first, errorCode: "not-stable" }), { ok: false, state: running.state, errorCode: "USER_ACTION_INVALID_ERROR_CODE", }); const succeeded = transitionUserAction(running.state, { type: "SUCCEED", identity: first }); assert.equal(succeeded.ok, true); assert.deepEqual(transitionUserAction(succeeded.state, { type: "START", identity: first }), { ok: false, state: succeeded.state, errorCode: "USER_ACTION_IDENTITY_REUSED", }); });