···88 "github.com/stormlightlabs/noteleaf/internal/store"
99)
10101111-// ConfigHandler handles configuration-related operations
1111+// ConfigHandler handles [store.Config]-related operations
1212type ConfigHandler struct {
1313 config *store.Config
1414}
15151616-// NewConfigHandler creates a new ConfigHandler
1616+// NewConfigHandler creates a new [ConfigHandler]
1717func NewConfigHandler() (*ConfigHandler, error) {
1818 config, err := store.LoadConfig()
1919 if err != nil {
2020 return nil, fmt.Errorf("failed to load config: %w", err)
2121 }
22222323- return &ConfigHandler{
2424- config: config,
2525- }, nil
2323+ return &ConfigHandler{config: config}, nil
2624}
27252826// Get displays one or all configuration values
2927func (h *ConfigHandler) Get(key string) error {
3028 if key == "" {
3131- // Display all configuration values
3229 return h.displayAll()
3330 }
34313535- // Display specific key
3632 value, err := h.getConfigValue(key)
3733 if err != nil {
3834 return err
···8783 field := t.Field(i)
8884 value := v.Field(i)
89859090- // Get the TOML tag name
9186 tomlTag := field.Tag.Get("toml")
9287 if tomlTag == "" {
9388 continue
9489 }
95909696- // Remove ",omitempty" suffix if present
9791 tagName := strings.Split(tomlTag, ",")[0]
98929999- // Format the output based on field type
10093 switch value.Kind() {
10194 case reflect.String:
10295 if value.String() != "" {
···118111 v := reflect.ValueOf(*h.config)
119112 t := reflect.TypeOf(*h.config)
120113121121- // Find the field with matching TOML tag
122114 for i := 0; i < v.NumField(); i++ {
123115 field := t.Field(i)
124116 tomlTag := field.Tag.Get("toml")
···139131 v := reflect.ValueOf(h.config).Elem()
140132 t := reflect.TypeOf(*h.config)
141133142142- // Find the field with matching TOML tag
143134 for i := 0; i < v.NumField(); i++ {
144135 field := t.Field(i)
145136 tomlTag := field.Tag.Get("toml")
···151142 if tagName == key {
152143 fieldValue := v.Field(i)
153144154154- // Set the value based on field type
155145 switch fieldValue.Kind() {
156146 case reflect.String:
157147 fieldValue.SetString(value)
+8-9
internal/handlers/handlers.go
···33import (
44 "context"
55 "fmt"
66+ "io"
67 "os"
78 "path/filepath"
89···130131}
131132132133// Status shows the current application status
133133-func Status(ctx context.Context, args []string) error {
134134+func Status(ctx context.Context, args []string, w io.Writer) error {
134135 configDir, err := store.GetConfigDir()
135136 if err != nil {
136137 return fmt.Errorf("failed to get config directory: %w", err)
···138139139140 fmt.Printf("Config directory: %s\n", configDir)
140141141141- // Load config to determine the actual database path
142142 config, err := store.LoadConfig()
143143 if err != nil {
144144 return fmt.Errorf("failed to load configuration: %w", err)
145145 }
146146147147- // Determine database path using the same logic as NewDatabase
148147 var dbPath string
149148 if config.DatabasePath != "" {
150149 dbPath = config.DatabasePath
···159158 }
160159161160 if _, err := os.Stat(dbPath); err != nil {
162162- fmt.Println("Database: Not found")
163163- fmt.Println("Run 'noteleaf setup' to initialize.")
161161+ fmt.Fprintln(w, "Database: Not found")
162162+ fmt.Fprintln(w, "Run 'noteleaf setup' to initialize.")
164163 return nil
165164 }
166165167167- fmt.Printf("Database: %s\n", dbPath)
166166+ fmt.Fprintf(w, "Database: %s\n", dbPath)
168167169168 configPath, err := store.GetConfigPath()
170169 if err != nil {
···172171 }
173172174173 if _, err := os.Stat(configPath); err != nil {
175175- fmt.Println("Configuration: Not found")
174174+ fmt.Fprintln(w, "Configuration: Not found")
176175 } else {
177177- fmt.Printf("Configuration: %s\n", configPath)
176176+ fmt.Fprintf(w, "Configuration: %s\n", configPath)
178177 }
179178180179 db, err := store.NewDatabase()
···194193 return fmt.Errorf("failed to get available migrations: %w", err)
195194 }
196195197197- fmt.Printf("Migrations: %d/%d applied\n", len(applied), len(available))
196196+ fmt.Fprintf(w, "Migrations: %d/%d applied\n", len(applied), len(available))
198197199198 return nil
200199}
+25-16
internal/handlers/handlers_test.go
···11package handlers
2233import (
44+ "bytes"
45 "context"
56 "os"
67 "path/filepath"
···197198 t.Run("reports status when setup", func(t *testing.T) {
198199 _ = createTestDir(t)
199200 ctx := context.Background()
201201+ var buf bytes.Buffer
200202201203 err := Setup(ctx, []string{})
202204 if err != nil {
203205 t.Fatalf("Setup failed: %v", err)
204206 }
205207206206- err = Status(ctx, []string{})
208208+ err = Status(ctx, []string{}, &buf)
207209 if err != nil {
208210 t.Errorf("Status failed: %v", err)
209211 }
···213215 t.Run("reports status when not setup", func(t *testing.T) {
214216 _ = createTestDir(t)
215217 ctx := context.Background()
218218+ var buf bytes.Buffer
216219217217- err := Status(ctx, []string{})
220220+ err := Status(ctx, []string{}, &buf)
218221 if err != nil {
219222 t.Errorf("Status should not fail when not setup: %v", err)
220223 }
···224227 t.Run("shows migration information", func(t *testing.T) {
225228 _ = createTestDir(t)
226229 ctx := context.Background()
230230+ var buf bytes.Buffer
227231228232 err := Setup(ctx, []string{})
229233 if err != nil {
230234 t.Fatalf("Setup failed: %v", err)
231235 }
232236233233- err = Status(ctx, []string{})
237237+ err = Status(ctx, []string{}, &buf)
234238 if err != nil {
235239 t.Errorf("Status failed: %v", err)
236240 }
···245249 originalNoteleafConfig := os.Getenv("NOTELEAF_CONFIG")
246250 originalNoteleafDataDir := os.Getenv("NOTELEAF_DATA_DIR")
247251248248- // Unset all environment variables so GetConfigDir fails
249252 os.Unsetenv("NOTELEAF_CONFIG")
250253 os.Unsetenv("NOTELEAF_DATA_DIR")
251254···431434 os.Setenv("HOME", originalHome)
432435 }()
433436437437+ var buf bytes.Buffer
434438 ctx := context.Background()
435435- err := Status(ctx, []string{})
439439+ err := Status(ctx, []string{}, &buf)
436440437441 if err == nil {
438442 t.Error("Status should fail when GetConfigDir fails")
···467471 dbPath = filepath.Join(dataDir, "noteleaf.db")
468472 }
469473470470- // Remove the database file
471474 os.Remove(dbPath)
472475473473- // Create a directory with the same name as the database file
474474- // This will cause database connection to fail
475476 if err := os.MkdirAll(dbPath, 0755); err != nil {
476477 t.Fatalf("Failed to create directory: %v", err)
477478 }
478479479479- err = Status(ctx, []string{})
480480+ var buf bytes.Buffer
481481+ err = Status(ctx, []string{}, &buf)
480482 if err == nil {
481483 t.Error("Status should fail when database connection fails")
482484 } else if !strings.Contains(err.Error(), "failed to connect to database") && !strings.Contains(err.Error(), "failed to open database") && !strings.Contains(err.Error(), "failed to load config") {
···510512 }
511513 db.Close()
512514513513- err = Status(ctx, []string{})
515515+ var buf bytes.Buffer
516516+ err = Status(ctx, []string{}, &buf)
514517 if err == nil {
515518 t.Error("Status should fail when migration queries fail")
516519 }
···590593 }
591594 }
592595 },
593593- handlerFunc: Status,
596596+ handlerFunc: func(ctx context.Context, args []string) error {
597597+ var buf bytes.Buffer
598598+ return Status(ctx, args, &buf)
599599+ },
594600 expectError: true,
595601 errorSubstr: "config directory",
596602 },
···620626 _ = createTestDir(t)
621627 ctx := context.Background()
622628623623- err := Status(ctx, []string{})
629629+ var buf bytes.Buffer
630630+ err := Status(ctx, []string{}, &buf)
624631 if err != nil {
625632 t.Errorf("Initial status failed: %v", err)
626633 }
···630637 t.Errorf("Setup failed: %v", err)
631638 }
632639633633- err = Status(ctx, []string{})
640640+ buf.Reset()
641641+ err = Status(ctx, []string{}, &buf)
634642 if err != nil {
635643 t.Errorf("Status after setup failed: %v", err)
636644 }
637645638638- // Determine database path using the same logic as Setup
639646 config, _ := store.LoadConfig()
640647 var dbPath string
641648 if config.DatabasePath != "" {
···660667 t.Error("Database should not exist after reset")
661668 }
662669663663- err = Status(ctx, []string{})
670670+ buf.Reset()
671671+ err = Status(ctx, []string{}, &buf)
664672 if err != nil {
665673 t.Errorf("Status after reset failed: %v", err)
666674 }
···678686679687 done := make(chan error, 3)
680688689689+ var buf bytes.Buffer
681690 for range 3 {
682691 go func() {
683692 time.Sleep(time.Millisecond * 10)
684684- done <- Status(ctx, []string{})
693693+ done <- Status(ctx, []string{}, &buf)
685694 }()
686695 }
687696