| import fs from "node:fs/promises"; |
| import os from "node:os"; |
| import path from "node:path"; |
|
|
| import { describe, expect, it } from "vitest"; |
|
|
| import { loadDotEnv } from "./dotenv.js"; |
|
|
| async function writeEnvFile(filePath: string, contents: string) { |
| await fs.mkdir(path.dirname(filePath), { recursive: true }); |
| await fs.writeFile(filePath, contents, "utf8"); |
| } |
|
|
| describe("loadDotEnv", () => { |
| it("loads ~/.openclaw/.env as fallback without overriding CWD .env", async () => { |
| const prevEnv = { ...process.env }; |
| const prevCwd = process.cwd(); |
|
|
| const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-dotenv-test-")); |
| const cwdDir = path.join(base, "cwd"); |
| const stateDir = path.join(base, "state"); |
|
|
| process.env.OPENCLAW_STATE_DIR = stateDir; |
|
|
| await writeEnvFile(path.join(stateDir, ".env"), "FOO=from-global\nBAR=1\n"); |
| await writeEnvFile(path.join(cwdDir, ".env"), "FOO=from-cwd\n"); |
|
|
| process.chdir(cwdDir); |
| delete process.env.FOO; |
| delete process.env.BAR; |
|
|
| loadDotEnv({ quiet: true }); |
|
|
| expect(process.env.FOO).toBe("from-cwd"); |
| expect(process.env.BAR).toBe("1"); |
|
|
| process.chdir(prevCwd); |
| for (const key of Object.keys(process.env)) { |
| if (!(key in prevEnv)) { |
| delete process.env[key]; |
| } |
| } |
| for (const [key, value] of Object.entries(prevEnv)) { |
| if (value === undefined) { |
| delete process.env[key]; |
| } else { |
| process.env[key] = value; |
| } |
| } |
| }); |
|
|
| it("does not override an already-set env var from the shell", async () => { |
| const prevEnv = { ...process.env }; |
| const prevCwd = process.cwd(); |
|
|
| const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-dotenv-test-")); |
| const cwdDir = path.join(base, "cwd"); |
| const stateDir = path.join(base, "state"); |
|
|
| process.env.OPENCLAW_STATE_DIR = stateDir; |
| process.env.FOO = "from-shell"; |
|
|
| await writeEnvFile(path.join(stateDir, ".env"), "FOO=from-global\n"); |
| await writeEnvFile(path.join(cwdDir, ".env"), "FOO=from-cwd\n"); |
|
|
| process.chdir(cwdDir); |
|
|
| loadDotEnv({ quiet: true }); |
|
|
| expect(process.env.FOO).toBe("from-shell"); |
|
|
| process.chdir(prevCwd); |
| for (const key of Object.keys(process.env)) { |
| if (!(key in prevEnv)) { |
| delete process.env[key]; |
| } |
| } |
| for (const [key, value] of Object.entries(prevEnv)) { |
| if (value === undefined) { |
| delete process.env[key]; |
| } else { |
| process.env[key] = value; |
| } |
| } |
| }); |
| }); |
|
|