mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
test: genericize synthetic auth coverage
This commit is contained in:
@@ -69,9 +69,9 @@ vi.mock("./model-auth-env-vars.js", () => {
|
||||
const candidates = {
|
||||
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
|
||||
google: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
||||
"demo-local": ["DEMO_LOCAL_API_KEY"],
|
||||
huggingface: ["HUGGINGFACE_HUB_TOKEN", "HF_TOKEN"],
|
||||
"minimax-portal": ["MINIMAX_OAUTH_TOKEN", "MINIMAX_API_KEY"],
|
||||
ollama: ["OLLAMA_API_KEY"],
|
||||
"opencode-go": ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"],
|
||||
openai: ["OPENAI_API_KEY"],
|
||||
qianfan: ["QIANFAN_API_KEY"],
|
||||
@@ -104,26 +104,19 @@ vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
provider: string;
|
||||
context: { providerConfig?: { api?: string; baseUrl?: string; models?: unknown[] } };
|
||||
}) => {
|
||||
if (params.provider !== "ollama" && params.provider !== "demo-local") {
|
||||
if (params.provider !== "demo-local") {
|
||||
return undefined;
|
||||
}
|
||||
const providerConfig = params.context.providerConfig;
|
||||
const hasMeaningfulOllamaConfig =
|
||||
params.provider !== "ollama"
|
||||
? Boolean(providerConfig?.api?.trim()) ||
|
||||
Boolean(providerConfig?.baseUrl?.trim()) ||
|
||||
(Array.isArray(providerConfig?.models) && providerConfig.models.length > 0)
|
||||
: (Array.isArray(providerConfig?.models) && providerConfig.models.length > 0) ||
|
||||
Boolean(providerConfig?.api?.trim() && providerConfig.api.trim() !== "ollama") ||
|
||||
Boolean(
|
||||
providerConfig?.baseUrl?.trim() &&
|
||||
providerConfig.baseUrl.trim().replace(/\/+$/, "") !== "http://127.0.0.1:11434",
|
||||
);
|
||||
if (!hasMeaningfulOllamaConfig) {
|
||||
const hasMeaningfulConfig =
|
||||
Boolean(providerConfig?.api?.trim()) ||
|
||||
Boolean(providerConfig?.baseUrl?.trim()) ||
|
||||
(Array.isArray(providerConfig?.models) && providerConfig.models.length > 0);
|
||||
if (!hasMeaningfulConfig) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
apiKey: params.provider === "ollama" ? "ollama-local" : "demo-local",
|
||||
apiKey: "demo-local",
|
||||
source: `models.providers.${params.provider} (synthetic local key)`,
|
||||
mode: "api-key" as const,
|
||||
};
|
||||
@@ -133,12 +126,7 @@ vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
provider: string;
|
||||
context: { resolvedApiKey?: string };
|
||||
}) => {
|
||||
const expectedMarker =
|
||||
params.provider === "ollama"
|
||||
? "ollama-local"
|
||||
: params.provider === "demo-local"
|
||||
? "demo-local"
|
||||
: undefined;
|
||||
const expectedMarker = params.provider === "demo-local" ? "demo-local" : undefined;
|
||||
return Boolean(expectedMarker && params.context.resolvedApiKey?.trim() === expectedMarker);
|
||||
},
|
||||
}));
|
||||
@@ -207,15 +195,15 @@ async function expectBedrockAuthSource(params: {
|
||||
});
|
||||
}
|
||||
|
||||
function buildOllamaStore(keys: string[]) {
|
||||
function buildDemoLocalStore(keys: string[]) {
|
||||
return {
|
||||
version: 1 as const,
|
||||
profiles: Object.fromEntries(
|
||||
keys.map((key, index) => [
|
||||
index === 0 ? "ollama:default" : `ollama:${index + 1}`,
|
||||
index === 0 ? "demo-local:default" : `demo-local:${index + 1}`,
|
||||
{
|
||||
type: "api_key" as const,
|
||||
provider: "ollama" as const,
|
||||
provider: "demo-local" as const,
|
||||
key,
|
||||
},
|
||||
]),
|
||||
@@ -223,13 +211,13 @@ function buildOllamaStore(keys: string[]) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildOllamaProviderCfg(apiKey: string): OpenClawConfig {
|
||||
function buildDemoLocalProviderCfg(apiKey: string): OpenClawConfig {
|
||||
return {
|
||||
models: {
|
||||
providers: {
|
||||
ollama: {
|
||||
baseUrl: "https://ollama.com",
|
||||
api: "ollama",
|
||||
"demo-local": {
|
||||
baseUrl: "https://local-provider.example",
|
||||
api: "openai-completions",
|
||||
apiKey,
|
||||
models: [],
|
||||
},
|
||||
@@ -238,17 +226,17 @@ function buildOllamaProviderCfg(apiKey: string): OpenClawConfig {
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveOllamaApiKey(params: {
|
||||
async function resolveDemoLocalApiKey(params: {
|
||||
envApiKey: string | undefined;
|
||||
storedKeys: string[];
|
||||
configuredApiKey: string;
|
||||
}) {
|
||||
let resolved!: Awaited<ReturnType<typeof resolveApiKeyForProvider>>;
|
||||
await withEnvAsync({ OLLAMA_API_KEY: params.envApiKey }, async () => {
|
||||
await withEnvAsync({ DEMO_LOCAL_API_KEY: params.envApiKey }, async () => {
|
||||
resolved = await resolveApiKeyForProvider({
|
||||
provider: "ollama",
|
||||
store: buildOllamaStore(params.storedKeys),
|
||||
cfg: buildOllamaProviderCfg(params.configuredApiKey),
|
||||
provider: "demo-local",
|
||||
store: buildDemoLocalStore(params.storedKeys),
|
||||
cfg: buildDemoLocalProviderCfg(params.configuredApiKey),
|
||||
});
|
||||
});
|
||||
return resolved;
|
||||
@@ -517,16 +505,16 @@ describe("getApiKeyForModel", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves synthetic local auth key for configured ollama provider without apiKey", async () => {
|
||||
await withEnvAsync({ OLLAMA_API_KEY: undefined }, async () => {
|
||||
it("resolves plugin-owned synthetic local auth for a configured provider without apiKey", async () => {
|
||||
await withEnvAsync({ DEMO_LOCAL_API_KEY: undefined }, async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "ollama",
|
||||
provider: "demo-local",
|
||||
store: { version: 1, profiles: {} },
|
||||
cfg: {
|
||||
models: {
|
||||
providers: {
|
||||
ollama: {
|
||||
baseUrl: "http://gpu-node-server:11434",
|
||||
"demo-local": {
|
||||
baseUrl: "http://local-provider:11434",
|
||||
api: "openai-completions",
|
||||
models: [],
|
||||
},
|
||||
@@ -534,45 +522,44 @@ describe("getApiKeyForModel", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(resolved.apiKey).toBe("ollama-local");
|
||||
expect(resolved.apiKey).toBe("demo-local");
|
||||
expect(resolved.mode).toBe("api-key");
|
||||
expect(resolved.source).toContain("synthetic local key");
|
||||
});
|
||||
});
|
||||
|
||||
it("does not mint synthetic local auth for default-ish ollama stubs", async () => {
|
||||
await withEnvAsync({ OLLAMA_API_KEY: undefined }, async () => {
|
||||
it("does not mint synthetic local auth for empty provider stubs", async () => {
|
||||
await withEnvAsync({ DEMO_LOCAL_API_KEY: undefined }, async () => {
|
||||
await expect(
|
||||
resolveApiKeyForProvider({
|
||||
provider: "ollama",
|
||||
provider: "demo-local",
|
||||
store: { version: 1, profiles: {} },
|
||||
cfg: {
|
||||
models: {
|
||||
providers: {
|
||||
ollama: {
|
||||
baseUrl: "http://127.0.0.1:11434",
|
||||
api: "ollama",
|
||||
"demo-local": {
|
||||
baseUrl: "",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/No API key found for provider "ollama"/);
|
||||
).rejects.toThrow(/No API key found for provider "demo-local"/);
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers explicit OLLAMA_API_KEY over synthetic local key", async () => {
|
||||
await withEnvAsync({ [envVar("OLLAMA", "API", "KEY")]: "env-ollama-key" }, async () => {
|
||||
it("prefers explicit provider env auth over synthetic local key", async () => {
|
||||
await withEnvAsync({ [envVar("DEMO", "LOCAL", "API", "KEY")]: "env-demo-key" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "ollama",
|
||||
provider: "demo-local",
|
||||
store: { version: 1, profiles: {} },
|
||||
cfg: {
|
||||
models: {
|
||||
providers: {
|
||||
ollama: {
|
||||
baseUrl: "http://gpu-node-server:11434",
|
||||
"demo-local": {
|
||||
baseUrl: "http://local-provider:11434",
|
||||
api: "openai-completions",
|
||||
models: [],
|
||||
},
|
||||
@@ -580,63 +567,63 @@ describe("getApiKeyForModel", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(resolved.apiKey).toBe("env-ollama-key");
|
||||
expect(resolved.source).toContain("OLLAMA_API_KEY");
|
||||
expect(resolved.apiKey).toBe("env-demo-key");
|
||||
expect(resolved.source).toContain("DEMO_LOCAL_API_KEY");
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers explicit OLLAMA_API_KEY over the stored ollama-local profile", async () => {
|
||||
const resolved = await resolveOllamaApiKey({
|
||||
envApiKey: "env-ollama-key",
|
||||
storedKeys: ["ollama-local"],
|
||||
configuredApiKey: "OLLAMA_API_KEY",
|
||||
it("prefers explicit provider env auth over a stored synthetic local profile", async () => {
|
||||
const resolved = await resolveDemoLocalApiKey({
|
||||
envApiKey: "env-demo-key",
|
||||
storedKeys: ["demo-local"],
|
||||
configuredApiKey: "DEMO_LOCAL_API_KEY",
|
||||
});
|
||||
expect(resolved.apiKey).toBe("env-ollama-key");
|
||||
expect(resolved.source).toContain("OLLAMA_API_KEY");
|
||||
expect(resolved.apiKey).toBe("env-demo-key");
|
||||
expect(resolved.source).toContain("DEMO_LOCAL_API_KEY");
|
||||
expect(resolved.profileId).toBeUndefined();
|
||||
});
|
||||
|
||||
it("prefers explicit configured ollama apiKey over the stored ollama-local profile", async () => {
|
||||
const resolved = await resolveOllamaApiKey({
|
||||
it("prefers explicit configured apiKey over a stored synthetic local profile", async () => {
|
||||
const resolved = await resolveDemoLocalApiKey({
|
||||
envApiKey: undefined,
|
||||
storedKeys: ["ollama-local"],
|
||||
configuredApiKey: "config-ollama-key",
|
||||
storedKeys: ["demo-local"],
|
||||
configuredApiKey: "config-demo-key",
|
||||
});
|
||||
expect(resolved.apiKey).toBe("config-ollama-key");
|
||||
expect(resolved.apiKey).toBe("config-demo-key");
|
||||
expect(resolved.source).toBe("models.json");
|
||||
expect(resolved.profileId).toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls back to the stored ollama-local profile when no real ollama auth exists", async () => {
|
||||
const resolved = await resolveOllamaApiKey({
|
||||
it("falls back to the stored synthetic local profile when no real auth exists", async () => {
|
||||
const resolved = await resolveDemoLocalApiKey({
|
||||
envApiKey: undefined,
|
||||
storedKeys: ["ollama-local"],
|
||||
configuredApiKey: "OLLAMA_API_KEY",
|
||||
storedKeys: ["demo-local"],
|
||||
configuredApiKey: "DEMO_LOCAL_API_KEY",
|
||||
});
|
||||
expect(resolved.apiKey).toBe("ollama-local");
|
||||
expect(resolved.source).toBe("profile:ollama:default");
|
||||
expect(resolved.profileId).toBe("ollama:default");
|
||||
expect(resolved.apiKey).toBe("demo-local");
|
||||
expect(resolved.source).toBe("profile:demo-local:default");
|
||||
expect(resolved.profileId).toBe("demo-local:default");
|
||||
});
|
||||
|
||||
it("keeps a real stored ollama profile ahead of env auth", async () => {
|
||||
const resolved = await resolveOllamaApiKey({
|
||||
envApiKey: "env-ollama-key",
|
||||
storedKeys: ["stored-ollama-key"],
|
||||
configuredApiKey: "OLLAMA_API_KEY",
|
||||
it("keeps a real stored profile ahead of env auth", async () => {
|
||||
const resolved = await resolveDemoLocalApiKey({
|
||||
envApiKey: "env-demo-key",
|
||||
storedKeys: ["stored-demo-key"],
|
||||
configuredApiKey: "DEMO_LOCAL_API_KEY",
|
||||
});
|
||||
expect(resolved.apiKey).toBe("stored-ollama-key");
|
||||
expect(resolved.source).toBe("profile:ollama:default");
|
||||
expect(resolved.profileId).toBe("ollama:default");
|
||||
expect(resolved.apiKey).toBe("stored-demo-key");
|
||||
expect(resolved.source).toBe("profile:demo-local:default");
|
||||
expect(resolved.profileId).toBe("demo-local:default");
|
||||
});
|
||||
|
||||
it("defers every stored ollama-local profile until real auth sources are checked", async () => {
|
||||
const resolved = await resolveOllamaApiKey({
|
||||
envApiKey: "env-ollama-key",
|
||||
storedKeys: ["ollama-local", "ollama-local"],
|
||||
configuredApiKey: "OLLAMA_API_KEY",
|
||||
it("defers every stored synthetic local profile until real auth sources are checked", async () => {
|
||||
const resolved = await resolveDemoLocalApiKey({
|
||||
envApiKey: "env-demo-key",
|
||||
storedKeys: ["demo-local", "demo-local"],
|
||||
configuredApiKey: "DEMO_LOCAL_API_KEY",
|
||||
});
|
||||
expect(resolved.apiKey).toBe("env-ollama-key");
|
||||
expect(resolved.source).toContain("OLLAMA_API_KEY");
|
||||
expect(resolved.apiKey).toBe("env-demo-key");
|
||||
expect(resolved.source).toContain("DEMO_LOCAL_API_KEY");
|
||||
expect(resolved.profileId).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -671,14 +658,14 @@ describe("getApiKeyForModel", () => {
|
||||
expect(resolved.profileId).toBeUndefined();
|
||||
});
|
||||
|
||||
it("still throws for ollama when no env/profile/config provider is available", async () => {
|
||||
await withEnvAsync({ OLLAMA_API_KEY: undefined }, async () => {
|
||||
it("still throws when no env/profile/config provider auth is available", async () => {
|
||||
await withEnvAsync({ DEMO_LOCAL_API_KEY: undefined }, async () => {
|
||||
await expect(
|
||||
resolveApiKeyForProvider({
|
||||
provider: "ollama",
|
||||
provider: "demo-local",
|
||||
store: { version: 1, profiles: {} },
|
||||
}),
|
||||
).rejects.toThrow('No API key found for provider "ollama".');
|
||||
).rejects.toThrow('No API key found for provider "demo-local".');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -414,7 +414,7 @@ export async function resolveApiKeyForProvider(params: {
|
||||
store?: AuthProfileStore;
|
||||
agentDir?: string;
|
||||
/** When true, treat profileId as a user-locked selection that must not be
|
||||
* silently overridden by env/config credentials (e.g. ollama-local). */
|
||||
* silently overridden by env/config credentials. */
|
||||
lockedProfile?: boolean;
|
||||
credentialPrecedence?: ProviderCredentialPrecedence;
|
||||
}): Promise<ResolvedProviderAuth> {
|
||||
|
||||
@@ -7,7 +7,7 @@ export type PluginHookBeforeModelResolveEvent = {
|
||||
export type PluginHookBeforeModelResolveResult = {
|
||||
/** Override the model for this agent run. E.g. "llama3.3:8b" */
|
||||
modelOverride?: string;
|
||||
/** Override the provider for this agent run. E.g. "ollama" */
|
||||
/** Override the provider for this agent run. E.g. "local-provider" */
|
||||
providerOverride?: string;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user