Files
fathah--hermes-desktop/tests/install-gate-providers.test.ts
2026-07-13 13:36:53 +08:00

109 lines
4.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { expectedEnvKeyForModel } from "../src/main/installer";
// Regression tests for #236: the install gate's .env check hard-coded
// only OPENROUTER_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY, so users
// configured for DeepSeek, Groq, Mistral, etc. saw the "set AI provider"
// first-run screen every restart, even with a valid key in .env.
//
// The new `expectedEnvKeyForModel(provider, baseUrl)` returns the
// canonical env var name the gateway expects per provider, falling back
// to URL-pattern matching for `custom` / `auto` providers pointed at a
// known endpoint, then to null for unrecognized configurations (where
// the caller does a permissive `*_API_KEY` scan).
describe("expectedEnvKeyForModel — provider-name lookup", () => {
it.each([
["openrouter", "OPENROUTER_API_KEY"],
["anthropic", "ANTHROPIC_API_KEY"],
["openai", "OPENAI_API_KEY"],
["ollama-cloud", "OLLAMA_API_KEY"],
["aimlapi", "AIMLAPI_API_KEY"],
["google", "GOOGLE_API_KEY"],
["xai", "XAI_API_KEY"],
["deepseek", "DEEPSEEK_API_KEY"], // the specific provider from issue #236
["groq", "GROQ_API_KEY"],
["mistral", "MISTRAL_API_KEY"],
["together", "TOGETHER_API_KEY"],
["fireworks", "FIREWORKS_API_KEY"],
["cerebras", "CEREBRAS_API_KEY"],
["perplexity", "PERPLEXITY_API_KEY"],
["huggingface", "HF_TOKEN"], // exception to the *_API_KEY convention
["alibaba", "DASHSCOPE_API_KEY"],
// The agent's DashScope aliases — `qwen` is what pre-#825 desktop
// configs stored, so it must keep resolving to a real key check.
["dashscope", "DASHSCOPE_API_KEY"],
["qwen", "DASHSCOPE_API_KEY"],
["aliyun", "DASHSCOPE_API_KEY"],
["alibaba-cloud", "DASHSCOPE_API_KEY"],
["qwen-oauth", "QWEN_API_KEY"],
["minimax", "MINIMAX_API_KEY"],
["glm", "GLM_API_KEY"],
["kimi", "KIMI_API_KEY"],
["xiaomi", "XIAOMI_API_KEY"],
])("maps provider %s → %s", (provider, expected) => {
expect(expectedEnvKeyForModel(provider, "")).toBe(expected);
});
it("is case-insensitive on the provider name", () => {
expect(expectedEnvKeyForModel("DeepSeek", "")).toBe("DEEPSEEK_API_KEY");
expect(expectedEnvKeyForModel("ANTHROPIC", "")).toBe("ANTHROPIC_API_KEY");
});
it("trims surrounding whitespace from the provider name", () => {
expect(expectedEnvKeyForModel(" groq ", "")).toBe("GROQ_API_KEY");
});
});
describe("expectedEnvKeyForModel — URL fallback for custom/auto providers", () => {
it("recognizes a known endpoint when provider is 'custom'", () => {
expect(
expectedEnvKeyForModel("custom", "https://api.deepseek.com/v1"),
).toBe("DEEPSEEK_API_KEY");
expect(
expectedEnvKeyForModel("custom", "https://api.groq.com/openai/v1"),
).toBe("GROQ_API_KEY");
expect(
expectedEnvKeyForModel("custom", "https://openrouter.ai/api/v1"),
).toBe("OPENROUTER_API_KEY");
expect(expectedEnvKeyForModel("custom", "https://ollama.com/v1")).toBe(
"OLLAMA_API_KEY",
);
expect(expectedEnvKeyForModel("custom", "https://api.aimlapi.com/v1")).toBe(
"AIMLAPI_API_KEY",
);
expect(
expectedEnvKeyForModel("custom", "https://api.xiaomimimo.com/v1"),
).toBe("XIAOMI_API_KEY");
});
it("recognizes a known endpoint when provider is 'auto'", () => {
expect(expectedEnvKeyForModel("auto", "https://api.mistral.ai/v1")).toBe(
"MISTRAL_API_KEY",
);
});
it("returns null for unknown provider with unknown URL", () => {
expect(
expectedEnvKeyForModel("custom", "http://localhost:1234/v1"),
).toBeNull();
expect(expectedEnvKeyForModel("custom", "")).toBeNull();
expect(expectedEnvKeyForModel("", "")).toBeNull();
});
it("provider-name match wins over URL fallback when both are present", () => {
// If somehow provider is "anthropic" but baseUrl points at deepseek
// (configuration error), trust the provider name — the gateway
// dispatches by provider, not URL.
expect(
expectedEnvKeyForModel("anthropic", "https://api.deepseek.com/v1"),
).toBe("ANTHROPIC_API_KEY");
});
});
// The actual install-gate behavior is exercised end-to-end by importing
// the module under a per-test HERMES_HOME — same pattern as the other
// installer tests. Skipped here because the existing tests already cover
// the file-existence half; the gap was provider awareness, which the
// pure-function tests above pin down.