Files
16gagent/test/baseline/test-10-session-recovery.test.mjs
T
2026-06-06 10:40:48 +08:00

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