import fs from "node:fs"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { withTempDir } from "./test-helpers/temp-dir.js"; import { ensureDir, resolveConfigDir, resolveHomeDir, resolveUserPath, shortenHomeInString, shortenHomePath, sleep, } from "./utils.js"; describe("ensureDir", () => { it("creates nested directory", async () => { await withTempDir({ prefix: "openclaw-test-" }, async (tmp) => { const target = path.join(tmp, "nested", "dir"); await ensureDir(target); expect(fs.existsSync(target)).toBe(true); }); }); }); describe("sleep", () => { it("resolves after delay using fake timers", async () => { vi.useFakeTimers(); const promise = sleep(1000); vi.advanceTimersByTime(1000); await expect(promise).resolves.toBeUndefined(); vi.useRealTimers(); }); }); describe("resolveConfigDir", () => { it("prefers ~/.openclaw when legacy dir is missing", async () => { await withTempDir({ prefix: "openclaw-config-dir-" }, async (root) => { const newDir = path.join(root, ".openclaw"); await fs.promises.mkdir(newDir, { recursive: true }); const resolved = resolveConfigDir({} as NodeJS.ProcessEnv, () => root); expect(resolved).toBe(newDir); }); }); it("expands OPENCLAW_STATE_DIR using the provided env", () => { const env = { HOME: "/tmp/openclaw-home", OPENCLAW_STATE_DIR: "~/state", } as NodeJS.ProcessEnv; expect(resolveConfigDir(env)).toBe(path.resolve("/tmp/openclaw-home", "state")); }); it("falls back to the config file directory when only OPENCLAW_CONFIG_PATH is set", () => { const env = { HOME: "/tmp/openclaw-home", OPENCLAW_CONFIG_PATH: "~/profiles/dev/openclaw.json", } as NodeJS.ProcessEnv; expect(resolveConfigDir(env)).toBe(path.resolve("/tmp/openclaw-home", "profiles", "dev")); }); }); describe("resolveHomeDir", () => { it("prefers OPENCLAW_HOME over HOME", () => { vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("HOME", "/home/other"); expect(resolveHomeDir()).toBe(path.resolve("/srv/openclaw-home")); vi.unstubAllEnvs(); }); }); describe("shortenHomePath", () => { it("uses $OPENCLAW_HOME prefix when OPENCLAW_HOME is set", () => { vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("HOME", "/home/other"); expect(shortenHomePath(`${path.resolve("/srv/openclaw-home")}/.openclaw/openclaw.json`)).toBe( "$OPENCLAW_HOME/.openclaw/openclaw.json", ); vi.unstubAllEnvs(); }); }); describe("shortenHomeInString", () => { it("uses $OPENCLAW_HOME replacement when OPENCLAW_HOME is set", () => { vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("HOME", "/home/other"); expect( shortenHomeInString(`config: ${path.resolve("/srv/openclaw-home")}/.openclaw/openclaw.json`), ).toBe("config: $OPENCLAW_HOME/.openclaw/openclaw.json"); vi.unstubAllEnvs(); }); }); describe("resolveUserPath", () => { it("expands ~ to home dir", () => { expect(resolveUserPath("~", {}, () => "/Users/thoffman")).toBe(path.resolve("/Users/thoffman")); }); it("expands ~/ to home dir", () => { expect(resolveUserPath("~/openclaw", {}, () => "/Users/thoffman")).toBe( path.resolve("/Users/thoffman", "openclaw"), ); }); it("resolves relative paths", () => { expect(resolveUserPath("tmp/dir")).toBe(path.resolve("tmp/dir")); }); it("prefers OPENCLAW_HOME for tilde expansion", () => { vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("HOME", "/home/other"); expect(resolveUserPath("~/openclaw")).toBe(path.resolve("/srv/openclaw-home", "openclaw")); vi.unstubAllEnvs(); }); it("uses the provided env for tilde expansion", () => { const env = { HOME: "/tmp/openclaw-home", OPENCLAW_HOME: "/srv/openclaw-home", } as NodeJS.ProcessEnv; expect(resolveUserPath("~/openclaw", env)).toBe(path.resolve("/srv/openclaw-home", "openclaw")); }); it("keeps blank paths blank", () => { expect(resolveUserPath("")).toBe(""); expect(resolveUserPath(" ")).toBe(""); }); it("returns empty string for undefined/null input", () => { expect(resolveUserPath(undefined as unknown as string)).toBe(""); expect(resolveUserPath(null as unknown as string)).toBe(""); }); });