mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-06 06:41:08 +00:00
233 lines
7.3 KiB
TypeScript
233 lines
7.3 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
|
|
import { loadConfig, writeConfigFile } from "../config/config.js";
|
|
import { withTempHome } from "../config/home-env.test-harness.js";
|
|
import { withEnvAsync } from "../test-utils/env.js";
|
|
import {
|
|
asConfig,
|
|
beginSecretsRuntimeIsolationForTest,
|
|
createOpenAIFileRuntimeConfig,
|
|
createOpenAIFileRuntimeFixture,
|
|
EMPTY_LOADABLE_PLUGIN_ORIGINS,
|
|
endSecretsRuntimeIsolationForTest,
|
|
expectResolvedOpenAIRuntime,
|
|
loadAuthStoreWithProfiles,
|
|
OPENAI_ENV_KEY_REF,
|
|
OPENAI_FILE_KEY_REF,
|
|
type SecretsRuntimeEnvSnapshot,
|
|
} from "./runtime.integration.test-helpers.js";
|
|
import {
|
|
activateSecretsRuntimeSnapshot,
|
|
getActiveSecretsRuntimeSnapshot,
|
|
prepareSecretsRuntimeSnapshot,
|
|
} from "./runtime.js";
|
|
|
|
vi.unmock("../version.js");
|
|
|
|
describe("secrets runtime snapshot auth integration", () => {
|
|
let envSnapshot: SecretsRuntimeEnvSnapshot;
|
|
|
|
beforeEach(() => {
|
|
envSnapshot = beginSecretsRuntimeIsolationForTest();
|
|
});
|
|
|
|
afterEach(() => {
|
|
endSecretsRuntimeIsolationForTest(envSnapshot);
|
|
});
|
|
|
|
it("activates runtime snapshots for loadConfig and ensureAuthProfileStore", async () => {
|
|
await withEnvAsync(
|
|
{
|
|
OPENCLAW_BUNDLED_PLUGINS_DIR: undefined,
|
|
OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE: "1",
|
|
OPENCLAW_VERSION: undefined,
|
|
},
|
|
async () => {
|
|
const prepared = await prepareSecretsRuntimeSnapshot({
|
|
config: asConfig({
|
|
models: {
|
|
providers: {
|
|
openai: {
|
|
baseUrl: "https://api.openai.com/v1",
|
|
apiKey: OPENAI_ENV_KEY_REF,
|
|
models: [],
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
env: { OPENAI_API_KEY: "sk-runtime" },
|
|
agentDirs: ["/tmp/openclaw-agent-main"],
|
|
loadablePluginOrigins: EMPTY_LOADABLE_PLUGIN_ORIGINS,
|
|
loadAuthStore: () =>
|
|
loadAuthStoreWithProfiles({
|
|
"openai:default": {
|
|
type: "api_key",
|
|
provider: "openai",
|
|
keyRef: OPENAI_ENV_KEY_REF,
|
|
},
|
|
}),
|
|
});
|
|
|
|
activateSecretsRuntimeSnapshot(prepared);
|
|
|
|
expect(loadConfig().models?.providers?.openai?.apiKey).toBe("sk-runtime");
|
|
expect(
|
|
ensureAuthProfileStore("/tmp/openclaw-agent-main").profiles["openai:default"],
|
|
).toMatchObject({
|
|
type: "api_key",
|
|
key: "sk-runtime",
|
|
});
|
|
},
|
|
);
|
|
});
|
|
|
|
it("keeps active secrets runtime snapshots resolved after config writes", async () => {
|
|
if (os.platform() === "win32") {
|
|
return;
|
|
}
|
|
await withTempHome("openclaw-secrets-runtime-write-", async (home) => {
|
|
const { secretFile, agentDir } = await createOpenAIFileRuntimeFixture(home);
|
|
|
|
const prepared = await prepareSecretsRuntimeSnapshot({
|
|
config: createOpenAIFileRuntimeConfig(secretFile),
|
|
agentDirs: [agentDir],
|
|
loadablePluginOrigins: EMPTY_LOADABLE_PLUGIN_ORIGINS,
|
|
});
|
|
|
|
activateSecretsRuntimeSnapshot(prepared);
|
|
|
|
expectResolvedOpenAIRuntime(agentDir);
|
|
|
|
await writeConfigFile({
|
|
...loadConfig(),
|
|
gateway: { auth: { mode: "token" } },
|
|
});
|
|
|
|
expect(loadConfig().gateway?.auth).toEqual({ mode: "token" });
|
|
expectResolvedOpenAIRuntime(agentDir);
|
|
});
|
|
});
|
|
|
|
it("keeps last-known-good runtime snapshot active when refresh fails after a write", async () => {
|
|
if (os.platform() === "win32") {
|
|
return;
|
|
}
|
|
await withTempHome("openclaw-secrets-runtime-refresh-fail-", async (home) => {
|
|
const { secretFile, agentDir } = await createOpenAIFileRuntimeFixture(home);
|
|
|
|
let loadAuthStoreCalls = 0;
|
|
const loadAuthStore = () => {
|
|
loadAuthStoreCalls += 1;
|
|
if (loadAuthStoreCalls > 1) {
|
|
throw new Error("simulated secrets runtime refresh failure");
|
|
}
|
|
return loadAuthStoreWithProfiles({
|
|
"openai:default": {
|
|
type: "api_key",
|
|
provider: "openai",
|
|
keyRef: OPENAI_FILE_KEY_REF,
|
|
},
|
|
});
|
|
};
|
|
|
|
const prepared = await prepareSecretsRuntimeSnapshot({
|
|
config: createOpenAIFileRuntimeConfig(secretFile),
|
|
agentDirs: [agentDir],
|
|
loadablePluginOrigins: EMPTY_LOADABLE_PLUGIN_ORIGINS,
|
|
loadAuthStore,
|
|
});
|
|
|
|
activateSecretsRuntimeSnapshot(prepared);
|
|
|
|
await expect(
|
|
writeConfigFile({
|
|
...loadConfig(),
|
|
gateway: { auth: { mode: "token" } },
|
|
}),
|
|
).rejects.toThrow(
|
|
/runtime snapshot refresh failed: simulated secrets runtime refresh failure/i,
|
|
);
|
|
|
|
const activeAfterFailure = getActiveSecretsRuntimeSnapshot();
|
|
expect(activeAfterFailure).not.toBeNull();
|
|
expect(loadConfig().gateway?.auth).toBeUndefined();
|
|
expectResolvedOpenAIRuntime(agentDir);
|
|
expect(activeAfterFailure?.sourceConfig.models?.providers?.openai?.apiKey).toEqual(
|
|
OPENAI_FILE_KEY_REF,
|
|
);
|
|
});
|
|
});
|
|
|
|
it("recomputes config-derived agent dirs when refreshing active secrets runtime snapshots", async () => {
|
|
await withTempHome("openclaw-secrets-runtime-agent-dirs-", async (home) => {
|
|
const mainAgentDir = path.join(home, ".openclaw", "agents", "main", "agent");
|
|
const opsAgentDir = path.join(home, ".openclaw", "agents", "ops", "agent");
|
|
await fs.mkdir(mainAgentDir, { recursive: true });
|
|
await fs.mkdir(opsAgentDir, { recursive: true });
|
|
await fs.writeFile(
|
|
path.join(mainAgentDir, "auth-profiles.json"),
|
|
`${JSON.stringify(
|
|
{
|
|
version: 1,
|
|
profiles: {
|
|
"openai:default": {
|
|
type: "api_key",
|
|
provider: "openai",
|
|
keyRef: OPENAI_ENV_KEY_REF,
|
|
},
|
|
},
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
{ encoding: "utf8", mode: 0o600 },
|
|
);
|
|
await fs.writeFile(
|
|
path.join(opsAgentDir, "auth-profiles.json"),
|
|
`${JSON.stringify(
|
|
{
|
|
version: 1,
|
|
profiles: {
|
|
"anthropic:ops": {
|
|
type: "api_key",
|
|
provider: "anthropic",
|
|
keyRef: { source: "env", provider: "default", id: "ANTHROPIC_API_KEY" },
|
|
},
|
|
},
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
{ encoding: "utf8", mode: 0o600 },
|
|
);
|
|
|
|
const prepared = await prepareSecretsRuntimeSnapshot({
|
|
config: asConfig({}),
|
|
env: {
|
|
OPENAI_API_KEY: "sk-main-runtime",
|
|
ANTHROPIC_API_KEY: "sk-ops-runtime",
|
|
},
|
|
loadablePluginOrigins: EMPTY_LOADABLE_PLUGIN_ORIGINS,
|
|
});
|
|
|
|
activateSecretsRuntimeSnapshot(prepared);
|
|
expect(ensureAuthProfileStore(opsAgentDir).profiles["anthropic:ops"]).toBeUndefined();
|
|
|
|
await writeConfigFile({
|
|
agents: {
|
|
list: [{ id: "ops", agentDir: opsAgentDir }],
|
|
},
|
|
});
|
|
|
|
expect(ensureAuthProfileStore(opsAgentDir).profiles["anthropic:ops"]).toMatchObject({
|
|
type: "api_key",
|
|
key: "sk-ops-runtime",
|
|
keyRef: { source: "env", provider: "default", id: "ANTHROPIC_API_KEY" },
|
|
});
|
|
});
|
|
});
|
|
});
|