Spaces:
Running
Running
File size: 5,634 Bytes
9f069df | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | import { execFileSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
type RestoreEntry = { key: string; value: string | undefined };
function restoreEnv(entries: RestoreEntry[]): void {
for (const { key, value } of entries) {
if (value === undefined) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
}
function loadProfileEnv(): void {
const profilePath = path.join(os.homedir(), ".profile");
if (!fs.existsSync(profilePath)) {
return;
}
try {
const output = execFileSync(
"/bin/bash",
["-lc", `set -a; source "${profilePath}" >/dev/null 2>&1; env -0`],
{ encoding: "utf8" },
);
const entries = output.split("\0");
let applied = 0;
for (const entry of entries) {
if (!entry) {
continue;
}
const idx = entry.indexOf("=");
if (idx <= 0) {
continue;
}
const key = entry.slice(0, idx);
if (!key || (process.env[key] ?? "") !== "") {
continue;
}
process.env[key] = entry.slice(idx + 1);
applied += 1;
}
if (applied > 0) {
console.log(`[live] loaded ${applied} env vars from ~/.profile`);
}
} catch {
// ignore profile load failures
}
}
export function installTestEnv(): { cleanup: () => void; tempHome: string } {
const live =
process.env.LIVE === "1" ||
process.env.OPENCLAW_LIVE_TEST === "1" ||
process.env.OPENCLAW_LIVE_GATEWAY === "1";
// Live tests must use the real user environment (keys, profiles, config).
// The default test env isolates HOME to avoid touching real state.
if (live) {
loadProfileEnv();
return { cleanup: () => {}, tempHome: process.env.HOME ?? "" };
}
const restore: RestoreEntry[] = [
{ key: "OPENCLAW_TEST_FAST", value: process.env.OPENCLAW_TEST_FAST },
{ key: "HOME", value: process.env.HOME },
{ key: "USERPROFILE", value: process.env.USERPROFILE },
{ key: "XDG_CONFIG_HOME", value: process.env.XDG_CONFIG_HOME },
{ key: "XDG_DATA_HOME", value: process.env.XDG_DATA_HOME },
{ key: "XDG_STATE_HOME", value: process.env.XDG_STATE_HOME },
{ key: "XDG_CACHE_HOME", value: process.env.XDG_CACHE_HOME },
{ key: "OPENCLAW_STATE_DIR", value: process.env.OPENCLAW_STATE_DIR },
{ key: "OPENCLAW_CONFIG_PATH", value: process.env.OPENCLAW_CONFIG_PATH },
{ key: "OPENCLAW_GATEWAY_PORT", value: process.env.OPENCLAW_GATEWAY_PORT },
{ key: "OPENCLAW_BRIDGE_ENABLED", value: process.env.OPENCLAW_BRIDGE_ENABLED },
{ key: "OPENCLAW_BRIDGE_HOST", value: process.env.OPENCLAW_BRIDGE_HOST },
{ key: "OPENCLAW_BRIDGE_PORT", value: process.env.OPENCLAW_BRIDGE_PORT },
{ key: "OPENCLAW_CANVAS_HOST_PORT", value: process.env.OPENCLAW_CANVAS_HOST_PORT },
{ key: "OPENCLAW_TEST_HOME", value: process.env.OPENCLAW_TEST_HOME },
{ key: "TELEGRAM_BOT_TOKEN", value: process.env.TELEGRAM_BOT_TOKEN },
{ key: "DISCORD_BOT_TOKEN", value: process.env.DISCORD_BOT_TOKEN },
{ key: "SLACK_BOT_TOKEN", value: process.env.SLACK_BOT_TOKEN },
{ key: "SLACK_APP_TOKEN", value: process.env.SLACK_APP_TOKEN },
{ key: "SLACK_USER_TOKEN", value: process.env.SLACK_USER_TOKEN },
{ key: "COPILOT_GITHUB_TOKEN", value: process.env.COPILOT_GITHUB_TOKEN },
{ key: "GH_TOKEN", value: process.env.GH_TOKEN },
{ key: "GITHUB_TOKEN", value: process.env.GITHUB_TOKEN },
{ key: "NODE_OPTIONS", value: process.env.NODE_OPTIONS },
];
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-home-"));
process.env.HOME = tempHome;
process.env.USERPROFILE = tempHome;
process.env.OPENCLAW_TEST_HOME = tempHome;
process.env.OPENCLAW_TEST_FAST = "1";
// Ensure test runs never touch the developer's real config/state, even if they have overrides set.
delete process.env.OPENCLAW_CONFIG_PATH;
// Prefer deriving state dir from HOME so nested tests that change HOME also isolate correctly.
delete process.env.OPENCLAW_STATE_DIR;
// Prefer test-controlled ports over developer overrides (avoid port collisions across tests/workers).
delete process.env.OPENCLAW_GATEWAY_PORT;
delete process.env.OPENCLAW_BRIDGE_ENABLED;
delete process.env.OPENCLAW_BRIDGE_HOST;
delete process.env.OPENCLAW_BRIDGE_PORT;
delete process.env.OPENCLAW_CANVAS_HOST_PORT;
// Avoid leaking real GitHub/Copilot tokens into non-live test runs.
delete process.env.TELEGRAM_BOT_TOKEN;
delete process.env.DISCORD_BOT_TOKEN;
delete process.env.SLACK_BOT_TOKEN;
delete process.env.SLACK_APP_TOKEN;
delete process.env.SLACK_USER_TOKEN;
delete process.env.COPILOT_GITHUB_TOKEN;
delete process.env.GH_TOKEN;
delete process.env.GITHUB_TOKEN;
// Avoid leaking local dev tooling flags into tests (e.g. --inspect).
delete process.env.NODE_OPTIONS;
// Windows: prefer the default state dir so auth/profile tests match real paths.
if (process.platform === "win32") {
process.env.OPENCLAW_STATE_DIR = path.join(tempHome, ".openclaw");
}
process.env.XDG_CONFIG_HOME = path.join(tempHome, ".config");
process.env.XDG_DATA_HOME = path.join(tempHome, ".local", "share");
process.env.XDG_STATE_HOME = path.join(tempHome, ".local", "state");
process.env.XDG_CACHE_HOME = path.join(tempHome, ".cache");
const cleanup = () => {
restoreEnv(restore);
try {
fs.rmSync(tempHome, { recursive: true, force: true });
} catch {
// ignore cleanup errors
}
};
return { cleanup, tempHome };
}
export function withIsolatedTestHome(): { cleanup: () => void; tempHome: string } {
return installTestEnv();
}
|