| 1 | package config |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestModelRuntimeContinuationRevocation(t *testing.T) { |
| 6 | for _, tc := range []struct { |
| 7 | name string |
| 8 | edit func(*Config) |
| 9 | allowed bool |
| 10 | }{ |
| 11 | {"address", func(c *Config) { c.Providers[0].BaseURL = "https://new.invalid" }, true}, |
| 12 | {"provider removed", func(c *Config) { c.Providers = nil }, false}, |
| 13 | {"access removed", func(c *Config) { c.Desktop.ProviderAccess = []string{} }, false}, |
| 14 | {"model removed", func(c *Config) { c.Providers[0].Model = "other" }, false}, |
| 15 | {"credential rotated", func(c *Config) { c.Providers[0].APIKeyEnv = "MODEL_CONTINUATION_NEW" }, false}, |
| 16 | {"credential cleared", func(c *Config) { c.Providers[0].APIKeyEnv = "MODEL_CONTINUATION_EMPTY" }, false}, |
| 17 | {"credential header", func(c *Config) { c.Providers[0].Headers = map[string]string{"Authorization": "new"} }, false}, |
| 18 | {"credential URL", func(c *Config) { c.Providers[0].BaseURL = "https://user:secret@new.invalid" }, false}, |
| 19 | {"limit tightened", func(c *Config) { c.Agent.MaxSubagentDepth = 1 }, false}, |
| 20 | {"task budget tightened", func(c *Config) { c.Agent.TaskCostBudget = 1 }, false}, |
| 21 | {"model output capped", func(c *Config) { c.Providers[0].MaxOutputTokens = 100 }, false}, |
| 22 | {"request URL credential", func(c *Config) { c.Providers[0].RequestURL = "https://new.invalid?token=secret" }, false}, |
| 23 | {"legacy URL credential", func(c *Config) { c.Providers[0].ChatURL = "https://user:secret@new.invalid" }, false}, |
| 24 | {"auth mode changed", func(c *Config) { c.Providers[0].AuthHeader = true }, false}, |
| 25 | } { |
| 26 | t.Run(tc.name, func(t *testing.T) { |
| 27 | t.Setenv("MODEL_CONTINUATION_OLD", "old-key") |
| 28 | t.Setenv("MODEL_CONTINUATION_NEW", "new-key") |
| 29 | t.Setenv("MODEL_CONTINUATION_EMPTY", "") |
| 30 | fixture := func() *Config { |
| 31 | c := &Config{Providers: []ProviderEntry{{Name: "p", Kind: "openai", BaseURL: "https://old.invalid", Model: "m", APIKeyEnv: "MODEL_CONTINUATION_OLD"}}} |
| 32 | c.Agent.MaxSubagentDepth = 4 |
| 33 | return c |
| 34 | } |
| 35 | old, current := fixture(), fixture() |
| 36 | tc.edit(current) |
| 37 | old.Providers[0].resolvedAPIKey = "old-key" |
| 38 | if len(current.Providers) > 0 { |
| 39 | switch current.Providers[0].APIKeyEnv { |
| 40 | case "MODEL_CONTINUATION_OLD": |
| 41 | current.Providers[0].resolvedAPIKey = "old-key" |
| 42 | case "MODEL_CONTINUATION_NEW": |
| 43 | current.Providers[0].resolvedAPIKey = "new-key" |
| 44 | } |
| 45 | current.Providers[0].credentialsFrozen = true |
| 46 | } |
| 47 | if err := ValidateModelRuntimeContinuation(old, current); (err == nil) != tc.allowed { |
| 48 | t.Fatalf("allowed=%v err=%v", tc.allowed, err) |
| 49 | } |
| 50 | }) |
| 51 | } |
| 52 | } |
| 53 |