Files
magpie/internal/profile/defaults_test.go
T

121 lines
4.1 KiB
Go

package profile
import (
"encoding/json"
"path/filepath"
"testing"
"github.com/yetone/magpie/internal/agent"
"github.com/yetone/magpie/internal/provider"
)
// Empty values mean the agent's default, including quiet fields that
// follow the main model. Saving and loading a snapshot must keep them.
func TestSnapshotRestoresDefaults(t *testing.T) {
for _, c := range []struct {
agent, field, value string
routed bool
}{
{"claude", "model", "opus", false},
{"claude", "effort", "high", false},
{"claude", "ultracode", "on", false},
{"claude", "haiku", "relay/flash", true},
{"codex", "model", "gpt-5.5", false},
{"codex", "effort", "high", false},
{"codex", "subagent", "gpt-5.5", false},
{"codex", "login", "api", false},
} {
t.Run(c.agent+"."+c.field, func(t *testing.T) {
sandbox(t)
a := must[*agent.Agent](t)(agent.Find(c.agent))
if c.routed {
if err := provider.Save(provider.Provider{ID: "relay", Name: "Relay", Chat: "https://relay.example.com/v1",
Key: "synthetic-key", Models: []string{"pro", "flash"}}); err != nil {
t.Fatal(err)
}
if err := a.Apply("model", "relay/pro"); err != nil {
t.Fatal(err)
}
}
f := a.Field(c.field)
if f == nil || f.Get() != "" {
t.Fatalf("%s is not at its default", c.field)
}
p := must[Profile](t)(Snapshot())
key := c.agent + "." + c.field
if v, ok := p.Fields[key]; !ok || v != "" {
t.Errorf("snapshot omitted default %s: %+v", key, p.Fields)
}
if _, ok := p.Fields["gemini.model"]; ok {
t.Error("snapshot included an undetected agent")
}
if err := Save("defaults", p); err != nil {
t.Fatal(err)
}
ps := must[map[string]Profile](t)(Load())
if v, ok := ps["defaults"].Fields[key]; !ok || v != "" {
t.Errorf("saved snapshot omitted default %s", key)
}
if err := a.Apply(c.field, c.value); err != nil {
t.Fatal(err)
}
if f.Get() == "" {
t.Fatalf("%s did not change", c.field)
}
got := must[Applied](t)(Apply(ps["defaults"]))
if f.Get() != "" || got.Changed == 0 {
t.Errorf("%s stayed %q instead of its default; applied %+v", key, f.Get(), got)
}
if again := must[Applied](t)(Apply(ps["defaults"])); again.Changed != 0 {
t.Errorf("applying defaults twice: %+v", again)
}
})
}
}
// Existing partial snapshots leave missing fields alone. An explicit
// empty value, already supported by Apply, resets only that field.
func TestPartialProfileKeepsMissingFields(t *testing.T) {
h := sandbox(t)
write(t, filepath.Join(h, ".claude/settings.json"), `{"model":"opus","theme":"dark"}`)
codex := filepath.Join(h, ".codex/config.toml")
original := "model = \"gpt-5.5\"\nmodel_reasoning_effort = \"high\"\n"
write(t, codex, original)
write(t, Path(), `{"legacy":{"claude.model":""}}`)
ps := must[map[string]Profile](t)(Load())
if len(ps["legacy"].Fields) != 1 {
t.Fatalf("legacy fields: %+v", ps["legacy"].Fields)
}
if got := must[Applied](t)(Apply(ps["legacy"])); got.Changed != 1 {
t.Fatalf("applied legacy profile: %+v", got)
}
a := must[*agent.Agent](t)(agent.Find("claude"))
if a.Field("model").Get() != "" || read(t, codex) != original {
t.Fatalf("an explicit reset changed missing fields: claude %q, codex %s", a.Field("model").Get(), read(t, codex))
}
var settings map[string]any
if err := json.Unmarshal([]byte(read(t, filepath.Join(h, ".claude/settings.json"))), &settings); err != nil {
t.Fatal(err)
}
if len(settings) != 1 || settings["theme"] != "dark" {
t.Fatalf("unrelated Claude settings changed: %+v", settings)
}
}
// Defaults stored in snapshots must not add blank model entries to the
// short summary; non-default models still appear in display order.
func TestSummaryDefaults(t *testing.T) {
for _, c := range []struct {
fields map[string]string
want string
}{
{map[string]string{"claude.model": "", "codex.model": "", "codex.effort": ""}, ""},
{map[string]string{"claude.model": "", "codex.model": "gpt-5.5"}, "codex gpt-5.5"},
{map[string]string{"claude.model": "opus", "codex.model": "gpt-5.5"}, "claude opus · codex gpt-5.5"},
} {
if got := Summary(Profile{Fields: c.fields}); got != c.want {
t.Errorf("summary %q, want %q", got, c.want)
}
}
}