63 lines
2.6 KiB
TypeScript
63 lines
2.6 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
test("M7-05 restores old revision, hash and bytes after every storage save interruption", async ({ page }) => {
|
|
await page.goto("/");
|
|
const result = await page.evaluate(async () => {
|
|
const { StorageClient } = await import("/src/storage/StorageClient.ts");
|
|
const projectId = `m7-save-interrupt-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
const oldBytes = Uint8Array.from([0x42, 0x4c, 0x45, 0x4e, 0x44, 7, 7, 7]);
|
|
const nextBytes = Uint8Array.from([0x42, 0x4c, 0x45, 0x4e, 0x44, 8, 8, 8, 8]);
|
|
const initial = new StorageClient();
|
|
const baseline = await initial.saveProject(projectId, 7, oldBytes.slice().buffer);
|
|
initial.terminate();
|
|
|
|
const observations = [];
|
|
for (const faultAt of ["after-stage", "after-scene-commit", "before-metadata-commit"] as const) {
|
|
const attempt = new StorageClient();
|
|
let error = "";
|
|
try {
|
|
await attempt.saveProject(projectId, 8, nextBytes.slice().buffer, faultAt);
|
|
}
|
|
catch (reason) {
|
|
error = reason instanceof Error ? reason.message : String(reason);
|
|
}
|
|
attempt.terminate();
|
|
|
|
const restarted = new StorageClient();
|
|
const restored = await restarted.readProject(projectId);
|
|
restarted.terminate();
|
|
observations.push({
|
|
faultAt,
|
|
error,
|
|
revision: restored.revision,
|
|
sha256: restored.sha256,
|
|
bytes: Array.from(new Uint8Array(restored.buffer)),
|
|
recovered: restored.recovered,
|
|
});
|
|
}
|
|
|
|
const completed = new StorageClient();
|
|
const committed = await completed.saveProject(projectId, 8, nextBytes.slice().buffer);
|
|
const reopened = await completed.readProject(projectId);
|
|
completed.terminate();
|
|
return {
|
|
baseline: { revision: baseline.revision, sha256: baseline.sha256, bytes: Array.from(oldBytes) },
|
|
observations,
|
|
committed: { revision: committed.revision, sha256: committed.sha256, reopenedRevision: reopened.revision, reopenedSha256: reopened.sha256 },
|
|
};
|
|
});
|
|
|
|
expect(result.observations).toHaveLength(3);
|
|
for (const item of result.observations) {
|
|
expect(item.error).toContain(`PROJECT_SAVE_FAULT_INJECTED: ${item.faultAt}`);
|
|
expect(item.revision).toBe(result.baseline.revision);
|
|
expect(item.sha256).toBe(result.baseline.sha256);
|
|
expect(item.bytes).toEqual(result.baseline.bytes);
|
|
expect(item.recovered).toBe(false);
|
|
}
|
|
expect(result.committed.revision).toBe(8);
|
|
expect(result.committed.reopenedRevision).toBe(8);
|
|
expect(result.committed.sha256).toBe(result.committed.reopenedSha256);
|
|
expect(result.committed.sha256).not.toBe(result.baseline.sha256);
|
|
});
|