From 9a0b26cafca76d267dd231b5eaceadb071ec749d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 03:55:16 +0100 Subject: [PATCH] test: cover config recovery policy edges --- CHANGELOG.md | 1 + src/config/recovery-policy.test.ts | 68 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/config/recovery-policy.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c6219499be7..96ab09e9a83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Config/recovery: skip whole-file last-known-good rollback when invalidity is scoped to `plugins.entries.*`, preserving unrelated user settings during plugin schema or host-version skew. Fixes #71289. Thanks @jalehman. - Compaction: honor explicit `agents.defaults.compaction.keepRecentTokens` for manual `/compact`, re-distill safeguard summaries instead of snowballing previous summaries, and enable safeguard summary quality checks by default. Fixes #71357. Thanks @WhiteGiverMa. - Sessions: honor configured `session.maintenance` settings during load-time maintenance instead of falling back to default entry caps. Fixes #71356. Thanks @comolago. - Browser/sandbox: pass the resolved `browser.ssrfPolicy` into sandbox browser bridges and refresh cached bridges when the effective policy changes, so sandboxed browser navigation honors private-network opt-ins. Fixes #45153 and #57055. Thanks @jzakirov, @zuoanCo, and @kybrcore. diff --git a/src/config/recovery-policy.test.ts b/src/config/recovery-policy.test.ts new file mode 100644 index 00000000000..203430ec2e9 --- /dev/null +++ b/src/config/recovery-policy.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, it } from "vitest"; +import { + isPluginLocalInvalidConfigSnapshot, + shouldAttemptLastKnownGoodRecovery, +} from "./recovery-policy.js"; +import type { ConfigFileSnapshot } from "./types.openclaw.js"; + +type PolicySnapshot = Pick; + +function snapshot(params: Partial): PolicySnapshot { + return { + valid: false, + issues: [], + legacyIssues: [], + ...params, + }; +} + +describe("config recovery policy", () => { + it("skips whole-file recovery for issues scoped only to plugin entries", () => { + const current = snapshot({ + issues: [ + { + path: "plugins.entries.feishu", + message: "plugin requires newer host", + }, + { + path: "plugins.entries.lossless-claw.config.cacheAwareCompaction", + message: "invalid config: must NOT have additional properties", + }, + ], + }); + + expect(isPluginLocalInvalidConfigSnapshot(current)).toBe(true); + expect(shouldAttemptLastKnownGoodRecovery(current)).toBe(false); + }); + + it("keeps recovery enabled for mixed plugin and root config invalidity", () => { + const current = snapshot({ + issues: [ + { path: "plugins.entries.feishu", message: "plugin requires newer host" }, + { path: "gateway.mode", message: "Expected string" }, + ], + }); + + expect(isPluginLocalInvalidConfigSnapshot(current)).toBe(false); + expect(shouldAttemptLastKnownGoodRecovery(current)).toBe(true); + }); + + it("keeps recovery enabled for ambiguous plugin collection issues", () => { + const current = snapshot({ + issues: [{ path: "plugins.entries", message: "Expected object" }], + }); + + expect(isPluginLocalInvalidConfigSnapshot(current)).toBe(false); + expect(shouldAttemptLastKnownGoodRecovery(current)).toBe(true); + }); + + it("keeps recovery enabled when legacy config issues are present", () => { + const current = snapshot({ + issues: [{ path: "plugins.entries.feishu", message: "plugin requires newer host" }], + legacyIssues: [{ path: "heartbeat", message: "Use agents.defaults.heartbeat" }], + }); + + expect(isPluginLocalInvalidConfigSnapshot(current)).toBe(false); + expect(shouldAttemptLastKnownGoodRecovery(current)).toBe(true); + }); +});