

























@@ -12,6 +12,7 @@ import { resolveInstalledPluginIndexPolicyHash } from "../plugins/installed-plug
1212import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
1313import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
1414import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
15+import { captureFullEnv, deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js";
1516import { loadDotEnv, loadWorkspaceDotEnvFile } from "./dotenv.js";
16171718const loggerMocks = vi.hoisted(() => ({
@@ -80,7 +81,7 @@ async function writeEnvFile(filePath: string, contents: string) {
80818182function clearEnv(keys: readonly string[]) {
8283for (const key of keys) {
83-delete process.env[key];
84+deleteTestEnvValue(key);
8485}
8586}
8687@@ -91,23 +92,12 @@ function expectEnvUndefined(keys: readonly string[]) {
9192}
92939394async function withIsolatedEnvAndCwd(run: () => Promise<void>) {
94-const prevEnv = { ...process.env };
95+const envSnapshot = captureFullEnv();
9596try {
9697await run();
9798} finally {
9899vi.restoreAllMocks();
99-for (const key of Object.keys(process.env)) {
100-if (!(key in prevEnv)) {
101-delete process.env[key];
102-}
103-}
104-for (const [key, value] of Object.entries(prevEnv)) {
105-if (value === undefined) {
106-delete process.env[key];
107-} else {
108-process.env[key] = value;
109-}
110-}
100+envSnapshot.restore();
111101}
112102}
113103@@ -169,7 +159,7 @@ async function withDotEnvFixture(run: (fixture: DotEnvFixture) => Promise<void>)
169159const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-dotenv-test-"));
170160const cwdDir = path.join(base, "cwd");
171161const stateDir = path.join(base, "state");
172-process.env.OPENCLAW_STATE_DIR = stateDir;
162+setTestEnvValue("OPENCLAW_STATE_DIR", stateDir);
173163await fs.mkdir(cwdDir, { recursive: true });
174164await fs.mkdir(stateDir, { recursive: true });
175165await run({ base, cwdDir, stateDir });
@@ -228,9 +218,9 @@ describe("loadDotEnv", () => {
228218it("loads the Ubuntu gateway.env compatibility fallback after ~/.openclaw/.env", async () => {
229219await withIsolatedEnvAndCwd(async () => {
230220await withDotEnvFixture(async ({ base, cwdDir }) => {
231-process.env.HOME = base;
221+setTestEnvValue("HOME", base);
232222const defaultStateDir = path.join(base, ".openclaw");
233-process.env.OPENCLAW_STATE_DIR = defaultStateDir;
223+setTestEnvValue("OPENCLAW_STATE_DIR", defaultStateDir);
234224await writeEnvFile(path.join(defaultStateDir, ".env"), "FOO=from-global\n");
235225await writeEnvFile(
236226path.join(base, ".config", "openclaw", "gateway.env"),
@@ -259,7 +249,7 @@ describe("loadDotEnv", () => {
259249it("does not warn about dotenv conflicts when the key is already set", async () => {
260250await withIsolatedEnvAndCwd(async () => {
261251await withDotEnvFixture(async ({ base, cwdDir, stateDir }) => {
262-process.env.HOME = base;
252+setTestEnvValue("HOME", base);
263253process.env.FOO = "from-shell";
264254await writeEnvFile(path.join(stateDir, ".env"), "FOO=from-global\n");
265255await writeEnvFile(
@@ -314,7 +304,7 @@ describe("loadDotEnv", () => {
314304delete process.env.NODE_REPL_EXTERNAL_MODULE;
315305delete process.env.NODE_REPL_HISTORY;
316306delete process.env.NODE_V8_COVERAGE;
317-delete process.env.OPENCLAW_CONFIG_PATH;
307+deleteTestEnvValue("OPENCLAW_CONFIG_PATH");
318308delete process.env.ANTHROPIC_BASE_URL;
319309delete process.env.CLOUDSDK_PYTHON;
320310delete process.env.EXAMPLE_API_HOST;
@@ -396,9 +386,9 @@ describe("loadDotEnv", () => {
396386].join("\n"),
397387);
398388399-delete process.env.OPENCLAW_STATE_DIR;
389+deleteTestEnvValue("OPENCLAW_STATE_DIR");
400390delete process.env.STATE_DIRECTORY;
401-delete process.env.OPENCLAW_CONFIG_PATH;
391+deleteTestEnvValue("OPENCLAW_CONFIG_PATH");
402392403393loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
404394@@ -453,7 +443,7 @@ describe("loadDotEnv", () => {
453443].join("\n"),
454444);
455445456-delete process.env.OPENCLAW_AGENT_DIR;
446+deleteTestEnvValue("OPENCLAW_AGENT_DIR");
457447delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
458448delete process.env.OPENCLAW_OAUTH_DIR;
459449delete process.env.PI_CODING_AGENT_DIR;
@@ -548,7 +538,7 @@ describe("loadDotEnv", () => {
548538await withDotEnvFixture(async ({ cwdDir }) => {
549539await writeEnvFile(path.join(cwdDir, ".env"), `${key}=./evil/npm-cli.js\n`);
550540551-delete process.env[key];
541+deleteTestEnvValue(key);
552542553543loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
554544@@ -656,7 +646,7 @@ describe("loadCliDotEnv", () => {
656646657647// Delete the fixture-provided value so the blocking must come from
658648// the workspace blocklist, not the "already set" skip.
659-delete process.env.OPENCLAW_STATE_DIR;
649+deleteTestEnvValue("OPENCLAW_STATE_DIR");
660650vi.spyOn(process, "cwd").mockReturnValue(cwdDir);
661651662652loadCliDotEnv({ quiet: true });
@@ -669,9 +659,9 @@ describe("loadCliDotEnv", () => {
669659it("loads the gateway.env compatibility fallback during CLI startup", async () => {
670660await withIsolatedEnvAndCwd(async () => {
671661await withDotEnvFixture(async ({ base, cwdDir }) => {
672-process.env.HOME = base;
662+setTestEnvValue("HOME", base);
673663const defaultStateDir = path.join(base, ".openclaw");
674-process.env.OPENCLAW_STATE_DIR = defaultStateDir;
664+setTestEnvValue("OPENCLAW_STATE_DIR", defaultStateDir);
675665await writeEnvFile(path.join(defaultStateDir, ".env"), "FOO=from-global\n");
676666await writeEnvFile(
677667path.join(base, ".config", "openclaw", "gateway.env"),
@@ -693,9 +683,9 @@ describe("loadCliDotEnv", () => {
693683it("can defer global dotenv while loading only workspace env", async () => {
694684await withIsolatedEnvAndCwd(async () => {
695685await withDotEnvFixture(async ({ base, cwdDir }) => {
696-process.env.HOME = base;
686+setTestEnvValue("HOME", base);
697687const defaultStateDir = path.join(base, ".openclaw");
698-process.env.OPENCLAW_STATE_DIR = defaultStateDir;
688+setTestEnvValue("OPENCLAW_STATE_DIR", defaultStateDir);
699689await writeEnvFile(path.join(cwdDir, ".env"), "BAZ=from-workspace\n");
700690await writeEnvFile(path.join(defaultStateDir, ".env"), "FOO=from-global\n");
701691await writeEnvFile(
@@ -721,8 +711,8 @@ describe("loadCliDotEnv", () => {
721711await withIsolatedEnvAndCwd(async () => {
722712await withDotEnvFixture(async ({ base, cwdDir }) => {
723713const customStateDir = path.join(base, "custom-state");
724-process.env.HOME = base;
725-process.env.OPENCLAW_STATE_DIR = customStateDir;
714+setTestEnvValue("HOME", base);
715+setTestEnvValue("OPENCLAW_STATE_DIR", customStateDir);
726716await writeEnvFile(
727717path.join(base, ".config", "openclaw", "gateway.env"),
728718"FOO=from-gateway\n",
@@ -745,8 +735,8 @@ describe("loadCliDotEnv", () => {
745735const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-dotenv-legacy-"));
746736const cwdDir = path.join(base, "cwd");
747737const legacyStateDir = path.join(base, ".clawdbot");
748-process.env.HOME = base;
749-delete process.env.OPENCLAW_STATE_DIR;
738+setTestEnvValue("HOME", base);
739+deleteTestEnvValue("OPENCLAW_STATE_DIR");
750740delete process.env.OPENCLAW_TEST_FAST;
751741await fs.mkdir(cwdDir, { recursive: true });
752742await writeEnvFile(path.join(legacyStateDir, ".env"), "LEGACY_ONLY=from-legacy\n");
@@ -800,7 +790,7 @@ describe("loadCliDotEnv", () => {
800790801791vi.spyOn(process, "cwd").mockReturnValue(cwdDir);
802792delete process.env.SAFE_KEY;
803-delete process.env.OPENCLAW_CONFIG_PATH;
793+deleteTestEnvValue("OPENCLAW_CONFIG_PATH");
804794delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
805795delete process.env.NODE_OPTIONS;
806796delete process.env.NODE_REDIRECT_WARNINGS;
@@ -970,7 +960,7 @@ describe("workspace .env blocklist completeness", () => {
970960);
971961972962for (const key of runtimeControlKeys) {
973-delete process.env[key];
963+deleteTestEnvValue(key);
974964}
975965976966loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。