返回 DeepSeek-Reasonix
independent_web_search_test.go
根目录 / internal / config / independent_web_search_test.go
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
74 lines GO