| 1 | package config |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | |
| 7 | "github.com/BurntSushi/toml" |
| 8 | ) |
| 9 | |
| 10 | func TestDesktopReasoningDisplayModeDefaultsAndLegacy(t *testing.T) { |
| 11 | tests := []struct { |
| 12 | name string |
| 13 | mode string |
| 14 | expand bool |
| 15 | want string |
| 16 | explicit bool |
| 17 | }{ |
| 18 | {name: "missing defaults to live follow", want: "auto"}, |
| 19 | {name: "legacy expand false follows the new default", expand: false, want: "auto"}, |
| 20 | {name: "legacy expand true maps to auto", expand: true, want: "auto"}, |
| 21 | {name: "explicit hidden wins over legacy", mode: "hidden", expand: true, want: "hidden", explicit: true}, |
| 22 | {name: "explicit summary wins over legacy", mode: "summary", expand: true, want: "summary", explicit: true}, |
| 23 | {name: "explicit auto", mode: "auto", want: "auto", explicit: true}, |
| 24 | {name: "explicit expanded", mode: "expanded", expand: true, want: "expanded", explicit: true}, |
| 25 | {name: "unknown follows the live-follow fallback", mode: "future-mode", want: "auto"}, |
| 26 | {name: "unknown with legacy expand remains auto", mode: "future-mode", expand: true, want: "auto"}, |
| 27 | } |
| 28 | for _, tt := range tests { |
| 29 | t.Run(tt.name, func(t *testing.T) { |
| 30 | cfg := Default() |
| 31 | cfg.Desktop.ReasoningDisplayMode = tt.mode |
| 32 | cfg.Desktop.ExpandThinking = tt.expand |
| 33 | if got := cfg.DesktopReasoningDisplayMode(); got != tt.want { |
| 34 | t.Fatalf("DesktopReasoningDisplayMode() = %q, want %q", got, tt.want) |
| 35 | } |
| 36 | if got := cfg.DesktopReasoningDisplayModeExplicit(); got != tt.explicit { |
| 37 | t.Fatalf("DesktopReasoningDisplayModeExplicit() = %t, want %t", got, tt.explicit) |
| 38 | } |
| 39 | }) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | func TestSetDesktopReasoningDisplayMode(t *testing.T) { |
| 44 | tests := []struct { |
| 45 | mode string |
| 46 | want string |
| 47 | expand bool |
| 48 | }{ |
| 49 | {mode: "hidden", want: "hidden"}, |
| 50 | {mode: "summary", want: "summary"}, |
| 51 | {mode: "auto", want: "auto", expand: true}, |
| 52 | {mode: "expanded", want: "expanded", expand: true}, |
| 53 | } |
| 54 | for _, tt := range tests { |
| 55 | t.Run(tt.mode, func(t *testing.T) { |
| 56 | cfg := Default() |
| 57 | if err := cfg.SetDesktopReasoningDisplayMode(tt.mode); err != nil { |
| 58 | t.Fatalf("SetDesktopReasoningDisplayMode(%q): %v", tt.mode, err) |
| 59 | } |
| 60 | if got := cfg.DesktopReasoningDisplayMode(); got != tt.want { |
| 61 | t.Fatalf("mode = %q, want %q", got, tt.want) |
| 62 | } |
| 63 | if cfg.Desktop.ExpandThinking != tt.expand { |
| 64 | t.Fatalf("ExpandThinking = %t, want %t", cfg.Desktop.ExpandThinking, tt.expand) |
| 65 | } |
| 66 | }) |
| 67 | } |
| 68 | |
| 69 | cfg := Default() |
| 70 | for _, invalid := range []string{"invalid", ""} { |
| 71 | if err := cfg.SetDesktopReasoningDisplayMode(invalid); err == nil { |
| 72 | t.Fatalf("invalid reasoning display mode %q unexpectedly accepted", invalid) |
| 73 | } |
| 74 | } |
| 75 | if err := cfg.SetExpandThinking(true); err != nil { |
| 76 | t.Fatalf("SetExpandThinking(true): %v", err) |
| 77 | } |
| 78 | if got := cfg.DesktopReasoningDisplayMode(); got != "auto" { |
| 79 | t.Fatalf("legacy SetExpandThinking(true) mode = %q, want auto", got) |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | func TestRenderTOMLIncludesExplicitReasoningDisplayMode(t *testing.T) { |
| 84 | cfg := Default() |
| 85 | renderedDefault := RenderTOML(cfg) |
| 86 | if strings.Contains(renderedDefault, "reasoning_display_mode =") { |
| 87 | t.Fatal("implicit default unexpectedly rendered reasoning_display_mode") |
| 88 | } |
| 89 | var decodedDefault Config |
| 90 | if _, err := toml.Decode(renderedDefault, &decodedDefault); err != nil { |
| 91 | t.Fatalf("decode rendered default config: %v", err) |
| 92 | } |
| 93 | if got := decodedDefault.DesktopReasoningDisplayMode(); got != "auto" { |
| 94 | t.Fatalf("round-tripped implicit default mode = %q, want auto", got) |
| 95 | } |
| 96 | if err := cfg.SetDesktopReasoningDisplayMode("hidden"); err != nil { |
| 97 | t.Fatalf("SetDesktopReasoningDisplayMode: %v", err) |
| 98 | } |
| 99 | rendered := RenderTOML(cfg) |
| 100 | if !strings.Contains(rendered, `reasoning_display_mode = "hidden"`) { |
| 101 | t.Fatalf("rendered config missing explicit reasoning display mode:\n%s", rendered) |
| 102 | } |
| 103 | if !strings.Contains(rendered, "expand_thinking = false") { |
| 104 | t.Fatalf("rendered config missing legacy expand_thinking alias:\n%s", rendered) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | func TestRenderTOMLOmitsUnknownReasoningDisplayMode(t *testing.T) { |
| 109 | cfg := Default() |
| 110 | cfg.Desktop.ReasoningDisplayMode = "future-mode" |
| 111 | cfg.Desktop.ExpandThinking = true |
| 112 | rendered := RenderTOML(cfg) |
| 113 | if strings.Contains(rendered, "reasoning_display_mode =") { |
| 114 | t.Fatalf("unknown reasoning display mode was canonicalized into an explicit preference:\n%s", rendered) |
| 115 | } |
| 116 | if !strings.Contains(rendered, "expand_thinking = true") { |
| 117 | t.Fatalf("unknown mode did not preserve the legacy compatibility alias:\n%s", rendered) |
| 118 | } |
| 119 | var decoded Config |
| 120 | if _, err := toml.Decode(rendered, &decoded); err != nil { |
| 121 | t.Fatalf("decode rendered config: %v", err) |
| 122 | } |
| 123 | if decoded.DesktopReasoningDisplayModeExplicit() { |
| 124 | t.Fatalf("unknown mode became explicit after round trip: %+v", decoded.Desktop) |
| 125 | } |
| 126 | if got := decoded.DesktopReasoningDisplayMode(); got != "auto" { |
| 127 | t.Fatalf("round-tripped unknown mode = %q, want stable live-follow fallback", got) |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | func TestDesktopReasoningDisplayModeTOMLRoundTrip(t *testing.T) { |
| 132 | cfg := Default() |
| 133 | if err := cfg.SetDesktopReasoningDisplayMode("auto"); err != nil { |
| 134 | t.Fatalf("SetDesktopReasoningDisplayMode: %v", err) |
| 135 | } |
| 136 | var decoded Config |
| 137 | if _, err := toml.Decode(RenderTOML(cfg), &decoded); err != nil { |
| 138 | t.Fatalf("decode rendered config: %v", err) |
| 139 | } |
| 140 | if got := decoded.DesktopReasoningDisplayMode(); got != "auto" { |
| 141 | t.Fatalf("round-tripped mode = %q, want auto", got) |
| 142 | } |
| 143 | if !decoded.DesktopReasoningDisplayModeExplicit() || !decoded.Desktop.ExpandThinking { |
| 144 | t.Fatalf("round-tripped compatibility fields were not preserved: %+v", decoded.Desktop) |
| 145 | } |
| 146 | } |
| 147 |