





















11// Covers best-effort config IO reads and warning behavior.
22import fs from "node:fs/promises";
33import { describe, expect, it, vi } from "vitest";
4+import { withEnvAsync } from "../test-utils/env.js";
45import {
56readBestEffortConfig,
67readBestEffortConfigSnapshot,
@@ -30,9 +31,7 @@ describe("readBestEffortConfig", () => {
3031it("can read snapshots without applying config env vars to the process", async () => {
3132await withTempHome(async (home) => {
3233const key = "OPENCLAW_ISOLATED_CONFIG_READ_TEST";
33-const previous = process.env[key];
34-delete process.env[key];
35-try {
34+await withEnvAsync({ [key]: undefined }, async () => {
3635await writeOpenClawConfig(home, {
3736env: { vars: { [key]: "from-config" } },
3837gateway: { mode: "local" },
@@ -41,22 +40,14 @@ describe("readBestEffortConfig", () => {
4140await readConfigFileSnapshot({ isolateEnv: true, observe: false });
42414342expect(process.env[key]).toBeUndefined();
44-} finally {
45-if (previous === undefined) {
46-delete process.env[key];
47-} else {
48-process.env[key] = previous;
49-}
50-}
43+});
5144});
5245});
53465447it("resolves config env above exact lower-precedence values in isolated snapshots", async () => {
5548await withTempHome(async (home) => {
5649const key = "OPENCLAW_GATEWAY_TOKEN";
57-const previous = process.env[key];
58-process.env[key] = "shell-token";
59-try {
50+await withEnvAsync({ [key]: "shell-token" }, async () => {
6051await writeOpenClawConfig(home, {
6152env: { vars: { [key]: "config-token" } },
6253gateway: { auth: { mode: "token", token: `\${${key}}` }, mode: "local" },
@@ -70,23 +61,13 @@ describe("readBestEffortConfig", () => {
70617162expect(snapshot.config.gateway?.auth?.token).toBe("config-token");
7263expect(process.env[key]).toBe("shell-token");
73-} finally {
74-if (previous === undefined) {
75-delete process.env[key];
76-} else {
77-process.env[key] = previous;
78-}
79-}
64+});
8065});
8166});
82678368it("resolves config env above normalized lower-precedence aliases in isolated snapshots", async () => {
8469await withTempHome(async (home) => {
85-const previousCanonical = process.env.ZAI_API_KEY;
86-const previousLegacy = process.env.Z_AI_API_KEY;
87-process.env.ZAI_API_KEY = "shell-token";
88-delete process.env.Z_AI_API_KEY;
89-try {
70+await withEnvAsync({ ZAI_API_KEY: "shell-token", Z_AI_API_KEY: undefined }, async () => {
9071await writeOpenClawConfig(home, {
9172env: { vars: { Z_AI_API_KEY: "config-token" } },
9273gateway: { auth: { mode: "token", token: "${ZAI_API_KEY}" }, mode: "local" },
@@ -101,28 +82,13 @@ describe("readBestEffortConfig", () => {
10182expect(snapshot.config.gateway?.auth?.token).toBe("config-token");
10283expect(process.env.ZAI_API_KEY).toBe("shell-token");
10384expect(process.env.Z_AI_API_KEY).toBeUndefined();
104-} finally {
105-if (previousCanonical === undefined) {
106-delete process.env.ZAI_API_KEY;
107-} else {
108-process.env.ZAI_API_KEY = previousCanonical;
109-}
110-if (previousLegacy === undefined) {
111-delete process.env.Z_AI_API_KEY;
112-} else {
113-process.env.Z_AI_API_KEY = previousLegacy;
114-}
115-}
85+});
11686});
11787});
1188811989it("resolves config aliases from a higher-precedence canonical value in isolated snapshots", async () => {
12090await withTempHome(async (home) => {
121-const previousCanonical = process.env.ZAI_API_KEY;
122-const previousLegacy = process.env.Z_AI_API_KEY;
123-process.env.ZAI_API_KEY = "invocation-token";
124-delete process.env.Z_AI_API_KEY;
125-try {
91+await withEnvAsync({ ZAI_API_KEY: "invocation-token", Z_AI_API_KEY: undefined }, async () => {
12692await writeOpenClawConfig(home, {
12793env: { vars: { Z_AI_API_KEY: "config-token" } },
12894gateway: { auth: { mode: "token", token: "${Z_AI_API_KEY}" }, mode: "local" },
@@ -136,27 +102,14 @@ describe("readBestEffortConfig", () => {
136102expect(snapshot.config.gateway?.auth?.token).toBe("invocation-token");
137103expect(process.env.ZAI_API_KEY).toBe("invocation-token");
138104expect(process.env.Z_AI_API_KEY).toBeUndefined();
139-} finally {
140-if (previousCanonical === undefined) {
141-delete process.env.ZAI_API_KEY;
142-} else {
143-process.env.ZAI_API_KEY = previousCanonical;
144-}
145-if (previousLegacy === undefined) {
146-delete process.env.Z_AI_API_KEY;
147-} else {
148-process.env.Z_AI_API_KEY = previousLegacy;
149-}
150-}
105+});
151106});
152107});
153108154109it("can read best-effort config without applying env vars or recording observation", async () => {
155110await withTempHome(async (home) => {
156111const key = "OPENCLAW_ISOLATED_BEST_EFFORT_CONFIG_TEST";
157-const previous = process.env[key];
158-delete process.env[key];
159-try {
112+await withEnvAsync({ [key]: undefined }, async () => {
160113await writeOpenClawConfig(home, {
161114env: { vars: { [key]: "from-config" } },
162115gateway: { mode: "local" },
@@ -169,49 +122,33 @@ describe("readBestEffortConfig", () => {
169122await expect(fs.stat(`${home}/.openclaw/logs/config-health.json`)).rejects.toMatchObject({
170123code: "ENOENT",
171124});
172-} finally {
173-if (previous === undefined) {
174-delete process.env[key];
175-} else {
176-process.env[key] = previous;
177-}
178-}
125+});
179126});
180127});
181128182129it("preserves Windows case-insensitive env lookup in isolated reads", async () => {
183130await withTempHome(async (home) => {
184131const mixedCaseKey = "OpenClaw_Config_Path";
185132const customConfigPath = `${home}/custom-openclaw.json`;
186-const previousMixedCasePath = process.env[mixedCaseKey];
187-const previousConfigPath = process.env.OPENCLAW_CONFIG_PATH;
188-const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
189-delete process.env.OPENCLAW_CONFIG_PATH;
190-process.env[mixedCaseKey] = customConfigPath;
191-try {
192-await fs.writeFile(
193-customConfigPath,
194-`${JSON.stringify({ gateway: { mode: "local" } }, null, 2)}\n`,
195-"utf-8",
196-);
197-198-const snapshot = await readConfigFileSnapshot({ isolateEnv: true, observe: false });
199-200-expect(snapshot.exists).toBe(true);
201-expect(snapshot.path).toBe(customConfigPath);
202-} finally {
203-platformSpy.mockRestore();
204-if (previousMixedCasePath === undefined) {
205-delete process.env[mixedCaseKey];
206-} else {
207-process.env[mixedCaseKey] = previousMixedCasePath;
208-}
209-if (previousConfigPath === undefined) {
210-delete process.env.OPENCLAW_CONFIG_PATH;
211-} else {
212-process.env.OPENCLAW_CONFIG_PATH = previousConfigPath;
213-}
214-}
133+await withEnvAsync({ OPENCLAW_CONFIG_PATH: undefined }, async () => {
134+await withEnvAsync({ [mixedCaseKey]: customConfigPath }, async () => {
135+const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
136+try {
137+await fs.writeFile(
138+customConfigPath,
139+`${JSON.stringify({ gateway: { mode: "local" } }, null, 2)}\n`,
140+"utf-8",
141+);
142+143+const snapshot = await readConfigFileSnapshot({ isolateEnv: true, observe: false });
144+145+expect(snapshot.exists).toBe(true);
146+expect(snapshot.path).toBe(customConfigPath);
147+} finally {
148+platformSpy.mockRestore();
149+}
150+});
151+});
215152});
216153});
217154此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。