91 lines
3.8 KiB
JavaScript
91 lines
3.8 KiB
JavaScript
/**
|
|
* PR-A Baseline Test 10: Session Crash Recovery
|
|
* Verifies: session state survives restart/reload scenarios
|
|
* Freezes: session persistence contract
|
|
*/
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert";
|
|
import { existsSync, readFileSync, writeFileSync, unlinkSync, statSync, mkdirSync, readdirSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { randomUUID } from "node:crypto";
|
|
|
|
const WORKSPACE = join(homedir(), ".openclaw", "workspace");
|
|
const TEMP_DIR = join(WORKSPACE, "test", "baseline", "tmp");
|
|
try { mkdirSync(TEMP_DIR, { recursive: true }); } catch {}
|
|
|
|
describe("test-10: Session Crash Recovery Baseline", () => {
|
|
it("10.01 - written data survives simulated crash (no fsync)", () => {
|
|
const path = join(TEMP_DIR, "crash-test.json");
|
|
const data = { id: randomUUID(), state: "running", timestamp: Date.now() };
|
|
writeFileSync(path, JSON.stringify(data), "utf8");
|
|
const recovered = JSON.parse(readFileSync(path, "utf8"));
|
|
assert.equal(recovered.id, data.id, "data must survive 'crash'");
|
|
assert.equal(recovered.state, "running", "state must be preserved");
|
|
try { unlinkSync(path); } catch {}
|
|
console.log("[BASELINE] crash recovery: data survives write");
|
|
});
|
|
|
|
it("10.02 - partial writes don't produce valid JSON (atomicity check)", () => {
|
|
const path = join(TEMP_DIR, "partial-crash.json");
|
|
writeFileSync(path, '{"id":"test","state":"runn', "utf8");
|
|
try {
|
|
JSON.parse(readFileSync(path, "utf8"));
|
|
assert.fail("partial JSON should not parse");
|
|
} catch (e) {
|
|
console.log("[BASELINE] partial write detected:", e.message.substring(0, 80));
|
|
assert.ok(e instanceof SyntaxError, "partial write must cause parse error");
|
|
}
|
|
try { unlinkSync(path); } catch {}
|
|
});
|
|
|
|
it("10.03 - session state file format is JSON", () => {
|
|
const sessionsDir = join(homedir(), ".openclaw", "agents", "main", "sessions");
|
|
if (existsSync(sessionsDir)) {
|
|
const files = readdirSync(sessionsDir);
|
|
const jsonFiles = files.filter(f => f.endsWith(".json"));
|
|
console.log("[BASELINE] session JSON files count:", jsonFiles.length);
|
|
if (jsonFiles.length > 0) {
|
|
for (const f of jsonFiles.slice(0, 3)) {
|
|
try {
|
|
const content = readFileSync(join(sessionsDir, f), "utf8");
|
|
JSON.parse(content);
|
|
console.log(`[BASELINE] ${f}: valid JSON, size=${content.length}`);
|
|
} catch (e) {
|
|
console.log(`[BASELINE] ${f}: invalid JSON — ${e.message.substring(0, 80)}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert.ok(true, "baseline frozen: session file format");
|
|
});
|
|
|
|
it("10.04 - memory index survives process restart simulation", () => {
|
|
const memoryDb = join(homedir(), ".openclaw", "memory", "main.sqlite");
|
|
if (existsSync(memoryDb)) {
|
|
const stat1 = statSync(memoryDb);
|
|
console.log("[BASELINE] memory DB before 'restart': size=%d, mtime=%s",
|
|
stat1.size, stat1.mtime.toISOString());
|
|
const stat2 = statSync(memoryDb);
|
|
assert.equal(stat2.size, stat1.size, "memory DB must survive re-read");
|
|
} else {
|
|
console.log("[BASELINE] no memory DB to check (valid baseline)");
|
|
}
|
|
assert.ok(true, "baseline frozen: memory index survival");
|
|
});
|
|
|
|
it("10.05 - workspace files survive across reads (persistence check)", () => {
|
|
const criticalFiles = ["MEMORY.md", "AGENTS.md"];
|
|
for (const f of criticalFiles) {
|
|
const path = join(WORKSPACE, f);
|
|
if (existsSync(path)) {
|
|
const content1 = readFileSync(path, "utf8");
|
|
const content2 = readFileSync(path, "utf8");
|
|
assert.equal(content1.length, content2.length,
|
|
`${f} must be identical across reads`);
|
|
console.log(`[BASELINE] ${f}: persistent read OK, len=${content1.length}`);
|
|
}
|
|
}
|
|
});
|
|
});
|