diff --git a/src/infra/brew.test.ts b/src/infra/brew.test.ts index 87e34a3a9..536df52c1 100644 --- a/src/infra/brew.test.ts +++ b/src/infra/brew.test.ts @@ -5,43 +5,80 @@ import { describe, expect, it } from "vitest"; import { resolveBrewExecutable, resolveBrewPathDirs } from "./brew.js"; describe("brew helpers", () => { - it("resolves brew from ~/.linuxbrew/bin when executable exists", async () => { + async function withBrewRoot(run: (tmp: string) => Promise) { const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-brew-")); try { + await run(tmp); + } finally { + await fs.rm(tmp, { recursive: true, force: true }); + } + } + + async function writeExecutable(filePath: string) { + await fs.mkdir(path.dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, "#!/bin/sh\necho ok\n", "utf-8"); + await fs.chmod(filePath, 0o755); + } + + it("resolves brew from ~/.linuxbrew/bin when executable exists", async () => { + await withBrewRoot(async (tmp) => { const homebrewBin = path.join(tmp, ".linuxbrew", "bin"); - await fs.mkdir(homebrewBin, { recursive: true }); const brewPath = path.join(homebrewBin, "brew"); - await fs.writeFile(brewPath, "#!/bin/sh\necho ok\n", "utf-8"); - await fs.chmod(brewPath, 0o755); + await writeExecutable(brewPath); const env: NodeJS.ProcessEnv = {}; expect(resolveBrewExecutable({ homeDir: tmp, env })).toBe(brewPath); - } finally { - await fs.rm(tmp, { recursive: true, force: true }); - } + }); }); it("prefers HOMEBREW_PREFIX/bin/brew when present", async () => { - const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-brew-")); - try { + await withBrewRoot(async (tmp) => { const prefix = path.join(tmp, "prefix"); const prefixBin = path.join(prefix, "bin"); - await fs.mkdir(prefixBin, { recursive: true }); const prefixBrew = path.join(prefixBin, "brew"); - await fs.writeFile(prefixBrew, "#!/bin/sh\necho ok\n", "utf-8"); - await fs.chmod(prefixBrew, 0o755); + await writeExecutable(prefixBrew); const homebrewBin = path.join(tmp, ".linuxbrew", "bin"); - await fs.mkdir(homebrewBin, { recursive: true }); const homebrewBrew = path.join(homebrewBin, "brew"); - await fs.writeFile(homebrewBrew, "#!/bin/sh\necho ok\n", "utf-8"); - await fs.chmod(homebrewBrew, 0o755); + await writeExecutable(homebrewBrew); const env: NodeJS.ProcessEnv = { HOMEBREW_PREFIX: prefix }; expect(resolveBrewExecutable({ homeDir: tmp, env })).toBe(prefixBrew); - } finally { - await fs.rm(tmp, { recursive: true, force: true }); - } + }); + }); + + it("prefers HOMEBREW_BREW_FILE over prefix and trims value", async () => { + await withBrewRoot(async (tmp) => { + const explicit = path.join(tmp, "custom", "brew"); + const prefix = path.join(tmp, "prefix"); + const prefixBrew = path.join(prefix, "bin", "brew"); + await writeExecutable(explicit); + await writeExecutable(prefixBrew); + + const env: NodeJS.ProcessEnv = { + HOMEBREW_BREW_FILE: ` ${explicit} `, + HOMEBREW_PREFIX: prefix, + }; + expect(resolveBrewExecutable({ homeDir: tmp, env })).toBe(explicit); + }); + }); + + it("falls back to prefix when HOMEBREW_BREW_FILE is not executable", async () => { + await withBrewRoot(async (tmp) => { + const explicit = path.join(tmp, "custom", "brew"); + const prefix = path.join(tmp, "prefix"); + const prefixBrew = path.join(prefix, "bin", "brew"); + await fs.mkdir(path.dirname(explicit), { recursive: true }); + await fs.writeFile(explicit, "#!/bin/sh\necho no\n", "utf-8"); + await fs.chmod(explicit, 0o644); + await writeExecutable(prefixBrew); + + const env: NodeJS.ProcessEnv = { + HOMEBREW_BREW_FILE: explicit, + HOMEBREW_PREFIX: prefix, + }; + expect(resolveBrewExecutable({ homeDir: tmp, env })).toBe(prefixBrew); + }); }); it("includes Linuxbrew bin/sbin in path candidates", () => {