mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 01:31:23 +00:00
test: dedupe auth fallback tests and add auth util unit coverage
This commit is contained in:
@@ -67,6 +67,25 @@ async function resolveBedrockProvider() {
|
||||
});
|
||||
}
|
||||
|
||||
async function withEnvUpdates<T>(
|
||||
updates: Record<string, string | undefined>,
|
||||
run: () => Promise<T>,
|
||||
): Promise<T> {
|
||||
const snapshot = captureEnv(Object.keys(updates));
|
||||
try {
|
||||
for (const [key, value] of Object.entries(updates)) {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
return await run();
|
||||
} finally {
|
||||
snapshot.restore();
|
||||
}
|
||||
}
|
||||
|
||||
describe("getApiKeyForModel", () => {
|
||||
it("migrates legacy oauth.json into auth-profiles.json", async () => {
|
||||
const envSnapshot = captureEnv([
|
||||
@@ -187,127 +206,75 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("throws when ZAI API key is missing", async () => {
|
||||
const previousZai = process.env.ZAI_API_KEY;
|
||||
const previousLegacy = process.env.Z_AI_API_KEY;
|
||||
await withEnvUpdates(
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: undefined,
|
||||
},
|
||||
async () => {
|
||||
let error: unknown = null;
|
||||
try {
|
||||
await resolveApiKeyForProvider({
|
||||
provider: "zai",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
try {
|
||||
delete process.env.ZAI_API_KEY;
|
||||
delete process.env.Z_AI_API_KEY;
|
||||
|
||||
let error: unknown = null;
|
||||
try {
|
||||
await resolveApiKeyForProvider({
|
||||
provider: "zai",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
expect(String(error)).toContain('No API key found for provider "zai".');
|
||||
} finally {
|
||||
if (previousZai === undefined) {
|
||||
delete process.env.ZAI_API_KEY;
|
||||
} else {
|
||||
process.env.ZAI_API_KEY = previousZai;
|
||||
}
|
||||
if (previousLegacy === undefined) {
|
||||
delete process.env.Z_AI_API_KEY;
|
||||
} else {
|
||||
process.env.Z_AI_API_KEY = previousLegacy;
|
||||
}
|
||||
}
|
||||
expect(String(error)).toContain('No API key found for provider "zai".');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts legacy Z_AI_API_KEY for zai", async () => {
|
||||
const previousZai = process.env.ZAI_API_KEY;
|
||||
const previousLegacy = process.env.Z_AI_API_KEY;
|
||||
|
||||
try {
|
||||
delete process.env.ZAI_API_KEY;
|
||||
process.env.Z_AI_API_KEY = "zai-test-key";
|
||||
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "zai",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("zai-test-key");
|
||||
expect(resolved.source).toContain("Z_AI_API_KEY");
|
||||
} finally {
|
||||
if (previousZai === undefined) {
|
||||
delete process.env.ZAI_API_KEY;
|
||||
} else {
|
||||
process.env.ZAI_API_KEY = previousZai;
|
||||
}
|
||||
if (previousLegacy === undefined) {
|
||||
delete process.env.Z_AI_API_KEY;
|
||||
} else {
|
||||
process.env.Z_AI_API_KEY = previousLegacy;
|
||||
}
|
||||
}
|
||||
await withEnvUpdates(
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: "zai-test-key",
|
||||
},
|
||||
async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "zai",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("zai-test-key");
|
||||
expect(resolved.source).toContain("Z_AI_API_KEY");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves Synthetic API key from env", async () => {
|
||||
const previousSynthetic = process.env.SYNTHETIC_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.SYNTHETIC_API_KEY = "synthetic-test-key";
|
||||
|
||||
await withEnvUpdates({ SYNTHETIC_API_KEY: "synthetic-test-key" }, async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "synthetic",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("synthetic-test-key");
|
||||
expect(resolved.source).toContain("SYNTHETIC_API_KEY");
|
||||
} finally {
|
||||
if (previousSynthetic === undefined) {
|
||||
delete process.env.SYNTHETIC_API_KEY;
|
||||
} else {
|
||||
process.env.SYNTHETIC_API_KEY = previousSynthetic;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves Qianfan API key from env", async () => {
|
||||
const previous = process.env.QIANFAN_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.QIANFAN_API_KEY = "qianfan-test-key";
|
||||
|
||||
await withEnvUpdates({ QIANFAN_API_KEY: "qianfan-test-key" }, async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "qianfan",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("qianfan-test-key");
|
||||
expect(resolved.source).toContain("QIANFAN_API_KEY");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.QIANFAN_API_KEY;
|
||||
} else {
|
||||
process.env.QIANFAN_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves Vercel AI Gateway API key from env", async () => {
|
||||
const previousGatewayKey = process.env.AI_GATEWAY_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.AI_GATEWAY_API_KEY = "gateway-test-key";
|
||||
|
||||
await withEnvUpdates({ AI_GATEWAY_API_KEY: "gateway-test-key" }, async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "vercel-ai-gateway",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("gateway-test-key");
|
||||
expect(resolved.source).toContain("AI_GATEWAY_API_KEY");
|
||||
} finally {
|
||||
if (previousGatewayKey === undefined) {
|
||||
delete process.env.AI_GATEWAY_API_KEY;
|
||||
} else {
|
||||
process.env.AI_GATEWAY_API_KEY = previousGatewayKey;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers Bedrock bearer token over access keys and profile", async () => {
|
||||
@@ -368,113 +335,63 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("accepts VOYAGE_API_KEY for voyage", async () => {
|
||||
const previous = process.env.VOYAGE_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.VOYAGE_API_KEY = "voyage-test-key";
|
||||
|
||||
await withEnvUpdates({ VOYAGE_API_KEY: "voyage-test-key" }, async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "voyage",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("voyage-test-key");
|
||||
expect(resolved.source).toContain("VOYAGE_API_KEY");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.VOYAGE_API_KEY;
|
||||
} else {
|
||||
process.env.VOYAGE_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("strips embedded CR/LF from ANTHROPIC_API_KEY", async () => {
|
||||
const previous = process.env.ANTHROPIC_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.ANTHROPIC_API_KEY = "sk-ant-test-\r\nkey";
|
||||
|
||||
await withEnvUpdates({ ANTHROPIC_API_KEY: "sk-ant-test-\r\nkey" }, async () => {
|
||||
const resolved = resolveEnvApiKey("anthropic");
|
||||
expect(resolved?.apiKey).toBe("sk-ant-test-key");
|
||||
expect(resolved?.source).toContain("ANTHROPIC_API_KEY");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
} else {
|
||||
process.env.ANTHROPIC_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("resolveEnvApiKey('huggingface') returns HUGGINGFACE_HUB_TOKEN when set", async () => {
|
||||
const prevHub = process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
const prevHf = process.env.HF_TOKEN;
|
||||
try {
|
||||
delete process.env.HF_TOKEN;
|
||||
process.env.HUGGINGFACE_HUB_TOKEN = "hf_hub_xyz";
|
||||
|
||||
const resolved = resolveEnvApiKey("huggingface");
|
||||
expect(resolved?.apiKey).toBe("hf_hub_xyz");
|
||||
expect(resolved?.source).toContain("HUGGINGFACE_HUB_TOKEN");
|
||||
} finally {
|
||||
if (prevHub === undefined) {
|
||||
delete process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
} else {
|
||||
process.env.HUGGINGFACE_HUB_TOKEN = prevHub;
|
||||
}
|
||||
if (prevHf === undefined) {
|
||||
delete process.env.HF_TOKEN;
|
||||
} else {
|
||||
process.env.HF_TOKEN = prevHf;
|
||||
}
|
||||
}
|
||||
await withEnvUpdates(
|
||||
{
|
||||
HUGGINGFACE_HUB_TOKEN: "hf_hub_xyz",
|
||||
HF_TOKEN: undefined,
|
||||
},
|
||||
async () => {
|
||||
const resolved = resolveEnvApiKey("huggingface");
|
||||
expect(resolved?.apiKey).toBe("hf_hub_xyz");
|
||||
expect(resolved?.source).toContain("HUGGINGFACE_HUB_TOKEN");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("resolveEnvApiKey('huggingface') prefers HUGGINGFACE_HUB_TOKEN over HF_TOKEN when both set", async () => {
|
||||
const prevHub = process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
const prevHf = process.env.HF_TOKEN;
|
||||
try {
|
||||
process.env.HUGGINGFACE_HUB_TOKEN = "hf_hub_first";
|
||||
process.env.HF_TOKEN = "hf_second";
|
||||
|
||||
const resolved = resolveEnvApiKey("huggingface");
|
||||
expect(resolved?.apiKey).toBe("hf_hub_first");
|
||||
expect(resolved?.source).toContain("HUGGINGFACE_HUB_TOKEN");
|
||||
} finally {
|
||||
if (prevHub === undefined) {
|
||||
delete process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
} else {
|
||||
process.env.HUGGINGFACE_HUB_TOKEN = prevHub;
|
||||
}
|
||||
if (prevHf === undefined) {
|
||||
delete process.env.HF_TOKEN;
|
||||
} else {
|
||||
process.env.HF_TOKEN = prevHf;
|
||||
}
|
||||
}
|
||||
await withEnvUpdates(
|
||||
{
|
||||
HUGGINGFACE_HUB_TOKEN: "hf_hub_first",
|
||||
HF_TOKEN: "hf_second",
|
||||
},
|
||||
async () => {
|
||||
const resolved = resolveEnvApiKey("huggingface");
|
||||
expect(resolved?.apiKey).toBe("hf_hub_first");
|
||||
expect(resolved?.source).toContain("HUGGINGFACE_HUB_TOKEN");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("resolveEnvApiKey('huggingface') returns HF_TOKEN when only HF_TOKEN set", async () => {
|
||||
const prevHub = process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
const prevHf = process.env.HF_TOKEN;
|
||||
try {
|
||||
delete process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
process.env.HF_TOKEN = "hf_abc123";
|
||||
|
||||
const resolved = resolveEnvApiKey("huggingface");
|
||||
expect(resolved?.apiKey).toBe("hf_abc123");
|
||||
expect(resolved?.source).toContain("HF_TOKEN");
|
||||
} finally {
|
||||
if (prevHub === undefined) {
|
||||
delete process.env.HUGGINGFACE_HUB_TOKEN;
|
||||
} else {
|
||||
process.env.HUGGINGFACE_HUB_TOKEN = prevHub;
|
||||
}
|
||||
if (prevHf === undefined) {
|
||||
delete process.env.HF_TOKEN;
|
||||
} else {
|
||||
process.env.HF_TOKEN = prevHf;
|
||||
}
|
||||
}
|
||||
await withEnvUpdates(
|
||||
{
|
||||
HUGGINGFACE_HUB_TOKEN: undefined,
|
||||
HF_TOKEN: "hf_abc123",
|
||||
},
|
||||
async () => {
|
||||
const resolved = resolveEnvApiKey("huggingface");
|
||||
expect(resolved?.apiKey).toBe("hf_abc123");
|
||||
expect(resolved?.source).toContain("HF_TOKEN");
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user