Advance M7 workflows and release operations
This commit is contained in:
93
web/tests/unit/user-action-state.test.mjs
Normal file
93
web/tests/unit/user-action-state.test.mjs
Normal file
@@ -0,0 +1,93 @@
|
||||
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",
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user