cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package handlers
2
3import (
4 "bytes"
5 "io"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10
11 "github.com/stormlightlabs/noteleaf/internal/shared"
12 "github.com/stormlightlabs/noteleaf/internal/store"
13)
14
15func TestConfigHandler(t *testing.T) {
16 t.Run("Get", func(t *testing.T) {
17 tempDir, cleanup := shared.CreateTempDir("noteleaf-config-handler-get-test-*", t)
18 defer cleanup()
19
20 customConfigPath := filepath.Join(tempDir, "test-config.toml")
21 originalEnv := os.Getenv("NOTELEAF_CONFIG")
22 os.Setenv("NOTELEAF_CONFIG", customConfigPath)
23 defer os.Setenv("NOTELEAF_CONFIG", originalEnv)
24
25 config := store.DefaultConfig()
26 config.ColorScheme = "test-scheme"
27 config.Editor = "vim"
28 if err := store.SaveConfig(config); err != nil {
29 t.Fatalf("Failed to save config: %v", err)
30 }
31
32 t.Run("Get all config values", func(t *testing.T) {
33 handler, err := NewConfigHandler()
34 if err != nil {
35 t.Fatalf("Failed to create handler: %v", err)
36 }
37
38 oldStdout := os.Stdout
39 r, w, _ := os.Pipe()
40 os.Stdout = w
41
42 err = handler.Get("")
43 if err != nil {
44 t.Fatalf("Get failed: %v", err)
45 }
46
47 w.Close()
48 os.Stdout = oldStdout
49
50 var buf bytes.Buffer
51 io.Copy(&buf, r)
52 output := buf.String()
53
54 if !strings.Contains(output, "color_scheme") {
55 t.Error("Output should contain color_scheme")
56 }
57 if !strings.Contains(output, "test-scheme") {
58 t.Error("Output should contain test-scheme value")
59 }
60 })
61
62 t.Run("Get specific config value", func(t *testing.T) {
63 handler, err := NewConfigHandler()
64 if err != nil {
65 t.Fatalf("Failed to create handler: %v", err)
66 }
67
68 oldStdout := os.Stdout
69 r, w, _ := os.Pipe()
70 os.Stdout = w
71
72 err = handler.Get("editor")
73 if err != nil {
74 t.Fatalf("Get failed: %v", err)
75 }
76
77 w.Close()
78 os.Stdout = oldStdout
79
80 var buf bytes.Buffer
81 io.Copy(&buf, r)
82 output := buf.String()
83
84 if !strings.Contains(output, "editor = vim") {
85 t.Errorf("Output should contain 'editor = vim', got: %s", output)
86 }
87 })
88
89 t.Run("Get unknown config key", func(t *testing.T) {
90 handler, err := NewConfigHandler()
91 if err != nil {
92 t.Fatalf("Failed to create handler: %v", err)
93 }
94
95 err = handler.Get("nonexistent_key")
96 if err == nil {
97 t.Error("Get should fail for unknown key")
98 }
99 if !strings.Contains(err.Error(), "unknown config key") {
100 t.Errorf("Error should mention unknown config key, got: %v", err)
101 }
102 })
103 })
104
105 t.Run("Set", func(t *testing.T) {
106 tempDir, cleanup := shared.CreateTempDir("noteleaf-config-handler-set-test-*", t)
107 defer cleanup()
108
109 customConfigPath := filepath.Join(tempDir, "test-config.toml")
110 originalEnv := os.Getenv("NOTELEAF_CONFIG")
111 os.Setenv("NOTELEAF_CONFIG", customConfigPath)
112 defer os.Setenv("NOTELEAF_CONFIG", originalEnv)
113
114 t.Run("Set string config value", func(t *testing.T) {
115 handler, err := NewConfigHandler()
116 if err != nil {
117 t.Fatalf("Failed to create handler: %v", err)
118 }
119
120 oldStdout := os.Stdout
121 r, w, _ := os.Pipe()
122 os.Stdout = w
123
124 err = handler.Set("editor", "emacs")
125 if err != nil {
126 t.Fatalf("Set failed: %v", err)
127 }
128
129 w.Close()
130 os.Stdout = oldStdout
131
132 var buf bytes.Buffer
133 io.Copy(&buf, r)
134 output := buf.String()
135
136 if !strings.Contains(output, "Set editor = emacs") {
137 t.Errorf("Output should confirm setting, got: %s", output)
138 }
139
140 loadedConfig, err := store.LoadConfig()
141 if err != nil {
142 t.Fatalf("Failed to load config: %v", err)
143 }
144
145 if loadedConfig.Editor != "emacs" {
146 t.Errorf("Expected editor 'emacs', got '%s'", loadedConfig.Editor)
147 }
148 })
149
150 t.Run("Set boolean config value", func(t *testing.T) {
151 handler, err := NewConfigHandler()
152 if err != nil {
153 t.Fatalf("Failed to create handler: %v", err)
154 }
155
156 err = handler.Set("auto_archive", "true")
157 if err != nil {
158 t.Fatalf("Set failed: %v", err)
159 }
160
161 loadedConfig, err := store.LoadConfig()
162 if err != nil {
163 t.Fatalf("Failed to load config: %v", err)
164 }
165
166 if !loadedConfig.AutoArchive {
167 t.Error("Expected auto_archive to be true")
168 }
169 })
170
171 t.Run("Set boolean config value with various formats", func(t *testing.T) {
172 tc := []struct {
173 value string
174 expected bool
175 }{
176 {"true", true},
177 {"1", true},
178 {"yes", true},
179 {"false", false},
180 {"0", false},
181 {"no", false},
182 }
183
184 for _, tt := range tc {
185 handler, err := NewConfigHandler()
186 if err != nil {
187 t.Fatalf("Failed to create handler: %v", err)
188 }
189
190 err = handler.Set("sync_enabled", tt.value)
191 if err != nil {
192 t.Fatalf("Set failed for value '%s': %v", tt.value, err)
193 }
194
195 loadedConfig, err := store.LoadConfig()
196 if err != nil {
197 t.Fatalf("Failed to load config: %v", err)
198 }
199
200 if loadedConfig.SyncEnabled != tt.expected {
201 t.Errorf("For value '%s', expected sync_enabled %v, got %v", tt.value, tt.expected, loadedConfig.SyncEnabled)
202 }
203 }
204 })
205
206 t.Run("Set unknown config key", func(t *testing.T) {
207 handler, err := NewConfigHandler()
208 if err != nil {
209 t.Fatalf("Failed to create handler: %v", err)
210 }
211
212 err = handler.Set("nonexistent_key", "value")
213 if err == nil {
214 t.Error("Set should fail for unknown key")
215 }
216 if !strings.Contains(err.Error(), "unknown config key") {
217 t.Errorf("Error should mention unknown config key, got: %v", err)
218 }
219 })
220 })
221 t.Run("Path", func(t *testing.T) {
222 tempDir, cleanup := shared.CreateTempDir("noteleaf-config-handler-path-test-*", t)
223 defer cleanup()
224
225 customConfigPath := filepath.Join(tempDir, "my-config.toml")
226 originalEnv := os.Getenv("NOTELEAF_CONFIG")
227 os.Setenv("NOTELEAF_CONFIG", customConfigPath)
228 defer os.Setenv("NOTELEAF_CONFIG", originalEnv)
229
230 t.Run("Path returns correct config file path", func(t *testing.T) {
231 handler, err := NewConfigHandler()
232 if err != nil {
233 t.Fatalf("Failed to create handler: %v", err)
234 }
235
236 oldStdout := os.Stdout
237 r, w, _ := os.Pipe()
238 os.Stdout = w
239
240 err = handler.Path()
241 if err != nil {
242 t.Fatalf("Path failed: %v", err)
243 }
244
245 w.Close()
246 os.Stdout = oldStdout
247
248 var buf bytes.Buffer
249 io.Copy(&buf, r)
250 output := strings.TrimSpace(buf.String())
251
252 if output != customConfigPath {
253 t.Errorf("Expected path '%s', got '%s'", customConfigPath, output)
254 }
255 })
256 })
257
258 t.Run("Reset", func(t *testing.T) {
259 tempDir, cleanup := shared.CreateTempDir("noteleaf-config-handler-reset-test-*", t)
260 defer cleanup()
261
262 customConfigPath := filepath.Join(tempDir, "test-config.toml")
263 originalEnv := os.Getenv("NOTELEAF_CONFIG")
264 os.Setenv("NOTELEAF_CONFIG", customConfigPath)
265 defer os.Setenv("NOTELEAF_CONFIG", originalEnv)
266
267 t.Run("Reset restores default config", func(t *testing.T) {
268 config := store.DefaultConfig()
269 config.ColorScheme = "custom"
270 config.AutoArchive = true
271 config.Editor = "emacs"
272 if err := store.SaveConfig(config); err != nil {
273 t.Fatalf("Failed to save config: %v", err)
274 }
275
276 handler, err := NewConfigHandler()
277 if err != nil {
278 t.Fatalf("Failed to create handler: %v", err)
279 }
280
281 oldStdout := os.Stdout
282 r, w, _ := os.Pipe()
283 os.Stdout = w
284
285 err = handler.Reset()
286 if err != nil {
287 t.Fatalf("Reset failed: %v", err)
288 }
289
290 w.Close()
291 os.Stdout = oldStdout
292
293 var buf bytes.Buffer
294 io.Copy(&buf, r)
295 output := buf.String()
296
297 if !strings.Contains(output, "reset to defaults") {
298 t.Errorf("Output should confirm reset, got: %s", output)
299 }
300
301 loadedConfig, err := store.LoadConfig()
302 if err != nil {
303 t.Fatalf("Failed to load config: %v", err)
304 }
305
306 defaultConfig := store.DefaultConfig()
307 if loadedConfig.ColorScheme != defaultConfig.ColorScheme {
308 t.Errorf("ColorScheme should be reset to default '%s', got '%s'", defaultConfig.ColorScheme, loadedConfig.ColorScheme)
309 }
310 if loadedConfig.AutoArchive != defaultConfig.AutoArchive {
311 t.Errorf("AutoArchive should be reset to default %v, got %v", defaultConfig.AutoArchive, loadedConfig.AutoArchive)
312 }
313 if loadedConfig.Editor != defaultConfig.Editor {
314 t.Errorf("Editor should be reset to default '%s', got '%s'", defaultConfig.Editor, loadedConfig.Editor)
315 }
316 })
317 })
318}