返回 DeepSeek-Reasonix
opencode_go_context_test.go
根目录 / internal / config / opencode_go_context_test.go
1 package config
2
3 import (
4 "path/filepath"
5 "testing"
6
7 "github.com/BurntSushi/toml"
8
9 "reasonix/internal/provider"
10 )
11
12 func TestNormalizeLegacyOpenCodeGoInstallsAppliesCatalogAndWindowsInOnePass(t *testing.T) {
13 legacy := ProviderEntry{
14 Name: "opencode-go",
15 Kind: "openai",
16 BaseURL: "https://opencode.ai/zen/go/v1",
17 Models: append([]string(nil), legacyOpenCodeGoModels...),
18 Default: "glm-5.2",
19 ContextWindow: legacyOpenCodeGoChatWindow,
20 PresetID: "opencode-go",
21 }
22 second := legacy
23 second.Name = "opencode-go-secondary"
24 c := &Config{Providers: []ProviderEntry{legacy, second}}
25
26 if !normalizeLegacyOpenCodeGoInstalls(c) {
27 t.Fatal("combined OpenCode Go migration did not report a change")
28 }
29 for i := range c.Providers {
30 got := c.Providers[i]
31 if !got.HasModel("kimi-k3") {
32 t.Fatalf("provider %q did not receive the Kimi K3 catalog update", got.Name)
33 }
34 for model, limits := range provider.OpenCodeGoChatModels() {
35 if window := got.ModelOverrides[model].ContextWindow; window != limits.Context {
36 t.Fatalf("provider %q model %q window = %d, want %d", got.Name, model, window, limits.Context)
37 }
38 }
39 }
40 if normalizeLegacyOpenCodeGoInstalls(c) {
41 t.Fatal("combined OpenCode Go migration was not idempotent")
42 }
43 }
44
45 func TestNormalizeLegacyOpenCodeGoContextWindowsMigratesOnlyUntouchedPresets(t *testing.T) {
46 legacy := ProviderEntry{
47 Name: "opencode-go",
48 Kind: "openai",
49 BaseURL: "https://opencode.ai/zen/go/v1",
50 Models: append([]string(nil), opencodeGoModels...),
51 Default: "glm-5.2",
52 ContextWindow: 128000,
53 PresetID: "opencode-go",
54 ModelOverrides: map[string]ProviderModelOverride{
55 "kimi-k3": {ContextWindow: 1_048_576},
56 },
57 }
58 customEndpoint := legacy
59 customEndpoint.Name = "og-proxy"
60 customEndpoint.BaseURL = "https://gateway.example/v1"
61 customEndpoint.ModelOverrides = cloneModelOverrideMap(legacy.ModelOverrides)
62 customWindow := legacy
63 customWindow.Name = "og-custom-window"
64 customWindow.ContextWindow = 777_000
65 customWindow.ModelOverrides = cloneModelOverrideMap(legacy.ModelOverrides)
66 customOverride := legacy
67 customOverride.Name = "og-custom-override"
68 customOverride.ModelOverrides = map[string]ProviderModelOverride{
69 "glm-5.2": {ContextWindow: 500_000},
70 }
71 c := &Config{Providers: []ProviderEntry{legacy, customEndpoint, customWindow, customOverride}}
72 if !normalizeLegacyOpenCodeGoContextWindows(c) {
73 t.Fatal("expected official legacy preset to migrate")
74 }
75 if c.Providers[0].ModelOverrides["glm-5.2"].ContextWindow != 1_000_000 {
76 t.Fatalf("migrated glm-5.2 = %+v", c.Providers[0].ModelOverrides["glm-5.2"])
77 }
78 if c.Providers[1].ModelOverrides["glm-5.2"].ContextWindow != 0 {
79 t.Fatal("custom endpoint was migrated")
80 }
81 if c.Providers[2].ContextWindow != 777_000 {
82 t.Fatal("custom provider window was overwritten")
83 }
84 if c.Providers[3].ModelOverrides["glm-5.2"].ContextWindow != 500_000 {
85 t.Fatal("positive custom override was overwritten")
86 }
87
88 preIdentity := ProviderEntry{
89 Name: "opencode-go",
90 Kind: "openai",
91 BaseURL: "https://opencode.ai/zen/go/v1",
92 Models: append([]string(nil), opencodeGoModels...),
93 Default: "glm-5.2",
94 ContextWindow: 128000,
95 ModelOverrides: map[string]ProviderModelOverride{
96 "kimi-k3": {ContextWindow: 1_048_576},
97 },
98 }
99 pre := &Config{Providers: []ProviderEntry{preIdentity}}
100 if !normalizeLegacyOpenCodeGoContextWindows(pre) || pre.Providers[0].ModelOverrides["glm-5.2"].ContextWindow != 1_000_000 {
101 t.Fatal("pre-preset-identity OpenCode Go was not migrated")
102 }
103 }
104
105 func TestLoadForEditOpenCodeGoWindowsStayInMemoryUntilSave(t *testing.T) {
106 path := filepath.Join(t.TempDir(), "config.toml")
107 cfg := Default()
108 preset, ok := CuratedProviderPreset("opencode-go")
109 if !ok {
110 t.Fatal("missing preset")
111 }
112 entry := preset.Entries[0]
113 entry.ContextWindow = 128000
114 entry.ModelOverrides = map[string]ProviderModelOverride{"kimi-k3": {ContextWindow: 1_048_576}}
115 cfg.Providers = []ProviderEntry{entry}
116 if err := cfg.SaveTo(path); err != nil {
117 t.Fatal(err)
118 }
119 loaded := LoadForEdit(path)
120 got, _ := loaded.Provider("opencode-go")
121 if got.ModelOverrides["glm-5.2"].ContextWindow != 1_000_000 {
122 t.Fatalf("in-memory migration missing: %+v", got.ModelOverrides)
123 }
124 var disk Config
125 if _, err := toml.DecodeFile(path, &disk); err != nil {
126 t.Fatal(err)
127 }
128 persisted, _ := disk.Provider("opencode-go")
129 if persisted.ModelOverrides["glm-5.2"].ContextWindow != 0 {
130 t.Fatalf("read-only load wrote disk: %+v", persisted.ModelOverrides)
131 }
132 }
133
134 func TestNormalizeLegacyOpenCodeGoRouteCatalogSplitsAggregateModelsAndRetargetsHistory(t *testing.T) {
135 legacy := ProviderEntry{
136 Name: "opencode-go",
137 Kind: "openai",
138 BaseURL: "https://opencode.ai/zen/go/v1",
139 Models: []string{"glm-5.2", "qwen3.7-plus", "grok-4.5", "private-model"},
140 Default: "grok-4.5",
141 APIKeyEnv: "OPENCODE_GO_API_KEY",
142 PresetID: "opencode-go",
143 ContextWindow: legacyOpenCodeGoChatWindow,
144 Headers: map[string]string{"X-User": "keep"},
145 }
146 c := &Config{
147 DefaultModel: "opencode-go/grok-4.5",
148 Agent: AgentConfig{
149 PlannerModel: "grok-4.5",
150 VisionModel: "opencode-go/grok-4.5",
151 SubagentModel: "opencode-go/qwen3.7-plus",
152 SubagentModels: map[string]string{"review": "grok-4.5", "local": "private-model"},
153 },
154 Bot: BotConfig{Model: "opencode-go/grok-4.5"},
155 Desktop: DesktopConfig{ProviderAccess: []string{"opencode-go"}},
156 Providers: []ProviderEntry{legacy},
157 }
158 if !normalizeLegacyOpenCodeGoInstalls(c) {
159 t.Fatal("aggregate OpenCode Go route migration did not report a change")
160 }
161 chat, ok := c.Provider("opencode-go")
162 if !ok || !chat.HasModel("glm-5.2") || !chat.HasModel("private-model") || chat.HasModel("grok-4.5") {
163 t.Fatalf("chat provider after split = %+v", chat)
164 }
165 if chat.BillingMode != "subscription_equivalent" || chat.Headers["X-User"] != "keep" {
166 t.Fatalf("chat provider lost preserved metadata = %+v", chat)
167 }
168 responses, ok := c.Provider("opencode-go-responses")
169 if !ok || responses.Kind != "responses" || responses.DefaultModel() != "grok-4.5" || !responses.HasModel("grok-4.5") || responses.HasModel("qwen3.7-plus") {
170 t.Fatalf("responses provider after split = %+v", responses)
171 }
172 if c.DefaultModel != "opencode-go-responses/grok-4.5" || c.Agent.PlannerModel != "opencode-go-responses/grok-4.5" || c.Agent.VisionModel != "opencode-go-responses/grok-4.5" || c.Agent.SubagentModel != "opencode-go/qwen3.7-plus" || c.Bot.Model != "opencode-go-responses/grok-4.5" {
173 t.Fatalf("historical model refs = default:%q planner:%q vision:%q subagent:%q bot:%q", c.DefaultModel, c.Agent.PlannerModel, c.Agent.VisionModel, c.Agent.SubagentModel, c.Bot.Model)
174 }
175 if len(c.Agent.SubagentModels) != 2 || c.Agent.SubagentModels["review"] != "opencode-go-responses/grok-4.5" || c.Agent.SubagentModels["local"] != "private-model" {
176 t.Fatalf("historical subagent refs = %v", c.Agent.SubagentModels)
177 }
178 access := desktopProviderAccessMap(c.Desktop.ProviderAccess)
179 if !access["opencode-go"] || !access["opencode-go-responses"] {
180 t.Fatalf("provider access after split = %v", c.Desktop.ProviderAccess)
181 }
182 if normalizeLegacyOpenCodeGoInstalls(c) {
183 t.Fatal("aggregate OpenCode Go route migration was not idempotent")
184 }
185 }
186
187 func TestNormalizeLegacyOpenCodeGoRouteCatalogRepairsWrongSingleRouteInPlace(t *testing.T) {
188 c := &Config{Providers: []ProviderEntry{{
189 Name: "opencode-go", Kind: "anthropic", BaseURL: "https://opencode.ai/zen/go",
190 Models: []string{"grok-4.5"}, Default: "grok-4.5", APIKeyEnv: "OPENCODE_GO_API_KEY",
191 }}}
192 if !normalizeLegacyOpenCodeGoRouteCatalog(c) {
193 t.Fatal("wrong single-route OpenCode Go config was not migrated")
194 }
195 entry := c.Providers[0]
196 if entry.Kind != "responses" || entry.BaseURL != "https://opencode.ai/zen/go/v1" || entry.DefaultModel() != "grok-4.5" || entry.PresetID != "opencode-go-responses" || entry.PresetVersion != ProviderPresetVersion || entry.BillingMode != "" {
197 t.Fatalf("wrong single-route migration = %+v", entry)
198 }
199 }
200
201 func TestNormalizeLegacyOpenCodeGoRouteCatalogLeavesCustomRequestURLUntouched(t *testing.T) {
202 original := ProviderEntry{
203 Name: "opencode-go", Kind: "anthropic", BaseURL: "https://opencode.ai/zen/go",
204 RequestURL: "https://proxy.example/messages", Models: []string{"grok-4.5"}, Default: "grok-4.5",
205 }
206 c := &Config{Providers: []ProviderEntry{original}}
207 if normalizeLegacyOpenCodeGoRouteCatalog(c) {
208 t.Fatal("custom request_url provider was migrated")
209 }
210 if got := c.Providers[0]; got.Kind != original.Kind || got.BaseURL != original.BaseURL || got.RequestURL != original.RequestURL || !stringSlicesEqual(got.Models, original.Models) {
211 t.Fatalf("custom request_url provider changed = %+v", got)
212 }
213 }
214
214 lines GO