| 1 | package config |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestIndependentWebSearchRoutes(t *testing.T) { |
| 6 | for _, tc := range []struct { |
| 7 | base, request string |
| 8 | want bool |
| 9 | }{ |
| 10 | {"https://api.deepseek.com", "", true}, |
| 11 | {"https://api.deepseek.com/v1/", "", true}, |
| 12 | {"http://api.deepseek.com", "", false}, |
| 13 | {"https://api.deepseek.com.evil.test", "", false}, |
| 14 | {"https://api.deepseek.com/custom", "", false}, |
| 15 | {"https://api.deepseek.com", "https://relay.test/chat", false}, |
| 16 | } { |
| 17 | entry := ProviderEntry{Name: "ds", Kind: "openai", BaseURL: tc.base, RequestURL: tc.request, Model: "deepseek-v4-flash", resolvedAPIKey: "test-key"} |
| 18 | cfg := &Config{Providers: []ProviderEntry{entry}} |
| 19 | got := cfg.ResolveWebSearchProvider(&entry) |
| 20 | if (got != nil) != tc.want { |
| 21 | t.Fatalf("route %s (%s) = %+v", tc.base, tc.request, got) |
| 22 | } |
| 23 | if got != nil && (got.Kind != "anthropic" || got.BaseURL != "https://api.deepseek.com/anthropic" || got.APIKey() != "test-key") { |
| 24 | t.Fatalf("wrong auxiliary route: %+v", got) |
| 25 | } |
| 26 | if entry.Kind != "openai" || entry.BaseURL != tc.base { |
| 27 | t.Fatal("selection mutated chat configuration") |
| 28 | } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | func TestIndependentWebSearchSelectionAndIsolation(t *testing.T) { |
| 33 | entry := ProviderEntry{Name: "search", Kind: "responses", BaseURL: "http://localhost:8080", Model: "m", WebSearch: boolPointer(true), ResponsesMode: "stateful", Headers: map[string]string{"X-Test": "original"}} |
| 34 | cfg := &Config{Providers: []ProviderEntry{entry}} |
| 35 | got := cfg.ResolveWebSearchProvider(&ProviderEntry{Name: "chat", Kind: "openai", Model: "m"}) |
| 36 | if got == nil || got.BaseURL != entry.BaseURL || got.ResponsesMode != "stateless" || got.ResponsesStateful == nil || *got.ResponsesStateful { |
| 37 | t.Fatalf("bad fallback: %+v", got) |
| 38 | } |
| 39 | got.Headers["X-Test"] = "changed" |
| 40 | if entry.Headers["X-Test"] != "original" { |
| 41 | t.Fatal("search and chat share mutable headers") |
| 42 | } |
| 43 | entry.WebSearch = boolPointer(false) |
| 44 | if cfg.ResolveWebSearchProvider(&entry) != nil { |
| 45 | t.Fatal("explicit off must prevent fallback") |
| 46 | } |
| 47 | cfg.Providers[0].WebSearch = nil |
| 48 | if cfg.ResolveWebSearchProvider(nil) != nil { |
| 49 | t.Fatal("unverified endpoint must remain opt-in") |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | func TestOfficialSearchAlwaysUsesMessagesWithoutChangingChat(t *testing.T) { |
| 54 | for _, kind := range []string{"openai", "anthropic", "responses"} { |
| 55 | base := "https://api.deepseek.com" |
| 56 | if kind == "anthropic" { |
| 57 | base += "/anthropic" |
| 58 | } |
| 59 | entry := ProviderEntry{Name: "ds", Kind: kind, BaseURL: base, Model: "deepseek-v4-flash", resolvedAPIKey: "test-key"} |
| 60 | cfg := &Config{} |
| 61 | route := cfg.ResolveWebSearchProvider(&entry) |
| 62 | if route == nil || route.Kind != "anthropic" || route.BaseURL != "https://api.deepseek.com/anthropic" || route.Model != entry.Model || route.APIKey() != "test-key" { |
| 63 | t.Fatalf("%s search route = %+v", kind, route) |
| 64 | } |
| 65 | if entry.Kind != kind || entry.BaseURL != base { |
| 66 | t.Fatal("changed main protocol") |
| 67 | } |
| 68 | entry.RequestURL = "https://relay.example/custom" |
| 69 | if IsOfficialDeepSeekSearchEndpoint(&entry) { |
| 70 | t.Fatal("request override classified as official") |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 |