mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 08:47:40 +00:00
test(auth): cover key normalization
This commit is contained in:
39
src/agents/minimax-vlm.normalizes-api-key.test.ts
Normal file
39
src/agents/minimax-vlm.normalizes-api-key.test.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("minimaxUnderstandImage apiKey normalization", () => {
|
||||||
|
const priorFetch = global.fetch;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// @ts-expect-error restore
|
||||||
|
global.fetch = priorFetch;
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("strips embedded CR/LF before sending Authorization header", async () => {
|
||||||
|
const fetchSpy = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
|
const auth = (init?.headers as Record<string, string> | undefined)?.Authorization;
|
||||||
|
expect(auth).toBe("Bearer minimax-test-key");
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
base_resp: { status_code: 0, status_msg: "ok" },
|
||||||
|
content: "ok",
|
||||||
|
}),
|
||||||
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// @ts-expect-error mock fetch
|
||||||
|
global.fetch = fetchSpy;
|
||||||
|
|
||||||
|
const { minimaxUnderstandImage } = await import("./minimax-vlm.js");
|
||||||
|
const text = await minimaxUnderstandImage({
|
||||||
|
apiKey: "minimax-test-\r\nkey",
|
||||||
|
prompt: "hi",
|
||||||
|
imageDataUrl: "data:image/png;base64,AAAA",
|
||||||
|
apiHost: "https://api.minimax.io",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(text).toBe("ok");
|
||||||
|
expect(fetchSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
vi.mock("../../infra/net/fetch-guard.js", () => {
|
||||||
|
return {
|
||||||
|
fetchWithSsrFGuard: vi.fn(async () => {
|
||||||
|
throw new Error("network down");
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("web_fetch firecrawl apiKey normalization", () => {
|
||||||
|
const priorFetch = global.fetch;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// @ts-expect-error restore
|
||||||
|
global.fetch = priorFetch;
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("strips embedded CR/LF before sending Authorization header", async () => {
|
||||||
|
const fetchSpy = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : "";
|
||||||
|
expect(url).toContain("/v2/scrape");
|
||||||
|
|
||||||
|
const auth = (init?.headers as Record<string, string> | undefined)?.Authorization;
|
||||||
|
expect(auth).toBe("Bearer firecrawl-test-key");
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
data: { markdown: "ok", metadata: { title: "t" } },
|
||||||
|
}),
|
||||||
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error mock fetch
|
||||||
|
global.fetch = fetchSpy;
|
||||||
|
|
||||||
|
const { createWebFetchTool } = await import("./web-tools.js");
|
||||||
|
const tool = createWebFetchTool({
|
||||||
|
config: {
|
||||||
|
tools: {
|
||||||
|
web: {
|
||||||
|
fetch: {
|
||||||
|
cacheTtlMinutes: 0,
|
||||||
|
firecrawl: { apiKey: "firecrawl-test-\r\nkey" },
|
||||||
|
readability: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await tool?.execute?.("call", {
|
||||||
|
url: "https://example.com",
|
||||||
|
extractMode: "text",
|
||||||
|
});
|
||||||
|
expect(result?.details).toMatchObject({ extractor: "firecrawl" });
|
||||||
|
expect(fetchSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
let writtenConfig: unknown = null;
|
||||||
|
|
||||||
|
vi.mock("../../config/config.js", () => {
|
||||||
|
return {
|
||||||
|
loadConfig: () => ({
|
||||||
|
skills: {
|
||||||
|
entries: {},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
writeConfigFile: async (cfg: unknown) => {
|
||||||
|
writtenConfig = cfg;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("skills.update", () => {
|
||||||
|
it("strips embedded CR/LF from apiKey", async () => {
|
||||||
|
writtenConfig = null;
|
||||||
|
const { skillsHandlers } = await import("./skills.js");
|
||||||
|
|
||||||
|
let ok: boolean | null = null;
|
||||||
|
let error: unknown = null;
|
||||||
|
await skillsHandlers["skills.update"]({
|
||||||
|
params: {
|
||||||
|
skillKey: "brave-search",
|
||||||
|
apiKey: "abc\r\ndef",
|
||||||
|
},
|
||||||
|
respond: (success, _result, err) => {
|
||||||
|
ok = success;
|
||||||
|
error = err;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ok).toBe(true);
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
expect(writtenConfig).toMatchObject({
|
||||||
|
skills: {
|
||||||
|
entries: {
|
||||||
|
"brave-search": {
|
||||||
|
apiKey: "abcdef",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
73
src/infra/provider-usage.auth.normalizes-keys.test.ts
Normal file
73
src/infra/provider-usage.auth.normalizes-keys.test.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import fs from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { withTempHome } from "../../test/helpers/temp-home.js";
|
||||||
|
|
||||||
|
describe("resolveProviderAuths key normalization", () => {
|
||||||
|
it("strips embedded CR/LF from env keys", async () => {
|
||||||
|
await withTempHome(
|
||||||
|
async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
const { resolveProviderAuths } = await import("./provider-usage.auth.js");
|
||||||
|
|
||||||
|
const auths = await resolveProviderAuths({
|
||||||
|
providers: ["zai", "minimax", "xiaomi"],
|
||||||
|
});
|
||||||
|
expect(auths).toEqual([
|
||||||
|
{ provider: "zai", token: "zai-key" },
|
||||||
|
{ provider: "minimax", token: "minimax-key" },
|
||||||
|
{ provider: "xiaomi", token: "xiaomi-key" },
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
ZAI_API_KEY: "zai-\r\nkey",
|
||||||
|
MINIMAX_API_KEY: "minimax-\r\nkey",
|
||||||
|
XIAOMI_API_KEY: "xiaomi-\r\nkey",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("strips embedded CR/LF from stored auth profiles (token + api_key)", async () => {
|
||||||
|
await withTempHome(
|
||||||
|
async (home) => {
|
||||||
|
const agentDir = path.join(home, ".openclaw", "agents", "main", "agent");
|
||||||
|
await fs.mkdir(agentDir, { recursive: true });
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(agentDir, "auth-profiles.json"),
|
||||||
|
`${JSON.stringify(
|
||||||
|
{
|
||||||
|
version: 1,
|
||||||
|
profiles: {
|
||||||
|
"minimax:default": { type: "token", provider: "minimax", token: "mini-\r\nmax" },
|
||||||
|
"xiaomi:default": { type: "api_key", provider: "xiaomi", key: "xiao-\r\nmi" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)}\n`,
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
vi.resetModules();
|
||||||
|
const { resolveProviderAuths } = await import("./provider-usage.auth.js");
|
||||||
|
|
||||||
|
const auths = await resolveProviderAuths({
|
||||||
|
providers: ["minimax", "xiaomi"],
|
||||||
|
});
|
||||||
|
expect(auths).toEqual([
|
||||||
|
{ provider: "minimax", token: "mini-max" },
|
||||||
|
{ provider: "xiaomi", token: "xiao-mi" },
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
MINIMAX_API_KEY: undefined,
|
||||||
|
MINIMAX_CODE_PLAN_KEY: undefined,
|
||||||
|
XIAOMI_API_KEY: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user