fix(quick-13): resolve lint issues in main source files

- Fix errcheck for db.Close() in testhelper.go
- Fix errcheck, nlreturn, wsl, gofumpt issues in genevents/main.go
- Fix gofumpt and wsl issues in library.go
This commit is contained in:
2026-03-05 14:07:50 -05:00
parent 97f256d67f
commit e1a95e65a9
12 changed files with 177 additions and 61 deletions
+16 -7
View File
@@ -72,7 +72,10 @@ func TestConfig_LoadSave_Roundtrip(t *testing.T) {
}
if loaded.Theme.BackgroundShade != theme.BackgroundLight {
t.Errorf("Theme.BackgroundShade = %q, want %q", loaded.Theme.BackgroundShade, theme.BackgroundLight)
t.Errorf(
"Theme.BackgroundShade = %q, want %q",
loaded.Theme.BackgroundShade, theme.BackgroundLight,
)
}
// Verify tracklist.
@@ -86,13 +89,19 @@ func TestConfig_LoadSave_Roundtrip(t *testing.T) {
}
for i, want := range wantColumns {
if loaded.TrackList.Columns[i].ID != want {
t.Errorf("TrackList.Columns[%d].ID = %q, want %q", i, loaded.TrackList.Columns[i].ID, want)
t.Errorf(
"TrackList.Columns[%d].ID = %q, want %q",
i, loaded.TrackList.Columns[i].ID, want,
)
}
}
// Verify favorites.
if loaded.Favorites.IconStyle != favorites.IconStar {
t.Errorf("Favorites.IconStyle = %q, want %q", loaded.Favorites.IconStyle, favorites.IconStar)
t.Errorf(
"Favorites.IconStyle = %q, want %q",
loaded.Favorites.IconStyle, favorites.IconStar,
)
}
if loaded.Favorites.PinDefault != false {
@@ -105,7 +114,10 @@ func TestConfig_LoadSave_Roundtrip(t *testing.T) {
}
if loaded.Library.ScanConcurrency != library.ScanConcurrencySSD {
t.Errorf("Library.ScanConcurrency = %q, want %q", loaded.Library.ScanConcurrency, library.ScanConcurrencySSD)
t.Errorf(
"Library.ScanConcurrency = %q, want %q",
loaded.Library.ScanConcurrency, library.ScanConcurrencySSD,
)
}
// Verify window.
@@ -206,9 +218,6 @@ func TestConfig_ApplyDefaults_NilSubConfigs(t *testing.T) {
if c.Favorites == nil {
t.Error("Favorites should not be nil after applyDefaults")
}
// Library is intentionally left nil by applyDefaults when it
// starts as nil (no library dir configured yet).
}
// containsSubstring is a test helper for checking error messages.
+62 -23
View File
@@ -44,28 +44,50 @@ func seedSearchData(t *testing.T, db *DB) {
intPtr := func(v int64) *int64 { return &v }
tracks := []track{
{1, "/music/queen/bohemian_rhapsody.mp3", "Bohemian Rhapsody", "Queen", "A Night at the Opera", intPtr(11), intPtr(1), 1975, "Rock", "Freddie Mercury", 354000, 0, 44100, 16, 2, 320000, 8500000},
{2, "/music/beyonce/halo.flac", "Halo", "Beyoncé", "Lemonade", intPtr(1), intPtr(1), 2008, "Pop", "Ryan Tedder", 261000, 1, 96000, 24, 2, 1411000, 42000000},
{3, "/music/acdc/back_in_black.mp3", "Back in Black", "AC/DC", "Back in Black", intPtr(1), intPtr(1), 1980, "Hard Rock", "Angus Young", 255000, 0, 44100, 16, 2, 320000, 6100000},
{4, "/music/pinkfloyd/comfortably_numb.flac", "Comfortably Numb", "Pink Floyd", "The Dark Side of the Moon", intPtr(6), intPtr(1), 1979, "Progressive Rock", "David Gilmour", 382000, 1, 96000, 24, 2, 1411000, 54000000},
{5, "/music/queen/another_one_bites_the_dust.mp3", "Another One Bites the Dust", "Queen", "The Game", intPtr(3), intPtr(1), 1980, "Funk Rock", "John Deacon", 215000, 0, 44100, 16, 2, 320000, 5200000},
{6, "/music/acdc/thunderstruck.mp3", "Thunderstruck", "AC/DC", "The Razors Edge", intPtr(1), intPtr(1), 1990, "Hard Rock", "Angus Young", 292000, 0, 44100, 16, 2, 320000, 7000000},
{7, "/music/qotsa/queen_of_the_stone_age.mp3", "Queen of the Stone Age", "Queens of the Stone Age", "Rated R", intPtr(1), intPtr(1), 2000, "Stoner Rock", "Josh Homme", 310000, 0, 44100, 16, 2, 320000, 7400000},
{
1, "/music/queen/bohemian_rhapsody.mp3", "Bohemian Rhapsody", "Queen",
"A Night at the Opera", intPtr(11), intPtr(1), 1975, "Rock",
"Freddie Mercury", 354000, 0, 44100, 16, 2, 320000, 8500000,
},
{
2, "/music/beyonce/halo.flac", "Halo", "Beyoncé", "Lemonade",
intPtr(1), intPtr(1), 2008, "Pop", "Ryan Tedder", 261000, 1,
96000, 24, 2, 1411000, 42000000,
},
{
3, "/music/acdc/back_in_black.mp3", "Back in Black", "AC/DC",
"Back in Black", intPtr(1), intPtr(1), 1980, "Hard Rock",
"Angus Young", 255000, 0, 44100, 16, 2, 320000, 6100000,
},
{
4, "/music/pinkfloyd/comfortably_numb.flac", "Comfortably Numb",
"Pink Floyd", "The Dark Side of the Moon", intPtr(6), intPtr(1),
1979, "Progressive Rock", "David Gilmour", 382000, 1, 96000, 24,
2, 1411000, 54000000,
},
{
5, "/music/queen/another_one_bites_the_dust.mp3",
"Another One Bites the Dust", "Queen", "The Game", intPtr(3),
intPtr(1), 1980, "Funk Rock", "John Deacon", 215000, 0, 44100,
16, 2, 320000, 5200000,
},
{
6, "/music/acdc/thunderstruck.mp3", "Thunderstruck", "AC/DC",
"The Razors Edge", intPtr(1), intPtr(1), 1990, "Hard Rock",
"Angus Young", 292000, 0, 44100, 16, 2, 320000, 7000000,
},
{
7, "/music/qotsa/queen_of_the_stone_age.mp3",
"Queen of the Stone Age", "Queens of the Stone Age", "Rated R",
intPtr(1), intPtr(1), 2000, "Stoner Rock", "Josh Homme", 310000,
0, 44100, 16, 2, 320000, 7400000,
},
}
// Build unique sets.
type artistEntry struct {
id int64
text string
}
type albumEntry struct {
id int64
name string
}
artistMap := map[string]int64{}
albumMap := map[string]int64{}
var artistID, albumID int64
for _, tr := range tracks {
@@ -104,6 +126,7 @@ func seedSearchData(t *testing.T, db *DB) {
// Insert genres + recording_genres.
genreMap := map[string]int64{}
var genreID int64
for _, tr := range tracks {
@@ -131,8 +154,11 @@ func seedSearchData(t *testing.T, db *DB) {
// Insert recording.
_, err := db.ExecContext(
"INSERT INTO recordings (id, name, artist_credit_id, track_number, disc_number, year, genre, composer) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
tr.id, tr.title, acID, tr.trackNum, tr.discNum, tr.year, tr.genre, tr.composer,
"INSERT INTO recordings (id, name, artist_credit_id, "+
"track_number, disc_number, year, genre, composer) "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
tr.id, tr.title, acID, tr.trackNum, tr.discNum,
tr.year, tr.genre, tr.composer,
)
if err != nil {
t.Fatalf("insert recording %d %q: %v", tr.id, tr.title, err)
@@ -140,8 +166,12 @@ func seedSearchData(t *testing.T, db *DB) {
// Insert audio_files.
_, err = db.ExecContext(
"INSERT INTO audio_files (id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
tr.id, tr.filePath, tr.lenMs, tr.ftID, tr.id, tr.sr, tr.bd, tr.ch, tr.br, tr.fsize,
"INSERT INTO audio_files (id, file_path, "+
"length_milliseconds, file_type_id, recording_id, "+
"sample_rate, bit_depth, channels, bitrate, file_size) "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
tr.id, tr.filePath, tr.lenMs, tr.ftID, tr.id,
tr.sr, tr.bd, tr.ch, tr.br, tr.fsize,
)
if err != nil {
t.Fatalf("insert audio_file %d: %v", tr.id, err)
@@ -149,7 +179,9 @@ func seedSearchData(t *testing.T, db *DB) {
// Link recording to release_group.
_, err = db.ExecContext(
"INSERT INTO release_group_recordings (release_group_id, recording_id, track_number, disc_number) VALUES (?, ?, ?, ?)",
"INSERT INTO release_group_recordings "+
"(release_group_id, recording_id, track_number, disc_number) "+
"VALUES (?, ?, ?, ?)",
rgID, tr.id, tr.trackNum, tr.discNum,
)
if err != nil {
@@ -368,6 +400,7 @@ func TestSearchFTS_SpecialCharacters(t *testing.T) {
// Verify at least one AC/DC track is present.
found := false
for _, r := range results {
if r.Artist == "AC/DC" {
found = true
@@ -432,6 +465,7 @@ func TestSearchFTS_Diacritics(t *testing.T) {
}
found := false
for _, r := range results {
if r.Artist == "Beyoncé" {
found = true
@@ -489,6 +523,7 @@ func TestSearchFTSByFilename(t *testing.T) {
}
found := false
for _, r := range results {
if r.Title == "Bohemian Rhapsody" {
found = true
@@ -620,7 +655,9 @@ func TestInsertAndDeleteSearchIndex(t *testing.T) {
}
// Insert into search index.
if err := db.InsertSearchIndex(1, "/test/track.mp3", "Test Track", "Test Artist", "Test Album"); err != nil {
if err := db.InsertSearchIndex(
1, "/test/track.mp3", "Test Track", "Test Artist", "Test Album",
); err != nil {
t.Fatalf("InsertSearchIndex: %v", err)
}
@@ -781,11 +818,13 @@ func TestMigrationsApplied(t *testing.T) {
if !rows.Next() {
_ = rows.Close()
t.Fatal("PRAGMA user_version: no row returned")
}
if err := rows.Scan(&version); err != nil {
_ = rows.Close()
t.Fatalf("scan user_version: %v", err)
}
+1 -1
View File
@@ -63,7 +63,7 @@ func NewTestDB(t *testing.T) *DB {
queries := sqlcgen.New(db)
t.Cleanup(func() { db.Close() })
t.Cleanup(func() { _ = db.Close() })
return &DB{
db: db,
+16 -3
View File
@@ -32,6 +32,7 @@ func main() {
if *output == "" || *output == "/dev/stdout" {
fmt.Print(ts)
return
}
@@ -57,6 +58,7 @@ type constEntry struct {
// groups in declaration order.
func parseEvents(path string) ([]constGroup, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("parse %s: %w", path, err)
@@ -82,11 +84,14 @@ func parseEvents(path string) ([]constGroup, error) {
if !ok {
continue
}
for i, name := range vs.Names {
if i >= len(vs.Values) {
continue
}
bl, ok := vs.Values[i].(*ast.BasicLit)
if !ok || bl.Kind != token.STRING {
continue
}
@@ -110,6 +115,7 @@ func parseEvents(path string) ([]constGroup, error) {
func cleanComment(s string) string {
s = strings.TrimSpace(s)
s = strings.TrimSuffix(s, ".")
return s
}
@@ -126,6 +132,7 @@ func generateTypeScript(groups []constGroup) string {
if g.Comment != "" {
b.WriteString(" // " + g.Comment + "\n")
}
for _, c := range g.Consts {
b.WriteString(fmt.Sprintf(" %s: %q,\n", c.Name, c.Value))
}
@@ -146,20 +153,26 @@ func generateTypeScript(groups []constGroup) string {
// then renames it into place for atomic replacement.
func writeAtomic(path, data string) error {
dir := filepath.Dir(path)
tmp, err := os.CreateTemp(dir, ".genevents-*.tmp")
if err != nil {
return err
}
tmpName := tmp.Name()
if _, err := tmp.WriteString(data); err != nil {
tmp.Close()
os.Remove(tmpName)
_ = tmp.Close()
_ = os.Remove(tmpName)
return err
}
if err := tmp.Close(); err != nil {
os.Remove(tmpName)
_ = os.Remove(tmpName)
return err
}
return os.Rename(tmpName, path)
}
+1
View File
@@ -31,6 +31,7 @@ func TestFavoritesConfig_Validate_InvalidIconStyle(t *testing.T) {
t.Parallel()
c := &Config{IconStyle: "diamond"}
err := c.Validate()
if err == nil {
t.Fatal("Validate() expected error for unknown icon style, got nil")
+13 -8
View File
@@ -229,6 +229,7 @@ func (l *Library) Scan() (*ScanMetrics, error) {
"count", len(existingFiles),
"library-directory", l.conf.DirectoryPath,
)
workChan := make(chan scanWork, 100)
resultChan := make(chan importResult, 100)
@@ -531,10 +532,14 @@ func (l *Library) Scan() (*ScanMetrics, error) {
// point so it is safe to close.
thumbStart := time.Now()
runtime.EventsEmit(l.ctx, events.LibraryScanProgress,
ScanProgress{Phase: "thumbnails", Total: totalFiles,
Processed: a + s + u, Added: a, Skipped: s, Updated: u},
)
runtime.EventsEmit(l.ctx, events.LibraryScanProgress, ScanProgress{
Phase: "thumbnails",
Total: totalFiles,
Processed: a + s + u,
Added: a,
Skipped: s,
Updated: u,
})
close(thumbChan)
thumbWg.Wait()
@@ -542,10 +547,10 @@ func (l *Library) Scan() (*ScanMetrics, error) {
metrics.ThumbnailWallClock = time.Since(thumbStart)
// --- Phase 5: orphan cleanup ---
runtime.EventsEmit(l.ctx, events.LibraryScanProgress,
ScanProgress{Phase: "orphans", Total: totalFiles,
Processed: a + s + u, Added: a, Skipped: s, Updated: u},
)
runtime.EventsEmit(l.ctx, events.LibraryScanProgress, ScanProgress{
Phase: "orphans", Total: totalFiles,
Processed: a + s + u, Added: a, Skipped: s, Updated: u,
})
orphanStart := time.Now()
+18 -5
View File
@@ -396,7 +396,10 @@ func TestCachedLinkArtist(t *testing.T) {
lib.cachedLinkArtist(q, cache, metrics, "Queen", ac.ID)
if len(cache.linkedCredits) != 1 {
t.Errorf("linkedCredits after duplicate = %d, want 1 (should skip)", len(cache.linkedCredits))
t.Errorf(
"linkedCredits after duplicate = %d, want 1 (should skip)",
len(cache.linkedCredits),
)
}
}
@@ -536,6 +539,7 @@ func TestResolveReleaseGroup(t *testing.T) {
// Cache key is composite: "albumName\x00artistCreditID".
cacheKey := fmt.Sprintf("%s\x00%d", "A Night at the Opera", ac.ID)
cachedRG := cache.releaseGroups[cacheKey]
if !cachedRG.CoverArtID.Valid {
t.Error("expected CoverArtID to be set after update")
}
@@ -616,7 +620,9 @@ func TestOrphanDeletion(t *testing.T) {
}
// Add FTS search index entry.
if err := db.InsertSearchIndex(af.ID, "/music/test.mp3", "Test Song", "Test Artist", ""); err != nil {
if err := db.InsertSearchIndex(
af.ID, "/music/test.mp3", "Test Song", "Test Artist", "",
); err != nil {
t.Fatalf("insert search index: %v", err)
}
@@ -651,11 +657,12 @@ func TestOrphanDeletion(t *testing.T) {
// become stale but harmless (they reference a non-existent
// audio_file ID, so JOINs return no results).
// ClearSearchIndex (used during full rescan) handles bulk cleanup.
// DeleteSearchIndex on contentless FTS5 is expected to error.
// Not a fatal error — documents the contentless FTS5 limitation.
err = db.DeleteSearchIndex(af.ID)
if err == nil {
t.Log("DeleteSearchIndex succeeded (unexpected for contentless FTS5)")
}
// Not a fatal error — documents the contentless FTS5 limitation.
}
// ---------------------------------------------------------------------------
@@ -705,7 +712,10 @@ func TestEntityCache_EmptyFields(t *testing.T) {
}
if albumACID.Int64 != trackAC.ID {
t.Errorf("empty AlbumArtist should reuse track credit: got %d, want %d", albumACID.Int64, trackAC.ID)
t.Errorf(
"empty AlbumArtist should reuse track credit: got %d, want %d",
albumACID.Int64, trackAC.ID,
)
}
// resolveAlbumArtistCredit when AlbumArtist matches Artist also reuses.
@@ -716,6 +726,9 @@ func TestEntityCache_EmptyFields(t *testing.T) {
sameACID := lib.resolveAlbumArtistCredit(q, cache, metrics, sameTags, trackAC.ID)
if sameACID.Int64 != trackAC.ID {
t.Errorf("matching AlbumArtist should reuse track credit: got %d, want %d", sameACID.Int64, trackAC.ID)
t.Errorf(
"matching AlbumArtist should reuse track credit: got %d, want %d",
sameACID.Int64, trackAC.ID,
)
}
}
+10 -4
View File
@@ -126,8 +126,11 @@ func TestUserVolume_ToVolume_Roundtrip(t *testing.T) {
diff := int(roundtripped) - int(i)
if diff < -1 || diff > 1 {
t.Errorf("Roundtrip UserVolume(%d) -> Volume(%f) -> UserVolume(%d): drift %d exceeds ±1",
i, vol, roundtripped, diff)
t.Errorf(
"Roundtrip UserVolume(%d) -> Volume(%f) -> UserVolume(%d): "+
"drift %d exceeds ±1",
i, vol, roundtripped, diff,
)
}
}
@@ -138,8 +141,11 @@ func TestUserVolume_ToVolume_Roundtrip(t *testing.T) {
roundtripped := vol.ToUserVolume()
if roundtripped != uv {
t.Errorf("Exact roundtrip UserVolume(%d) -> Volume(%f) -> UserVolume(%d): want exact match",
uv, vol, roundtripped)
t.Errorf(
"Exact roundtrip UserVolume(%d) -> Volume(%f) -> "+
"UserVolume(%d): want exact match",
uv, vol, roundtripped,
)
}
}
}
+17 -4
View File
@@ -14,7 +14,7 @@ func newTestQueueDirect(tracks int, currentIndex int) *Queue {
}
q.tracks = make([]Track, tracks)
for i := 0; i < tracks; i++ {
for i := range tracks {
q.tracks[i] = Track{FilePath: "/test/track.mp3", Position: int64(i)}
}
@@ -135,11 +135,15 @@ func TestGenerateShuffleOrder_Properties(t *testing.T) {
// Property 2: current track is at shuffleOrder[0].
if q.shuffleOrder[0] != tc.currentIdx {
t.Errorf("shuffleOrder[0]: got %d, want %d (currentIndex)", q.shuffleOrder[0], tc.currentIdx)
t.Errorf(
"shuffleOrder[0]: got %d, want %d (currentIndex)",
q.shuffleOrder[0], tc.currentIdx,
)
}
// Property 3: all indices present (no duplicates, no missing).
seen := make(map[int]bool, tc.trackCount)
for _, idx := range q.shuffleOrder {
if idx < 0 || idx >= tc.trackCount {
t.Errorf("shuffleOrder contains out-of-range index: %d", idx)
@@ -153,7 +157,10 @@ func TestGenerateShuffleOrder_Properties(t *testing.T) {
}
if len(seen) != tc.trackCount {
t.Errorf("unique indices in shuffleOrder: got %d, want %d", len(seen), tc.trackCount)
t.Errorf(
"unique indices in shuffleOrder: got %d, want %d",
len(seen), tc.trackCount,
)
}
})
}
@@ -177,6 +184,7 @@ func TestNextIndex_ShuffleMode(t *testing.T) {
// Advance to index 4 and get next.
q.currentIndex = 4
got = q.nextIndex()
if got != 0 {
t.Errorf("nextIndex (shuffle, pos 2): got %d, want 0", got)
}
@@ -184,6 +192,7 @@ func TestNextIndex_ShuffleMode(t *testing.T) {
// At the end of shuffle order with RepeatOff.
q.currentIndex = 1 // last in shuffleOrder
got = q.nextIndex()
if got != -1 {
t.Errorf("nextIndex (shuffle, end, repeatOff): got %d, want -1", got)
}
@@ -191,7 +200,11 @@ func TestNextIndex_ShuffleMode(t *testing.T) {
// At the end of shuffle order with RepeatAll.
q.repeatMode = RepeatAll
got = q.nextIndex()
if got != 2 {
t.Errorf("nextIndex (shuffle, end, repeatAll): got %d, want 2 (wraps to shuffleOrder[0])", got)
t.Errorf(
"nextIndex (shuffle, end, repeatAll): got %d, want 2 "+
"(wraps to shuffleOrder[0])", got,
)
}
}
+12 -3
View File
@@ -35,7 +35,10 @@ func TestSaveState_RestoreState_Roundtrip(t *testing.T) {
// Each track's FilePath, Title, Artist.
for i := range s1.Tracks {
if s2.Tracks[i].FilePath != s1.Tracks[i].FilePath {
t.Errorf("track[%d] FilePath: got %q, want %q", i, s2.Tracks[i].FilePath, s1.Tracks[i].FilePath)
t.Errorf(
"track[%d] FilePath: got %q, want %q",
i, s2.Tracks[i].FilePath, s1.Tracks[i].FilePath,
)
}
if s2.Tracks[i].Title != s1.Tracks[i].Title {
@@ -43,7 +46,10 @@ func TestSaveState_RestoreState_Roundtrip(t *testing.T) {
}
if s2.Tracks[i].Artist != s1.Tracks[i].Artist {
t.Errorf("track[%d] Artist: got %q, want %q", i, s2.Tracks[i].Artist, s1.Tracks[i].Artist)
t.Errorf(
"track[%d] Artist: got %q, want %q",
i, s2.Tracks[i].Artist, s1.Tracks[i].Artist,
)
}
}
@@ -71,7 +77,10 @@ func TestSaveState_RestoreState_Roundtrip(t *testing.T) {
} else {
for i := range q.shuffleOrder {
if q2.shuffleOrder[i] != q.shuffleOrder[i] {
t.Errorf("shuffleOrder[%d]: got %d, want %d", i, q2.shuffleOrder[i], q.shuffleOrder[i])
t.Errorf(
"shuffleOrder[%d]: got %d, want %d",
i, q2.shuffleOrder[i], q.shuffleOrder[i],
)
}
}
}
+9 -3
View File
@@ -16,6 +16,7 @@ type mockTrackLoader struct {
func (m *mockTrackLoader) LoadFile(filePath string) error {
m.loadedFile = filePath
return nil
}
@@ -53,7 +54,7 @@ func seedAudioFiles(t *testing.T, db *database.DB, count int) []string {
paths := make([]string, count)
for i := 0; i < count; i++ {
for i := range count {
recID := i + 1
afID := i + 1
fp := fmt.Sprintf("/test/track%d.mp3", i+1)
@@ -68,7 +69,9 @@ func seedAudioFiles(t *testing.T, db *database.DB, count int) []string {
}
_, err = db.ExecContext(
"INSERT OR IGNORE INTO audio_files (id, file_path, length_milliseconds, file_type_id, recording_id) VALUES (?, ?, 180000, 0, ?)",
"INSERT OR IGNORE INTO audio_files (id, file_path, "+
"length_milliseconds, file_type_id, recording_id) "+
"VALUES (?, ?, 180000, 0, ?)",
afID, fp, recID,
)
if err != nil {
@@ -292,7 +295,10 @@ func TestRemoveTrack_RemoveCurrentTrack(t *testing.T) {
// After removing currentIndex=2, index should be clamped to valid range.
if state.CurrentIndex < 0 || state.CurrentIndex >= len(state.Tracks) {
t.Errorf("currentIndex out of range: got %d, track count %d", state.CurrentIndex, len(state.Tracks))
t.Errorf(
"currentIndex out of range: got %d, track count %d",
state.CurrentIndex, len(state.Tracks),
)
}
}
+2
View File
@@ -49,6 +49,7 @@ func TestThemeConfig_Validate_InvalidHexColor(t *testing.T) {
t.Parallel()
c := &Config{AccentColor: tt.color, BackgroundShade: BackgroundDark}
err := c.Validate()
if err == nil {
t.Error("Validate() expected error for invalid hex color, got nil")
@@ -61,6 +62,7 @@ func TestThemeConfig_Validate_InvalidBackgroundShade(t *testing.T) {
t.Parallel()
c := &Config{AccentColor: "#ffd43b", BackgroundShade: "neon"}
err := c.Validate()
if err == nil {
t.Fatal("Validate() expected error for unknown shade, got nil")