package database import ( "database/sql" "testing" ) // TestGetArtistSettings_Found verifies retrieving an existing artist. func TestGetArtistSettings_Found(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert a row directly. _, err = db.Conn().Exec( "INSERT INTO artist_settings (id, name, ignore_singles, ignore_compilations, monitored) VALUES (?, ?, ?, ?, ?)", "artist-1", "Test Artist", true, false, true, ) if err != nil { t.Fatalf("insert: %v", err) } s, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if s.ID != "artist-1" { t.Errorf("expected ID 'artist-1', got %q", s.ID) } if s.Name != "Test Artist" { t.Errorf("expected Name 'Test Artist', got %q", s.Name) } if !s.IgnoreSingles { t.Error("expected IgnoreSingles true") } if s.IgnoreCompilations { t.Error("expected IgnoreCompilations false") } if !s.Monitored { t.Error("expected Monitored true") } } // TestGetArtistSettings_NotFound verifies that a missing artist returns ErrArtistNotFound. func TestGetArtistSettings_NotFound(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() _, err = GetArtistSettings(db, "nonexistent") if err != ErrArtistNotFound { t.Errorf("expected ErrArtistNotFound, got %v", err) } } // TestSaveArtistSettings_Insert verifies inserting a new artist. func TestSaveArtistSettings_Insert(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() s := &ArtistSettings{ ID: "artist-1", Name: "New Artist", IgnoreSingles: false, IgnoreCompilations: true, Monitored: true, } if err := SaveArtistSettings(db, s); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } // Verify it was inserted. got, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.Name != "New Artist" { t.Errorf("expected Name 'New Artist', got %q", got.Name) } if got.IgnoreCompilations != true { t.Errorf("expected IgnoreCompilations true, got %v", got.IgnoreCompilations) } } // TestSaveArtistSettings_Update verifies that SaveArtistSettings replaces an existing row. func TestSaveArtistSettings_Update(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert initial row. s1 := &ArtistSettings{ ID: "artist-1", Name: "Original Name", IgnoreSingles: false, IgnoreCompilations: false, Monitored: true, } if err := SaveArtistSettings(db, s1); err != nil { t.Fatalf("first SaveArtistSettings() error: %v", err) } // Update the row. s2 := &ArtistSettings{ ID: "artist-1", Name: "Updated Name", IgnoreSingles: true, IgnoreCompilations: true, Monitored: false, } if err := SaveArtistSettings(db, s2); err != nil { t.Fatalf("second SaveArtistSettings() error: %v", err) } got, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.Name != "Updated Name" { t.Errorf("expected Name 'Updated Name', got %q", got.Name) } if !got.IgnoreSingles { t.Error("expected IgnoreSingles true") } if !got.IgnoreCompilations { t.Error("expected IgnoreCompilations true") } if got.Monitored { t.Error("expected Monitored false") } } // TestGetAllArtistSettings_Empty verifies GetAllArtistSettings returns empty slice when no rows. func TestGetAllArtistSettings_Empty(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() results, err := GetAllArtistSettings(db) if err != nil { t.Fatalf("GetAllArtistSettings() error: %v", err) } if len(results) != 0 { t.Errorf("expected 0 results, got %d", len(results)) } } // TestGetAllArtistSettings_Populated verifies GetAllArtistSettings returns all rows. func TestGetAllArtistSettings_Populated(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() artists := []ArtistSettings{ {ID: "a1", Name: "Artist One", Monitored: true}, {ID: "a2", Name: "Artist Two", IgnoreSingles: true}, {ID: "a3", Name: "Artist Three", IgnoreCompilations: true}, } for _, a := range artists { if err := SaveArtistSettings(db, &a); err != nil { t.Fatalf("SaveArtistSettings(%s) error: %v", a.ID, err) } } results, err := GetAllArtistSettings(db) if err != nil { t.Fatalf("GetAllArtistSettings() error: %v", err) } if len(results) != 3 { t.Fatalf("expected 3 results, got %d", len(results)) } // Verify all artists are present (order not guaranteed, use a map). byID := make(map[string]ArtistSettings) for _, r := range results { byID[r.ID] = r } for _, expected := range artists { got, ok := byID[expected.ID] if !ok { t.Errorf("expected artist %s in results", expected.ID) continue } if got.Name != expected.Name { t.Errorf("artist %s: expected Name %q, got %q", expected.ID, expected.Name, got.Name) } } } // TestUpdateArtistSettings_Success verifies partial updates work. func TestUpdateArtistSettings_Success(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert initial row. s := &ArtistSettings{ ID: "artist-1", Name: "Original", IgnoreSingles: false, IgnoreCompilations: false, Monitored: true, } if err := SaveArtistSettings(db, s); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } // Update only name and monitored. updates := map[string]interface{}{ "name": "Updated", "monitored": false, } if err := UpdateArtistSettings(db, "artist-1", updates); err != nil { t.Fatalf("UpdateArtistSettings() error: %v", err) } got, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.Name != "Updated" { t.Errorf("expected Name 'Updated', got %q", got.Name) } if got.Monitored { t.Error("expected Monitored false") } // Unchanged fields should remain. if got.IgnoreSingles != false { t.Error("expected IgnoreSingles unchanged (false)") } if got.IgnoreCompilations != false { t.Error("expected IgnoreCompilations unchanged (false)") } } // TestUpdateArtistSettings_NotFound verifies updating a nonexistent artist returns error. func TestUpdateArtistSettings_NotFound(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() updates := map[string]interface{}{"name": "Ghost"} err = UpdateArtistSettings(db, "nonexistent", updates) if err == nil { t.Error("expected error for nonexistent artist, got nil") } } // TestUpdateArtistSettings_InvalidColumn verifies unknown columns are rejected. func TestUpdateArtistSettings_InvalidColumn(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() s := &ArtistSettings{ID: "artist-1", Name: "Test"} if err := SaveArtistSettings(db, s); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } updates := map[string]interface{}{"invalid_col": "value"} err = UpdateArtistSettings(db, "artist-1", updates) if err == nil { t.Error("expected error for invalid column, got nil") } } // TestUpdateArtistSettings_EmptyUpdates verifies empty updates map returns error. func TestUpdateArtistSettings_EmptyUpdates(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() s := &ArtistSettings{ID: "artist-1", Name: "Test"} if err := SaveArtistSettings(db, s); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } updates := map[string]interface{}{} err = UpdateArtistSettings(db, "artist-1", updates) if err == nil { t.Error("expected error for empty updates, got nil") } } // TestMigration008_MbidColumnExists verifies the 008 migration adds the mbid // column and that rows created before resolution have a NULL/empty MBID. func TestMigration008_MbidColumnExists(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert a row without supplying mbid (simulates a pre-resolution row). if _, err := db.Conn().Exec( "INSERT INTO artist_settings (id, name) VALUES (?, ?)", "artist-1", "No MBID Yet", ); err != nil { t.Fatalf("insert without mbid: %v", err) } var mbid sql.NullString if err := db.Conn().QueryRow( "SELECT mbid FROM artist_settings WHERE id = ?", "artist-1", ).Scan(&mbid); err != nil { t.Fatalf("query mbid: %v", err) } if mbid.Valid && mbid.String != "" { t.Errorf("expected empty mbid for pre-resolution row, got %q", mbid.String) } } // TestArtistSettings_MbidRoundTrip verifies Save/Get round-trips an MBID, // and that an empty MBID is preserved (not overwritten with garbage). func TestArtistSettings_MbidRoundTrip(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() s := &ArtistSettings{ ID: "artist-1", Name: "Test Artist", MBID: "f27e6623-8771-4a2e-8dcb-6c8b1a4f8b9a", Monitored: true, } if err := SaveArtistSettings(db, s); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } got, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.MBID != s.MBID { t.Errorf("expected MBID %q, got %q", s.MBID, got.MBID) } } // TestArtistSettings_MbidEmptyAllowed verifies an artist can be saved and // retrieved with no MBID set (lazy resolution not yet performed). func TestArtistSettings_MbidEmptyAllowed(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() s := &ArtistSettings{ID: "artist-1", Name: "No MBID", Monitored: true} if err := SaveArtistSettings(db, s); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } got, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.MBID != "" { t.Errorf("expected empty MBID, got %q", got.MBID) } } // TestArtistSettings_MbidUpdatePersists verifies UpdateArtistSettings can set // and clear the MBID column. func TestArtistSettings_MbidUpdatePersists(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() if err := SaveArtistSettings(db, &ArtistSettings{ID: "artist-1", Name: "Test", Monitored: true}); err != nil { t.Fatalf("SaveArtistSettings() error: %v", err) } mbid := "f27e6623-8771-4a2e-8dcb-6c8b1a4f8b9a" if err := UpdateArtistSettings(db, "artist-1", map[string]interface{}{"mbid": mbid}); err != nil { t.Fatalf("UpdateArtistSettings(set mbid) error: %v", err) } got, err := GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.MBID != mbid { t.Errorf("expected MBID %q after set, got %q", mbid, got.MBID) } // Clear it again. if err := UpdateArtistSettings(db, "artist-1", map[string]interface{}{"mbid": ""}); err != nil { t.Fatalf("UpdateArtistSettings(clear mbid) error: %v", err) } got, err = GetArtistSettings(db, "artist-1") if err != nil { t.Fatalf("GetArtistSettings() error: %v", err) } if got.MBID != "" { t.Errorf("expected empty MBID after clear, got %q", got.MBID) } } // TestArtistSettings_MbidInGetAll verifies GetAllArtistSettings returns the // MBID field for all rows. func TestArtistSettings_MbidInGetAll(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() artists := []ArtistSettings{ {ID: "a1", Name: "Artist One", MBID: "mbid-1", Monitored: true}, {ID: "a2", Name: "Artist Two", Monitored: true}, } for _, a := range artists { if err := SaveArtistSettings(db, &a); err != nil { t.Fatalf("SaveArtistSettings(%s) error: %v", a.ID, err) } } results, err := GetAllArtistSettings(db) if err != nil { t.Fatalf("GetAllArtistSettings() error: %v", err) } if len(results) != 2 { t.Fatalf("expected 2 results, got %d", len(results)) } byID := make(map[string]ArtistSettings) for _, r := range results { byID[r.ID] = r } if byID["a1"].MBID != "mbid-1" { t.Errorf("artist a1: expected MBID 'mbid-1', got %q", byID["a1"].MBID) } if byID["a2"].MBID != "" { t.Errorf("artist a2: expected empty MBID, got %q", byID["a2"].MBID) } }