返回 DeepSeek-Reasonix
deepseek_chat_default_upgrade_test.go
根目录 / internal / config / deepseek_chat_default_upgrade_test.go
1 package config
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8 "sync"
9 "testing"
10
11 "github.com/BurntSushi/toml"
12 )
13
14 func TestDeepSeekChatDefaultStartupUpgradePreservesData(t *testing.T) {
15 path := filepath.Join(t.TempDir(), "config.toml")
16 raw := `# user comment
17 config_version = 7 # schema
18 future_field = "keep me"
19 default_model = "deepseek/deepseek-v4-pro"
20 [[providers]]
21 name = "deepseek"
22 kind = "anthropic" # transport
23 base_url = "https://api.deepseek.com/anthropic"
24 models = ["deepseek-v4-flash", "deepseek-v4-pro"]
25 default = "deepseek-v4-pro"
26 api_key_env = "DEEPSEEK_API_KEY"
27 web_search = false
28 default_effort = "max"
29 future_provider_field = { value = "untouched" }
30 [providers.prices.deepseek-v4-pro]
31 input = 123
32 [desktop]
33 future_desktop_field = true
34 `
35 if err := os.WriteFile(path, []byte(raw), 0600); err != nil {
36 t.Fatal(err)
37 }
38 changed, err := ApplyUserConfigUpgradesOnStartup(path)
39 if err != nil || !changed {
40 t.Fatalf("upgrade=%v err=%v", changed, err)
41 }
42 got, _ := os.ReadFile(path)
43 want := strings.ReplaceAll(raw, `config_version = 7`, fmt.Sprintf("config_version = %d", Default().ConfigVersion))
44 want = strings.ReplaceAll(want, `models = ["deepseek-v4-flash", "deepseek-v4-pro"]`, `models = ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-flash"]`)
45 want = strings.ReplaceAll(want, `kind = "anthropic"`, `kind = "openai"`)
46 want = strings.ReplaceAll(want, `base_url = "https://api.deepseek.com/anthropic"`, `base_url = "https://api.deepseek.com"`)
47 if string(got) != want {
48 t.Fatalf("unexpected rewrite:\n%s", got)
49 }
50 // After the one-time upgrade, an explicit Messages choice must survive.
51 manual := strings.ReplaceAll(want, `kind = "openai"`, `kind = "anthropic"`)
52 manual = strings.ReplaceAll(manual, `base_url = "https://api.deepseek.com"`, `base_url = "https://api.deepseek.com/anthropic"`)
53 if err := os.WriteFile(path, []byte(manual), 0600); err != nil {
54 t.Fatal(err)
55 }
56 if changed, err := ApplyUserConfigUpgradesOnStartup(path); err != nil || changed {
57 t.Fatalf("overrode post-upgrade choice: %v %v", changed, err)
58 }
59 got, _ = os.ReadFile(path)
60 if string(got) != manual {
61 t.Fatal("post-upgrade config changed")
62 }
63 }
64
65 func TestDeepSeekChatDefaultMigrationScope(t *testing.T) {
66 base := ProviderEntry{Name: "deepseek-flash", Kind: "anthropic", BaseURL: deepSeekAnthropicBaseURL, Model: "deepseek-v4-flash", APIKeyEnv: "DEEPSEEK_API_KEY"}
67 for _, tc := range []struct {
68 name string
69 edit func(*ProviderEntry)
70 want bool
71 }{
72 {"stock", func(*ProviderEntry) {}, true},
73 {"key and search preference", func(p *ProviderEntry) { p.APIKeyEnv = "MY_KEY"; p.WebSearch = boolPointer(false) }, true},
74 {"explicit preset", func(p *ProviderEntry) { p.PresetID = "deepseek-anthropic" }, false},
75 {"separate name", func(p *ProviderEntry) { p.Name = "deepseek-anthropic" }, false},
76 {"responses choice", func(p *ProviderEntry) { p.Kind = "responses"; p.BaseURL = "https://api.deepseek.com" }, false},
77 {"proxy", func(p *ProviderEntry) { p.BaseURL = "https://relay.example/anthropic" }, false},
78 {"request override", func(p *ProviderEntry) { p.RequestURL = "https://api.deepseek.com/anthropic/v1/messages" }, false},
79 {"headers", func(p *ProviderEntry) { p.Headers = map[string]string{"X-Route": "custom"} }, false},
80 {"body override", func(p *ProviderEntry) { p.ExtraBody = map[string]any{"thinking": false} }, false},
81 {"unknown model", func(p *ProviderEntry) { p.Model = "future-model" }, false},
82 } {
83 t.Run(tc.name, func(t *testing.T) {
84 p := base
85 tc.edit(&p)
86 if got := isLegacyDeepSeekMessagesDefault(&p); got != tc.want {
87 t.Fatalf("eligible=%v want %v", got, tc.want)
88 }
89 })
90 }
91 }
92
93 func TestDeepSeekChatDefaultUpgradeInlineAndConcurrent(t *testing.T) {
94 path := filepath.Join(t.TempDir(), "config.toml")
95 raw := `config_version = 7
96 providers = [{name="deepseek-pro",kind="anthropic",base_url="https://api.deepseek.com/anthropic",model="deepseek-v4-pro",api_key_env="DEEPSEEK_API_KEY",web_search=false,future="kept"}]
97 `
98 if err := os.WriteFile(path, []byte(raw), 0600); err != nil {
99 t.Fatal(err)
100 }
101 var wg sync.WaitGroup
102 for range 4 {
103 wg.Go(func() {
104 if _, err := ApplyUserConfigUpgradesOnStartup(path); err != nil {
105 t.Error(err)
106 }
107 })
108 }
109 wg.Wait()
110 got, _ := os.ReadFile(path)
111 var parsed Config
112 if _, err := toml.Decode(string(got), &parsed); err != nil {
113 t.Fatal(err)
114 }
115 if parsed.ConfigVersion != Default().ConfigVersion || len(parsed.Providers) != 1 || parsed.Providers[0].Kind != "openai" || *parsed.Providers[0].WebSearch {
116 t.Fatalf("bad migration: %s", got)
117 }
118 if !strings.Contains(string(got), `future="kept"`) {
119 t.Fatal("lost unknown field")
120 }
121 }
122
123 func TestDeepSeekChatDefaultUpgradeFutureConfigUntouched(t *testing.T) {
124 path := filepath.Join(t.TempDir(), "config.toml")
125 raw := `config_version = 999
126 providers = [{name="deepseek-flash",kind="anthropic",base_url="https://api.deepseek.com/anthropic",model="deepseek-v4-flash",api_key_env="DEEPSEEK_API_KEY"}]
127 `
128 if err := os.WriteFile(path, []byte(raw), 0600); err != nil {
129 t.Fatal(err)
130 }
131 if changed, err := ApplyUserConfigUpgradesOnStartup(path); err != nil || changed {
132 t.Fatalf("future changed: %v %v", changed, err)
133 }
134 got, _ := os.ReadFile(path)
135 if string(got) != raw {
136 t.Fatal("future config rewritten")
137 }
138 }
139
140 func TestOldAutomaticProtocolMigrationDoesNotUndoChatDefault(t *testing.T) {
141 raw := `config_version = 8
142 [[providers]]
143 name = "deepseek-flash"
144 kind = "openai"
145 base_url = "https://api.deepseek.com"
146 model = "deepseek-v4-flash"
147 api_key_env = "DEEPSEEK_API_KEY"
148 `
149 next, changed, err := rewriteLegacyDeepSeekProtocol(raw, "", true)
150 if err != nil || changed || next != raw {
151 t.Fatalf("old migration undid v8 default: changed=%v err=%v", changed, err)
152 }
153 }
154
154 lines GO