mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-12 08:00:48 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
230
openclaw/test/test-env.test.ts
Normal file
230
openclaw/test/test-env.test.ts
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { importFreshModule } from "./helpers/import-fresh.js";
|
||||
import { cleanupTempDirs, makeTempDir } from "./helpers/temp-dir.js";
|
||||
import { installTestEnv } from "./test-env.js";
|
||||
|
||||
const ORIGINAL_ENV = { ...process.env };
|
||||
|
||||
const tempDirs = new Set<string>();
|
||||
const cleanupFns: Array<() => void> = [];
|
||||
|
||||
function restoreProcessEnv(): void {
|
||||
for (const key of Object.keys(process.env)) {
|
||||
if (!(key in ORIGINAL_ENV)) {
|
||||
delete process.env[key];
|
||||
}
|
||||
}
|
||||
for (const [key, value] of Object.entries(ORIGINAL_ENV)) {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(targetPath: string, content: string): void {
|
||||
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
||||
fs.writeFileSync(targetPath, content, "utf8");
|
||||
}
|
||||
|
||||
function createTempHome(): string {
|
||||
return makeTempDir(tempDirs, "openclaw-test-env-real-home-");
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
while (cleanupFns.length > 0) {
|
||||
cleanupFns.pop()?.();
|
||||
}
|
||||
restoreProcessEnv();
|
||||
cleanupTempDirs(tempDirs);
|
||||
});
|
||||
|
||||
describe("installTestEnv", () => {
|
||||
it("keeps live tests on a temp HOME while copying config and auth state", () => {
|
||||
const realHome = createTempHome();
|
||||
const priorIsolatedHome = createTempHome();
|
||||
writeFile(path.join(realHome, ".profile"), "export TEST_PROFILE_ONLY=from-profile\n");
|
||||
writeFile(
|
||||
path.join(realHome, "custom-openclaw.json5"),
|
||||
`{
|
||||
// Preserve provider config, strip host-bound paths.
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: "/Users/peter/Projects",
|
||||
agentDir: "/Users/peter/.openclaw/agents/main/agent",
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "dev",
|
||||
workspace: "/Users/peter/dev-workspace",
|
||||
agentDir: "/Users/peter/.openclaw/agents/dev/agent",
|
||||
},
|
||||
],
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
custom: { baseUrl: "https://example.test/v1" },
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
telegram: {
|
||||
streaming: {
|
||||
mode: "block",
|
||||
chunkMode: "newline",
|
||||
block: {
|
||||
enabled: true,
|
||||
},
|
||||
preview: {
|
||||
chunk: {
|
||||
minChars: 120,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}`,
|
||||
);
|
||||
writeFile(path.join(realHome, ".openclaw", "credentials", "token.txt"), "secret\n");
|
||||
writeFile(
|
||||
path.join(realHome, ".openclaw", "external-plugins", "glueclaw", "openclaw.plugin.json"),
|
||||
'{"id":"glueclaw"}\n',
|
||||
);
|
||||
writeFile(
|
||||
path.join(realHome, ".openclaw", "agents", "main", "agent", "auth-profiles.json"),
|
||||
JSON.stringify({ version: 1, profiles: { default: { provider: "openai" } } }, null, 2),
|
||||
);
|
||||
writeFile(path.join(realHome, ".claude", ".credentials.json"), '{"accessToken":"token"}\n');
|
||||
|
||||
process.env.HOME = realHome;
|
||||
process.env.USERPROFILE = realHome;
|
||||
process.env.OPENCLAW_LIVE_TEST = "1";
|
||||
process.env.OPENCLAW_LIVE_TEST_QUIET = "1";
|
||||
process.env.OPENCLAW_CONFIG_PATH = "~/custom-openclaw.json5";
|
||||
process.env.OPENCLAW_TEST_HOME = priorIsolatedHome;
|
||||
process.env.OPENCLAW_STATE_DIR = path.join(priorIsolatedHome, ".openclaw");
|
||||
|
||||
const testEnv = installTestEnv();
|
||||
cleanupFns.push(testEnv.cleanup);
|
||||
|
||||
expect(testEnv.tempHome).not.toBe(realHome);
|
||||
expect(process.env.HOME).toBe(testEnv.tempHome);
|
||||
expect(process.env.OPENCLAW_TEST_HOME).toBe(testEnv.tempHome);
|
||||
expect(process.env.TEST_PROFILE_ONLY).toBe("from-profile");
|
||||
|
||||
const copiedConfigPath = path.join(testEnv.tempHome, ".openclaw", "openclaw.json");
|
||||
const copiedConfig = JSON.parse(fs.readFileSync(copiedConfigPath, "utf8")) as {
|
||||
agents?: {
|
||||
defaults?: Record<string, unknown>;
|
||||
list?: Array<Record<string, unknown>>;
|
||||
};
|
||||
models?: { providers?: Record<string, unknown> };
|
||||
channels?: {
|
||||
telegram?: {
|
||||
streaming?: {
|
||||
mode?: string;
|
||||
chunkMode?: string;
|
||||
block?: { enabled?: boolean };
|
||||
preview?: { chunk?: { minChars?: number } };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
expect(copiedConfig.models?.providers?.custom).toEqual({ baseUrl: "https://example.test/v1" });
|
||||
expect(copiedConfig.agents?.defaults?.workspace).toBeUndefined();
|
||||
expect(copiedConfig.agents?.defaults?.agentDir).toBeUndefined();
|
||||
expect(copiedConfig.agents?.list?.[0]?.workspace).toBeUndefined();
|
||||
expect(copiedConfig.agents?.list?.[0]?.agentDir).toBeUndefined();
|
||||
expect(copiedConfig.channels?.telegram?.streaming).toEqual({
|
||||
mode: "block",
|
||||
chunkMode: "newline",
|
||||
block: { enabled: true },
|
||||
preview: { chunk: { minChars: 120 } },
|
||||
});
|
||||
|
||||
expect(
|
||||
fs.existsSync(path.join(testEnv.tempHome, ".openclaw", "credentials", "token.txt")),
|
||||
).toBe(true);
|
||||
expect(
|
||||
fs.existsSync(
|
||||
path.join(
|
||||
testEnv.tempHome,
|
||||
".openclaw",
|
||||
"external-plugins",
|
||||
"glueclaw",
|
||||
"openclaw.plugin.json",
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
fs.existsSync(
|
||||
path.join(testEnv.tempHome, ".openclaw", "agents", "main", "agent", "auth-profiles.json"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(fs.existsSync(path.join(testEnv.tempHome, ".claude", ".credentials.json"))).toBe(true);
|
||||
});
|
||||
|
||||
it("allows explicit live runs against the real HOME", () => {
|
||||
const realHome = createTempHome();
|
||||
writeFile(path.join(realHome, ".profile"), "export TEST_PROFILE_ONLY=from-profile\n");
|
||||
|
||||
process.env.HOME = realHome;
|
||||
process.env.USERPROFILE = realHome;
|
||||
process.env.OPENCLAW_LIVE_TEST = "1";
|
||||
process.env.OPENCLAW_LIVE_USE_REAL_HOME = "1";
|
||||
process.env.OPENCLAW_LIVE_TEST_QUIET = "1";
|
||||
|
||||
const testEnv = installTestEnv();
|
||||
|
||||
expect(testEnv.tempHome).toBe(realHome);
|
||||
expect(process.env.HOME).toBe(realHome);
|
||||
expect(process.env.TEST_PROFILE_ONLY).toBe("from-profile");
|
||||
});
|
||||
|
||||
it("does not load ~/.profile for normal isolated test runs", () => {
|
||||
const realHome = createTempHome();
|
||||
writeFile(path.join(realHome, ".profile"), "export TEST_PROFILE_ONLY=from-profile\n");
|
||||
|
||||
process.env.HOME = realHome;
|
||||
process.env.USERPROFILE = realHome;
|
||||
delete process.env.LIVE;
|
||||
delete process.env.OPENCLAW_LIVE_TEST;
|
||||
delete process.env.OPENCLAW_LIVE_GATEWAY;
|
||||
delete process.env.OPENCLAW_LIVE_USE_REAL_HOME;
|
||||
delete process.env.OPENCLAW_LIVE_TEST_QUIET;
|
||||
|
||||
const testEnv = installTestEnv();
|
||||
cleanupFns.push(testEnv.cleanup);
|
||||
|
||||
expect(testEnv.tempHome).not.toBe(realHome);
|
||||
expect(process.env.TEST_PROFILE_ONLY).toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls back to parsing ~/.profile when bash is unavailable", async () => {
|
||||
const realHome = createTempHome();
|
||||
writeFile(path.join(realHome, ".profile"), "export TEST_PROFILE_ONLY=from-profile\n");
|
||||
|
||||
process.env.HOME = realHome;
|
||||
process.env.USERPROFILE = realHome;
|
||||
process.env.OPENCLAW_LIVE_TEST = "1";
|
||||
process.env.OPENCLAW_LIVE_USE_REAL_HOME = "1";
|
||||
process.env.OPENCLAW_LIVE_TEST_QUIET = "1";
|
||||
|
||||
vi.doMock("node:child_process", () => ({
|
||||
execFileSync: () => {
|
||||
throw Object.assign(new Error("bash missing"), { code: "ENOENT" });
|
||||
},
|
||||
}));
|
||||
|
||||
const { installTestEnv: installFreshTestEnv } = await importFreshModule<
|
||||
typeof import("./test-env.js")
|
||||
>(import.meta.url, "./test-env.js?scope=profile-fallback");
|
||||
|
||||
const testEnv = installFreshTestEnv();
|
||||
|
||||
expect(testEnv.tempHome).toBe(realHome);
|
||||
expect(process.env.TEST_PROFILE_ONLY).toBe("from-profile");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue