From 514e318df90b0b96b2dcb0578e150c9a3562ea6c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 18 Feb 2026 12:20:56 +0000 Subject: [PATCH] test(config): dedupe io write config test setup --- src/config/io.write-config.test.ts | 161 ++++++++++++----------------- 1 file changed, 66 insertions(+), 95 deletions(-) diff --git a/src/config/io.write-config.test.ts b/src/config/io.write-config.test.ts index a926869d033..51d746f44f3 100644 --- a/src/config/io.write-config.test.ts +++ b/src/config/io.write-config.test.ts @@ -14,6 +14,7 @@ describe("config io write", () => { home: string; initialConfig: Record; env?: NodeJS.ProcessEnv; + logger?: { warn: (msg: string) => void; error: (msg: string) => void }; }) { const configPath = path.join(params.home, ".openclaw", "openclaw.json"); await fs.mkdir(path.dirname(configPath), { recursive: true }); @@ -22,32 +23,69 @@ describe("config io write", () => { const io = createConfigIO({ env: params.env ?? {}, homedir: () => params.home, - logger: silentLogger, + logger: params.logger ?? silentLogger, }); const snapshot = await io.readConfigFileSnapshot(); expect(snapshot.valid).toBe(true); return { configPath, io, snapshot }; } + async function writeTokenAuthAndReadConfig(params: { + io: { writeConfigFile: (config: Record) => Promise }; + snapshot: { config: Record }; + configPath: string; + }) { + const next = structuredClone(params.snapshot.config); + const gateway = + next.gateway && typeof next.gateway === "object" + ? (next.gateway as Record) + : {}; + next.gateway = { + ...gateway, + auth: { mode: "token" }, + }; + await params.io.writeConfigFile(next); + return JSON.parse(await fs.readFile(params.configPath, "utf-8")) as Record; + } + + async function writeGatewayPatchAndReadLastAuditEntry(params: { + home: string; + initialConfig: Record; + gatewayPatch: Record; + env?: NodeJS.ProcessEnv; + }) { + const { io, snapshot, configPath } = await writeConfigAndCreateIo({ + home: params.home, + initialConfig: params.initialConfig, + env: params.env, + logger: { + warn: vi.fn(), + error: vi.fn(), + }, + }); + const auditPath = path.join(params.home, ".openclaw", "logs", "config-audit.jsonl"); + const next = structuredClone(snapshot.config); + const gateway = + next.gateway && typeof next.gateway === "object" + ? (next.gateway as Record) + : {}; + next.gateway = { + ...gateway, + ...params.gatewayPatch, + }; + await io.writeConfigFile(next); + const lines = (await fs.readFile(auditPath, "utf-8")).trim().split("\n").filter(Boolean); + const last = JSON.parse(lines.at(-1) ?? "{}") as Record; + return { last, lines, configPath }; + } + it("persists caller changes onto resolved config without leaking runtime defaults", async () => { await withTempHome("openclaw-config-io-", async (home) => { const { configPath, io, snapshot } = await writeConfigAndCreateIo({ home, initialConfig: { gateway: { port: 18789 } }, }); - - const next = structuredClone(snapshot.config); - next.gateway = { - ...next.gateway, - auth: { mode: "token" }, - }; - - await io.writeConfigFile(next); - - const persisted = JSON.parse(await fs.readFile(configPath, "utf-8")) as Record< - string, - unknown - >; + const persisted = await writeTokenAuthAndReadConfig({ io, snapshot, configPath }); expect(persisted.gateway).toEqual({ port: 18789, auth: { mode: "token" }, @@ -79,16 +117,7 @@ describe("config io write", () => { gateway: { port: 18789 }, }, }); - - const next = structuredClone(snapshot.config); - next.gateway = { - ...next.gateway, - auth: { mode: "token" }, - }; - - await io.writeConfigFile(next); - - const persisted = JSON.parse(await fs.readFile(configPath, "utf-8")) as { + const persisted = (await writeTokenAuthAndReadConfig({ io, snapshot, configPath })) as { agents: { defaults: { cliBackends: { codex: { env: { OPENAI_API_KEY: string } } } } }; gateway: { port: number; auth: { mode: string } }; }; @@ -223,25 +252,16 @@ describe("config io write", () => { it("logs an overwrite audit entry when replacing an existing config file", async () => { await withTempHome("openclaw-config-io-", async (home) => { - const configPath = path.join(home, ".openclaw", "openclaw.json"); - await fs.mkdir(path.dirname(configPath), { recursive: true }); - await fs.writeFile( - configPath, - JSON.stringify({ gateway: { port: 18789 } }, null, 2), - "utf-8", - ); const warn = vi.fn(); - const io = createConfigIO({ + const { configPath, io, snapshot } = await writeConfigAndCreateIo({ + home, + initialConfig: { gateway: { port: 18789 } }, env: {} as NodeJS.ProcessEnv, - homedir: () => home, logger: { - warn, - error: vi.fn(), + warn: warn as (msg: string) => void, + error: vi.fn() as (msg: string) => void, }, }); - - const snapshot = await io.readConfigFileSnapshot(); - expect(snapshot.valid).toBe(true); const next = structuredClone(snapshot.config); next.gateway = { ...next.gateway, @@ -285,38 +305,13 @@ describe("config io write", () => { it("appends config write audit JSONL entries with forensic metadata", async () => { await withTempHome("openclaw-config-io-", async (home) => { - const configPath = path.join(home, ".openclaw", "openclaw.json"); - const auditPath = path.join(home, ".openclaw", "logs", "config-audit.jsonl"); - await fs.mkdir(path.dirname(configPath), { recursive: true }); - await fs.writeFile( - configPath, - JSON.stringify({ gateway: { port: 18789 } }, null, 2), - "utf-8", - ); - - const io = createConfigIO({ + const { configPath, lines, last } = await writeGatewayPatchAndReadLastAuditEntry({ + home, + initialConfig: { gateway: { port: 18789 } }, + gatewayPatch: { mode: "local" }, env: {} as NodeJS.ProcessEnv, - homedir: () => home, - logger: { - warn: vi.fn(), - error: vi.fn(), - }, }); - - const snapshot = await io.readConfigFileSnapshot(); - expect(snapshot.valid).toBe(true); - - const next = structuredClone(snapshot.config); - next.gateway = { - ...next.gateway, - mode: "local", - }; - - await io.writeConfigFile(next); - - const lines = (await fs.readFile(auditPath, "utf-8")).trim().split("\n").filter(Boolean); expect(lines.length).toBeGreaterThan(0); - const last = JSON.parse(lines.at(-1) ?? "{}") as Record; expect(last.source).toBe("config-io"); expect(last.event).toBe("config.write"); expect(last.configPath).toBe(configPath); @@ -330,40 +325,16 @@ describe("config io write", () => { it("records gateway watch session markers in config audit entries", async () => { await withTempHome("openclaw-config-io-", async (home) => { - const configPath = path.join(home, ".openclaw", "openclaw.json"); - const auditPath = path.join(home, ".openclaw", "logs", "config-audit.jsonl"); - await fs.mkdir(path.dirname(configPath), { recursive: true }); - await fs.writeFile( - configPath, - JSON.stringify({ gateway: { mode: "local" } }, null, 2), - "utf-8", - ); - - const io = createConfigIO({ + const { last } = await writeGatewayPatchAndReadLastAuditEntry({ + home, + initialConfig: { gateway: { mode: "local" } }, + gatewayPatch: { bind: "loopback" }, env: { OPENCLAW_WATCH_MODE: "1", OPENCLAW_WATCH_SESSION: "watch-session-1", OPENCLAW_WATCH_COMMAND: "gateway --force", } as NodeJS.ProcessEnv, - homedir: () => home, - logger: { - warn: vi.fn(), - error: vi.fn(), - }, }); - - const snapshot = await io.readConfigFileSnapshot(); - expect(snapshot.valid).toBe(true); - const next = structuredClone(snapshot.config); - next.gateway = { - ...next.gateway, - bind: "loopback", - }; - - await io.writeConfigFile(next); - - const lines = (await fs.readFile(auditPath, "utf-8")).trim().split("\n").filter(Boolean); - const last = JSON.parse(lines.at(-1) ?? "{}") as Record; expect(last.watchMode).toBe(true); expect(last.watchSession).toBe("watch-session-1"); expect(last.watchCommand).toBe("gateway --force");