/** * 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}`); } } }); });