From c2874aead78430adc66ff62b25c6a86ec12961d9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 21 Feb 2026 12:59:14 +0000 Subject: [PATCH] refactor(test): centralize temporary state-dir env setup --- src/canvas-host/server.state-dir.test.ts | 53 +++++++-------------- src/infra/device-identity.state-dir.test.ts | 31 ++---------- src/test-helpers/state-dir-env.test.ts | 45 +++++++++++++++++ src/test-helpers/state-dir-env.ts | 20 ++++++++ 4 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 src/test-helpers/state-dir-env.test.ts diff --git a/src/canvas-host/server.state-dir.test.ts b/src/canvas-host/server.state-dir.test.ts index f5cc012e90e..744daef57d8 100644 --- a/src/canvas-host/server.state-dir.test.ts +++ b/src/canvas-host/server.state-dir.test.ts @@ -1,45 +1,28 @@ import fs from "node:fs/promises"; -import os from "node:os"; import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { defaultRuntime } from "../runtime.js"; -import { - restoreStateDirEnv, - setStateDirEnv, - snapshotStateDirEnv, -} from "../test-helpers/state-dir-env.js"; +import { withStateDirEnv } from "../test-helpers/state-dir-env.js"; import { createCanvasHostHandler } from "./server.js"; describe("canvas host state dir defaults", () => { - let envSnapshot: ReturnType; - - beforeEach(() => { - envSnapshot = snapshotStateDirEnv(); - }); - - afterEach(() => { - restoreStateDirEnv(envSnapshot); - }); - it("uses OPENCLAW_STATE_DIR for the default canvas root", async () => { - const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-canvas-state-")); - const stateDir = path.join(tempRoot, "state"); - setStateDirEnv(stateDir); - const handler = await createCanvasHostHandler({ - runtime: defaultRuntime, - allowInTests: true, - }); + await withStateDirEnv("openclaw-canvas-state-", async ({ stateDir }) => { + const handler = await createCanvasHostHandler({ + runtime: defaultRuntime, + allowInTests: true, + }); - try { - const expectedRoot = await fs.realpath(path.join(stateDir, "canvas")); - const actualRoot = await fs.realpath(handler.rootDir); - expect(actualRoot).toBe(expectedRoot); - const indexPath = path.join(expectedRoot, "index.html"); - const indexContents = await fs.readFile(indexPath, "utf8"); - expect(indexContents).toContain("OpenClaw Canvas"); - } finally { - await handler.close(); - await fs.rm(tempRoot, { recursive: true, force: true }); - } + try { + const expectedRoot = await fs.realpath(path.join(stateDir, "canvas")); + const actualRoot = await fs.realpath(handler.rootDir); + expect(actualRoot).toBe(expectedRoot); + const indexPath = path.join(expectedRoot, "index.html"); + const indexContents = await fs.readFile(indexPath, "utf8"); + expect(indexContents).toContain("OpenClaw Canvas"); + } finally { + await handler.close(); + } + }); }); }); diff --git a/src/infra/device-identity.state-dir.test.ts b/src/infra/device-identity.state-dir.test.ts index a119616e60a..71281344819 100644 --- a/src/infra/device-identity.state-dir.test.ts +++ b/src/infra/device-identity.state-dir.test.ts @@ -1,37 +1,16 @@ import fs from "node:fs/promises"; -import os from "node:os"; import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { - restoreStateDirEnv, - setStateDirEnv, - snapshotStateDirEnv, -} from "../test-helpers/state-dir-env.js"; +import { describe, expect, it } from "vitest"; +import { withStateDirEnv } from "../test-helpers/state-dir-env.js"; import { loadOrCreateDeviceIdentity } from "./device-identity.js"; describe("device identity state dir defaults", () => { - let envSnapshot: ReturnType; - - beforeEach(() => { - envSnapshot = snapshotStateDirEnv(); - }); - - afterEach(() => { - restoreStateDirEnv(envSnapshot); - }); - it("writes the default identity file under OPENCLAW_STATE_DIR", async () => { - const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-identity-state-")); - const stateDir = path.join(tempRoot, "state"); - setStateDirEnv(stateDir); - const identity = loadOrCreateDeviceIdentity(); - - try { + await withStateDirEnv("openclaw-identity-state-", async ({ stateDir }) => { + const identity = loadOrCreateDeviceIdentity(); const identityPath = path.join(stateDir, "identity", "device.json"); const raw = JSON.parse(await fs.readFile(identityPath, "utf8")) as { deviceId?: string }; expect(raw.deviceId).toBe(identity.deviceId); - } finally { - await fs.rm(tempRoot, { recursive: true, force: true }); - } + }); }); }); diff --git a/src/test-helpers/state-dir-env.test.ts b/src/test-helpers/state-dir-env.test.ts new file mode 100644 index 00000000000..744e42f868f --- /dev/null +++ b/src/test-helpers/state-dir-env.test.ts @@ -0,0 +1,45 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { + restoreStateDirEnv, + setStateDirEnv, + snapshotStateDirEnv, + withStateDirEnv, +} from "./state-dir-env.js"; + +describe("state-dir-env helpers", () => { + it("set/snapshot/restore round-trips OPENCLAW_STATE_DIR", () => { + const prevOpenClaw = process.env.OPENCLAW_STATE_DIR; + const prevLegacy = process.env.CLAWDBOT_STATE_DIR; + const snapshot = snapshotStateDirEnv(); + + setStateDirEnv("/tmp/openclaw-state-dir-test"); + expect(process.env.OPENCLAW_STATE_DIR).toBe("/tmp/openclaw-state-dir-test"); + expect(process.env.CLAWDBOT_STATE_DIR).toBeUndefined(); + + restoreStateDirEnv(snapshot); + expect(process.env.OPENCLAW_STATE_DIR).toBe(prevOpenClaw); + expect(process.env.CLAWDBOT_STATE_DIR).toBe(prevLegacy); + }); + + it("withStateDirEnv sets env for callback and cleans up temp root", async () => { + const prevOpenClaw = process.env.OPENCLAW_STATE_DIR; + const prevLegacy = process.env.CLAWDBOT_STATE_DIR; + + let capturedTempRoot = ""; + let capturedStateDir = ""; + await withStateDirEnv("openclaw-state-dir-env-", async ({ tempRoot, stateDir }) => { + capturedTempRoot = tempRoot; + capturedStateDir = stateDir; + expect(process.env.OPENCLAW_STATE_DIR).toBe(stateDir); + expect(process.env.CLAWDBOT_STATE_DIR).toBeUndefined(); + await fs.writeFile(path.join(stateDir, "probe.txt"), "ok", "utf8"); + }); + + expect(process.env.OPENCLAW_STATE_DIR).toBe(prevOpenClaw); + expect(process.env.CLAWDBOT_STATE_DIR).toBe(prevLegacy); + await expect(fs.stat(capturedStateDir)).rejects.toThrow(); + await expect(fs.stat(capturedTempRoot)).rejects.toThrow(); + }); +}); diff --git a/src/test-helpers/state-dir-env.ts b/src/test-helpers/state-dir-env.ts index 235cbca1c4e..db41718da60 100644 --- a/src/test-helpers/state-dir-env.ts +++ b/src/test-helpers/state-dir-env.ts @@ -1,3 +1,6 @@ +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; import { captureEnv } from "../test-utils/env.js"; export function snapshotStateDirEnv() { @@ -12,3 +15,20 @@ export function setStateDirEnv(stateDir: string): void { process.env.OPENCLAW_STATE_DIR = stateDir; delete process.env.CLAWDBOT_STATE_DIR; } + +export async function withStateDirEnv( + prefix: string, + fn: (ctx: { tempRoot: string; stateDir: string }) => Promise, +): Promise { + const snapshot = snapshotStateDirEnv(); + const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); + const stateDir = path.join(tempRoot, "state"); + await fs.mkdir(stateDir, { recursive: true }); + setStateDirEnv(stateDir); + try { + return await fn({ tempRoot, stateDir }); + } finally { + restoreStateDirEnv(snapshot); + await fs.rm(tempRoot, { recursive: true, force: true }); + } +}