Files
CrossRelay/internal/config/config_test.go
T

243 lines
7.3 KiB
Go

package config
import (
"strings"
"testing"
)
func TestResolveModel(t *testing.T) {
p := &Provider{
Models: Models{
Default: "ds-chat",
Sonnet: "ds-chat",
Haiku: "ds-haiku",
Opus: "ds-opus",
Extra: map[string]string{"claude-3-5-haiku-20241022": "ds-fast"},
},
}
cases := []struct {
requested string
want string
}{
{"claude-sonnet-4-5-20250929", "ds-chat"},
{"claude-3-5-haiku-20241022", "ds-fast"}, // exact Extra match wins
{"some-haiku-model", "ds-haiku"}, // keyword match
{"claude-opus-4-1", "ds-opus"},
{"ds-chat", "ds-chat"}, // already an upstream name
{"unknown-model", "ds-chat"},
{"", "ds-chat"},
}
for _, tc := range cases {
if got := p.ResolveModel(tc.requested); got != tc.want {
t.Errorf("ResolveModel(%q) = %q, want %q", tc.requested, got, tc.want)
}
}
}
func TestResolveModelWithoutDefaults(t *testing.T) {
p := &Provider{Models: Models{Sonnet: "kimi-k2"}}
if got := p.ResolveModel("claude-3-7-sonnet"); got != "kimi-k2" {
t.Errorf("sonnet request = %q, want kimi-k2", got)
}
// No default configured: unknown models pass through unchanged.
if got := p.ResolveModel("weird-model"); got != "weird-model" {
t.Errorf("unknown request = %q, want passthrough", got)
}
}
func TestStoreRoundTrip(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
if !strings.HasPrefix(store.Config().RelayToken, "crl_") {
t.Fatalf("relay token = %q, want crl_ prefix", store.Config().RelayToken)
}
if store.Config().Language != LanguageChinese {
t.Fatalf("default language = %q", store.Config().Language)
}
p, err := store.AddProvider("DeepSeek", "deepseek", "https://api.deepseek.com/v1", "sk-test",
Models{Default: "deepseek-chat", Sonnet: "deepseek-chat", Haiku: "deepseek-chat", Opus: "deepseek-chat"})
if err != nil {
t.Fatalf("AddProvider: %v", err)
}
if _, err := store.ActivateClaude(p.ID); err != nil {
t.Fatalf("ActivateClaude: %v", err)
}
reloaded, err := Load()
if err != nil {
t.Fatalf("reload: %v", err)
}
if got := reloaded.ActiveClaude(); got == nil || got.Name != "DeepSeek" {
t.Fatalf("active provider after reload = %+v", got)
}
if len(reloaded.Config().Providers) != 1 {
t.Fatalf("providers after reload = %d", len(reloaded.Config().Providers))
}
}
func TestAddProviderValidation(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, _ := Load()
cases := []struct {
name, baseURL, apiKey string
}{
{"", "https://x.com/v1", "sk"},
{"p", "", "sk"},
{"p", "ftp://x.com", "sk"},
{"p", "https://x.com/v1", ""},
}
for _, tc := range cases {
if _, err := store.AddProvider(tc.name, "", tc.baseURL, tc.apiKey, Models{}); err == nil {
t.Errorf("AddProvider(%q, %q, %q) succeeded, want error", tc.name, tc.baseURL, tc.apiKey)
}
}
}
func TestUpdateProviderKeepsKeyWhenEmpty(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, _ := Load()
p, _ := store.AddProvider("p", "", "https://x.com/v1", "sk-secret", Models{Default: "m1"})
updated, err := store.UpdateProvider(p.ID, "p2", "", "https://x.com/v1/", "", Models{Default: "m2"})
if err != nil {
t.Fatalf("UpdateProvider: %v", err)
}
if updated.APIKey != "sk-secret" {
t.Errorf("api key = %q, want sk-secret", updated.APIKey)
}
if updated.Name != "p2" || updated.Models.Default != "m2" {
t.Errorf("update lost fields: %+v", updated)
}
if updated.BaseURL != "https://x.com/v1" {
t.Errorf("base url = %q, want trailing slash trimmed", updated.BaseURL)
}
}
func TestDeleteProviderClearsActive(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, _ := Load()
p, _ := store.AddProvider("p", "", "https://x.com/v1", "sk", Models{})
store.ActivateClaude(p.ID)
if err := store.DeleteProvider(p.ID); err != nil {
t.Fatalf("DeleteProvider: %v", err)
}
if store.ActiveClaude() != nil {
t.Error("active provider survived deletion")
}
}
func TestActivateCodexRoundTrip(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
p, err := store.AddProvider("DeepSeek", "deepseek", "https://api.deepseek.com/v1", "sk-test",
Models{Default: "deepseek-chat"})
if err != nil {
t.Fatalf("AddProvider: %v", err)
}
if _, err := store.ActivateCodex(p.ID); err != nil {
t.Fatalf("ActivateCodex: %v", err)
}
if err := store.SetCodexPriorProvider("custom"); err != nil {
t.Fatalf("SetCodexPriorProvider: %v", err)
}
reloaded, err := Load()
if err != nil {
t.Fatalf("reload: %v", err)
}
active := reloaded.ActiveCodex()
if active == nil || active.Name != "DeepSeek" {
t.Fatalf("active codex provider after reload = %+v", active)
}
if !active.EnabledForCodex {
t.Error("enabled_for_codex not persisted")
}
if reloaded.Config().CodexPriorProvider != "custom" {
t.Errorf("prior provider = %q, want custom", reloaded.Config().CodexPriorProvider)
}
if err := reloaded.ClearActiveCodex(); err != nil {
t.Fatalf("ClearActiveCodex: %v", err)
}
if reloaded.ActiveCodex() != nil {
t.Error("active codex provider survived clear")
}
if _, err := reloaded.ActivateCodex("p_missing"); err == nil {
t.Error("ActivateCodex accepted an unknown id")
}
}
func TestUpdateProviderKeepsEnabledFlags(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
p, err := store.AddProvider("DeepSeek", "deepseek", "https://api.deepseek.com/v1", "sk-test",
Models{Default: "deepseek-chat"})
if err != nil {
t.Fatalf("AddProvider: %v", err)
}
if _, err := store.ActivateClaude(p.ID); err != nil {
t.Fatalf("ActivateClaude: %v", err)
}
if _, err := store.ActivateCodex(p.ID); err != nil {
t.Fatalf("ActivateCodex: %v", err)
}
updated, err := store.UpdateProvider(p.ID, "DeepSeek 2", "deepseek", "https://api.deepseek.com/v1", "",
Models{Default: "deepseek-reasoner"})
if err != nil {
t.Fatalf("UpdateProvider: %v", err)
}
if !updated.EnabledForClaude || !updated.EnabledForCodex {
t.Errorf("enabled flags lost: claude=%v codex=%v", updated.EnabledForClaude, updated.EnabledForCodex)
}
if updated.APIKey != "sk-test" {
t.Errorf("api key = %q, want the stored key kept", updated.APIKey)
}
}
func TestDeleteProviderClearsCodexActive(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
p, err := store.AddProvider("DeepSeek", "deepseek", "https://api.deepseek.com/v1", "sk-test",
Models{Default: "deepseek-chat"})
if err != nil {
t.Fatalf("AddProvider: %v", err)
}
if _, err := store.ActivateCodex(p.ID); err != nil {
t.Fatalf("ActivateCodex: %v", err)
}
if err := store.DeleteProvider(p.ID); err != nil {
t.Fatalf("DeleteProvider: %v", err)
}
if store.ActiveCodex() != nil {
t.Error("deleted provider stayed active for codex")
}
}
func TestSetGatewayValidation(t *testing.T) {
t.Setenv("CROSSRELAY_HOME", t.TempDir())
store, _ := Load()
if err := store.SetGateway(Gateway{PortMode: "fixed", FixedPort: 8787}); err != nil {
t.Fatalf("SetGateway: %v", err)
}
if err := store.SetGateway(Gateway{PortMode: "fixed", FixedPort: 0}); err == nil {
t.Error("fixed port 0 accepted")
}
if err := store.SetGateway(Gateway{PortMode: "bogus"}); err == nil {
t.Error("bogus port mode accepted")
}
if err := store.SetLanguage("fr"); err == nil {
t.Error("unknown language accepted")
}
}