67 lines
2.7 KiB
JavaScript
67 lines
2.7 KiB
JavaScript
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");
|
|
});
|