package config import ( "os" "path/filepath" "strings" "testing" ) func withHome(t *testing.T) string { t.Helper() home := t.TempDir() t.Setenv("HOME", home) return home } func readConfig(t *testing.T, home string) string { t.Helper() data, err := os.ReadFile(filepath.Join(home, ".config", "cliamp", "config.toml")) if err != nil { t.Fatalf("ReadFile: %v", err) } return string(data) } func TestSaveCreatesConfigFile(t *testing.T) { home := withHome(t) if err := Save("volume", "-6"); err != nil { t.Fatalf("Save: %v", err) } got := readConfig(t, home) if !strings.Contains(got, "volume = -6") { t.Errorf("config = %q, want 'volume = -6' line", got) } } func TestSaveReplacesExistingKey(t *testing.T) { home := withHome(t) dir := filepath.Join(home, ".config", "cliamp") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } initial := "# a comment\nvolume = -12\nspeed = 1.0\n" if err := os.WriteFile(filepath.Join(dir, "config.toml"), []byte(initial), 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } if err := Save("volume", "-3"); err != nil { t.Fatalf("Save: %v", err) } got := readConfig(t, home) if !strings.Contains(got, "volume = -3") { t.Errorf("config = %q, want volume = -3", got) } if strings.Contains(got, "volume = -12") { t.Errorf("config = %q, should have replaced old volume line", got) } if !strings.Contains(got, "speed = 1.0") { t.Errorf("config = %q, unrelated keys must be preserved", got) } } func TestSaveInsertsBeforeFirstSection(t *testing.T) { home := withHome(t) dir := filepath.Join(home, ".config", "cliamp") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } initial := "[navidrome]\nurl = \"https://ex.com\"\n" if err := os.WriteFile(filepath.Join(dir, "config.toml"), []byte(initial), 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } if err := Save("volume", "-6"); err != nil { t.Fatalf("Save: %v", err) } got := readConfig(t, home) // volume should appear before [navidrome] volIdx := strings.Index(got, "volume = -6") navIdx := strings.Index(got, "[navidrome]") if volIdx < 0 { t.Fatalf("volume line missing: %q", got) } if navIdx < 0 || volIdx > navIdx { t.Errorf("volume should appear before [navidrome], got:\n%s", got) } } func TestSaveDoesNotMatchKeyInSection(t *testing.T) { home := withHome(t) dir := filepath.Join(home, ".config", "cliamp") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } // A [navidrome] section with a 'volume' key (shouldn't be touched by // the top-level Save). initial := "[navidrome]\nvolume = \"old\"\n" if err := os.WriteFile(filepath.Join(dir, "config.toml"), []byte(initial), 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } if err := Save("volume", "-6"); err != nil { t.Fatalf("Save: %v", err) } got := readConfig(t, home) // The navidrome.volume line stays, and a new top-level volume is added. if !strings.Contains(got, `volume = "old"`) { t.Errorf("section-local volume should be untouched:\n%s", got) } if !strings.Contains(got, "volume = -6") { t.Errorf("top-level volume should be added:\n%s", got) } } func TestSaveNavidromeSortCreatesSection(t *testing.T) { home := withHome(t) if err := SaveNavidromeSort("alphabeticalByName"); err != nil { t.Fatalf("SaveNavidromeSort: %v", err) } got := readConfig(t, home) if !strings.Contains(got, "[navidrome]") { t.Errorf("config should contain [navidrome] section:\n%s", got) } if !strings.Contains(got, `browse_sort = "alphabeticalByName"`) { t.Errorf("config should contain browse_sort key:\n%s", got) } } func TestSaveNavidromeSortReplacesExisting(t *testing.T) { home := withHome(t) dir := filepath.Join(home, ".config", "cliamp") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } initial := "[navidrome]\nurl = \"https://e.com\"\nbrowse_sort = \"old\"\n" if err := os.WriteFile(filepath.Join(dir, "config.toml"), []byte(initial), 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } if err := SaveNavidromeSort("byYear"); err != nil { t.Fatalf("SaveNavidromeSort: %v", err) } got := readConfig(t, home) if strings.Contains(got, "\"old\"") { t.Errorf("old browse_sort should be replaced:\n%s", got) } if !strings.Contains(got, `browse_sort = "byYear"`) { t.Errorf("new browse_sort missing:\n%s", got) } } func TestSaveNavidromeSortAppendsKeyInExistingSection(t *testing.T) { home := withHome(t) dir := filepath.Join(home, ".config", "cliamp") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } initial := "[navidrome]\nurl = \"https://e.com\"\n[other]\nkey = \"val\"\n" if err := os.WriteFile(filepath.Join(dir, "config.toml"), []byte(initial), 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } if err := SaveNavidromeSort("random"); err != nil { t.Fatalf("SaveNavidromeSort: %v", err) } got := readConfig(t, home) if !strings.Contains(got, `browse_sort = "random"`) { t.Errorf("browse_sort line missing:\n%s", got) } // browse_sort should be within [navidrome] block, before [other]. navIdx := strings.Index(got, "[navidrome]") sortIdx := strings.Index(got, "browse_sort") otherIdx := strings.Index(got, "[other]") if navIdx < 0 || sortIdx < navIdx || sortIdx > otherIdx { t.Errorf("browse_sort should be inside [navidrome] block:\n%s", got) } } func TestSaveFuncDelegates(t *testing.T) { home := withHome(t) var f SaveFunc if err := f.Save("test_key", "123"); err != nil { t.Fatalf("SaveFunc.Save: %v", err) } got := readConfig(t, home) if !strings.Contains(got, "test_key = 123") { t.Errorf("config should contain 'test_key = 123':\n%s", got) } }