mirror of https://github.com/openclaw/openclaw.git
test: share gateway reload helpers
This commit is contained in:
parent
320de5ecdd
commit
b72ac7936a
|
|
@ -218,80 +218,52 @@ describe("gateway hot reload", () => {
|
|||
});
|
||||
|
||||
async function writeEnvRefConfig() {
|
||||
await writeConfigFile({
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function writeConfigFile(config: unknown) {
|
||||
const configPath = process.env.OPENCLAW_CONFIG_PATH;
|
||||
if (!configPath) {
|
||||
throw new Error("OPENCLAW_CONFIG_PATH is not set");
|
||||
}
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
await fs.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
async function writeTalkApiKeyEnvRefConfig(refId = "TALK_API_KEY_REF") {
|
||||
const configPath = process.env.OPENCLAW_CONFIG_PATH;
|
||||
if (!configPath) {
|
||||
throw new Error("OPENCLAW_CONFIG_PATH is not set");
|
||||
}
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: refId },
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
await writeConfigFile({
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: refId },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function writeGatewayTraversalExecRefConfig() {
|
||||
const configPath = process.env.OPENCLAW_CONFIG_PATH;
|
||||
if (!configPath) {
|
||||
throw new Error("OPENCLAW_CONFIG_PATH is not set");
|
||||
}
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: { source: "exec", provider: "vault", id: "a/../b" },
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
providers: {
|
||||
vault: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
},
|
||||
},
|
||||
await writeConfigFile({
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: { source: "exec", provider: "vault", id: "a/../b" },
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
providers: {
|
||||
vault: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function writeGatewayTokenExecRefConfig(params: {
|
||||
|
|
@ -299,36 +271,24 @@ describe("gateway hot reload", () => {
|
|||
modePath: string;
|
||||
tokenValue: string;
|
||||
}) {
|
||||
const configPath = process.env.OPENCLAW_CONFIG_PATH;
|
||||
if (!configPath) {
|
||||
throw new Error("OPENCLAW_CONFIG_PATH is not set");
|
||||
}
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: { source: "exec", provider: "vault", id: "gateway/token" },
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
providers: {
|
||||
vault: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
allowSymlinkCommand: true,
|
||||
args: [params.resolverScriptPath, params.modePath, params.tokenValue],
|
||||
},
|
||||
},
|
||||
await writeConfigFile({
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: { source: "exec", provider: "vault", id: "gateway/token" },
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
providers: {
|
||||
vault: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
allowSymlinkCommand: true,
|
||||
args: [params.resolverScriptPath, params.modePath, params.tokenValue],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function writeDisabledSurfaceRefConfig() {
|
||||
|
|
@ -461,6 +421,32 @@ describe("gateway hot reload", () => {
|
|||
await fs.rm(authStorePath, { force: true });
|
||||
}
|
||||
|
||||
async function expectOneShotSecretReloadEvents(params: {
|
||||
applyReload: () => Promise<unknown> | undefined;
|
||||
sessionKey: string;
|
||||
expectedError: RegExp | string;
|
||||
}) {
|
||||
await expect(params.applyReload()).rejects.toThrow(params.expectedError);
|
||||
const degradedEvents = drainSystemEvents(params.sessionKey);
|
||||
expect(degradedEvents.some((event) => event.includes("[SECRETS_RELOADER_DEGRADED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await expect(params.applyReload()).rejects.toThrow(params.expectedError);
|
||||
expect(drainSystemEvents(params.sessionKey)).toEqual([]);
|
||||
}
|
||||
|
||||
async function expectSecretReloadRecovered(params: {
|
||||
applyReload: () => Promise<unknown> | undefined;
|
||||
sessionKey: string;
|
||||
}) {
|
||||
await expect(params.applyReload()).resolves.toBeUndefined();
|
||||
const recoveredEvents = drainSystemEvents(params.sessionKey);
|
||||
expect(recoveredEvents.some((event) => event.includes("[SECRETS_RELOADER_RECOVERED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
it("applies hot reload actions and emits restart signal", async () => {
|
||||
await withGatewayServer(async () => {
|
||||
const onHotReload = hoisted.getOnHotReload();
|
||||
|
|
@ -512,14 +498,16 @@ describe("gateway hot reload", () => {
|
|||
);
|
||||
|
||||
expect(hoisted.stopGmailWatcher).toHaveBeenCalled();
|
||||
expect(hoisted.startGmailWatcher).toHaveBeenCalledWith(nextConfig);
|
||||
expect(hoisted.startGmailWatcher).toHaveBeenCalledWith(expect.objectContaining(nextConfig));
|
||||
|
||||
expect(hoisted.browserStop).toHaveBeenCalledTimes(1);
|
||||
expect(hoisted.startBrowserControlServerIfEnabled).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(hoisted.startHeartbeatRunner).toHaveBeenCalledTimes(1);
|
||||
expect(hoisted.heartbeatUpdateConfig).toHaveBeenCalledTimes(1);
|
||||
expect(hoisted.heartbeatUpdateConfig).toHaveBeenCalledWith(nextConfig);
|
||||
expect(hoisted.heartbeatUpdateConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining(nextConfig),
|
||||
);
|
||||
|
||||
expect(hoisted.cronInstances.length).toBe(2);
|
||||
expect(hoisted.cronInstances[0].stop).toHaveBeenCalledTimes(1);
|
||||
|
|
@ -649,25 +637,17 @@ describe("gateway hot reload", () => {
|
|||
};
|
||||
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
await expect(onHotReload?.(plan, nextConfig)).rejects.toThrow(
|
||||
'Environment variable "OPENAI_API_KEY" is missing or empty.',
|
||||
);
|
||||
const degradedEvents = drainSystemEvents(sessionKey);
|
||||
expect(degradedEvents.some((event) => event.includes("[SECRETS_RELOADER_DEGRADED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await expect(onHotReload?.(plan, nextConfig)).rejects.toThrow(
|
||||
'Environment variable "OPENAI_API_KEY" is missing or empty.',
|
||||
);
|
||||
expect(drainSystemEvents(sessionKey)).toEqual([]);
|
||||
await expectOneShotSecretReloadEvents({
|
||||
applyReload: () => onHotReload?.(plan, nextConfig),
|
||||
sessionKey,
|
||||
expectedError: 'Environment variable "OPENAI_API_KEY" is missing or empty.',
|
||||
});
|
||||
|
||||
process.env.OPENAI_API_KEY = "sk-recovered"; // pragma: allowlist secret
|
||||
await expect(onHotReload?.(plan, nextConfig)).resolves.toBeUndefined();
|
||||
const recoveredEvents = drainSystemEvents(sessionKey);
|
||||
expect(recoveredEvents.some((event) => event.includes("[SECRETS_RELOADER_RECOVERED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
await expectSecretReloadRecovered({
|
||||
applyReload: () => onHotReload?.(plan, nextConfig),
|
||||
sessionKey,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -707,25 +687,17 @@ describe("gateway hot reload", () => {
|
|||
};
|
||||
|
||||
delete process.env.GEMINI_API_KEY;
|
||||
await expect(onHotReload?.(plan, nextConfig)).rejects.toThrow(
|
||||
"[WEB_SEARCH_KEY_UNRESOLVED_NO_FALLBACK]",
|
||||
);
|
||||
const degradedEvents = drainSystemEvents(sessionKey);
|
||||
expect(degradedEvents.some((event) => event.includes("[SECRETS_RELOADER_DEGRADED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await expect(onHotReload?.(plan, nextConfig)).rejects.toThrow(
|
||||
"[WEB_SEARCH_KEY_UNRESOLVED_NO_FALLBACK]",
|
||||
);
|
||||
expect(drainSystemEvents(sessionKey)).toEqual([]);
|
||||
await expectOneShotSecretReloadEvents({
|
||||
applyReload: () => onHotReload?.(plan, nextConfig),
|
||||
sessionKey,
|
||||
expectedError: "[WEB_SEARCH_KEY_UNRESOLVED_NO_FALLBACK]",
|
||||
});
|
||||
|
||||
process.env.GEMINI_API_KEY = "gemini-recovered-key"; // pragma: allowlist secret
|
||||
await expect(onHotReload?.(plan, nextConfig)).resolves.toBeUndefined();
|
||||
const recoveredEvents = drainSystemEvents(sessionKey);
|
||||
expect(recoveredEvents.some((event) => event.includes("[SECRETS_RELOADER_RECOVERED]"))).toBe(
|
||||
true,
|
||||
);
|
||||
await expectSecretReloadRecovered({
|
||||
applyReload: () => onHotReload?.(plan, nextConfig),
|
||||
sessionKey,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue