Advance M7 workflows and release operations
This commit is contained in:
66
web/tests/unit/save-transaction.test.mjs
Normal file
66
web/tests/unit/save-transaction.test.mjs
Normal file
@@ -0,0 +1,66 @@
|
||||
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/save-transaction.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 {
|
||||
SAVE_ATTEMPT_STAGES,
|
||||
advanceSaveTransaction,
|
||||
beginSaveTransaction,
|
||||
commitSaveTransaction,
|
||||
createSaveTransactionState,
|
||||
failSaveTransaction,
|
||||
} = await import(moduleUrl);
|
||||
|
||||
const oldCommit = { revision: 7, sha256: "a".repeat(64) };
|
||||
const newCommit = { revision: 8, sha256: "b".repeat(64) };
|
||||
|
||||
function stateAt(stage) {
|
||||
let result = beginSaveTransaction(createSaveTransactionState(oldCommit), newCommit.revision);
|
||||
assert.equal(result.ok, true);
|
||||
for (const next of SAVE_ATTEMPT_STAGES.slice(1, SAVE_ATTEMPT_STAGES.indexOf(stage) + 1)) {
|
||||
result = advanceSaveTransaction(result.state, next, next === "OPFS_STAGE" ? newCommit.sha256 : undefined);
|
||||
assert.equal(result.ok, true);
|
||||
}
|
||||
return result.state;
|
||||
}
|
||||
|
||||
test("M7-05 preserves the old committed identity at every failed save stage", () => {
|
||||
for (const stage of SAVE_ATTEMPT_STAGES) {
|
||||
const failed = failSaveTransaction(stateAt(stage), `SAVE_${stage}_INTERRUPTED`);
|
||||
assert.equal(failed.status, "FAILED");
|
||||
assert.equal(failed.stage, stage);
|
||||
assert.deepEqual(failed.committed, oldCommit);
|
||||
assert.equal(failed.candidate.revision, newCommit.revision);
|
||||
}
|
||||
});
|
||||
|
||||
test("M7-05 advances committed revision and hash only after matching metadata commit", () => {
|
||||
const metadata = stateAt("METADATA_COMMIT");
|
||||
assert.deepEqual(commitSaveTransaction(metadata, { ...newCommit, sha256: "c".repeat(64) }), {
|
||||
ok: false,
|
||||
state: metadata,
|
||||
errorCode: "SAVE_TRANSACTION_COMMIT_MISMATCH",
|
||||
});
|
||||
const committed = commitSaveTransaction(metadata, newCommit);
|
||||
assert.equal(committed.ok, true);
|
||||
assert.deepEqual(committed.state.committed, newCommit);
|
||||
assert.equal(committed.state.status, "SUCCEEDED");
|
||||
});
|
||||
|
||||
test("M7-05 rejects reentrant and out-of-order save transitions", () => {
|
||||
const running = beginSaveTransaction(createSaveTransactionState(oldCommit), 8);
|
||||
assert.equal(running.ok, true);
|
||||
assert.equal(beginSaveTransaction(running.state, 9).errorCode, "SAVE_TRANSACTION_INVALID_TRANSITION");
|
||||
assert.equal(advanceSaveTransaction(running.state, "SCENE_COMMIT").errorCode, "SAVE_TRANSACTION_INVALID_TRANSITION");
|
||||
});
|
||||
Reference in New Issue
Block a user