package database import ( "testing" ) // TestNew_InitializationAndSchema verifies that New() creates all expected tables. func TestNew_InitializationAndSchema(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() expectedTables := []string{ "_migrations", "artist_settings", "external_releases", "local_albums", "notifications_sent", } for _, table := range expectedTables { var name string err := db.Conn().QueryRow( "SELECT name FROM sqlite_master WHERE type='table' AND name=?", table, ).Scan(&name) if err != nil { t.Errorf("expected table %q to exist, got error: %v", table, err) } } } // TestNew_MigrationIdempency verifies that calling New() twice (via migrate) does not fail. func TestNew_MigrationIdempotency(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("first New() error: %v", err) } // Running migrate again on the same connection should be a no-op. err = db.migrate() if err != nil { t.Fatalf("second migrate() error: %v", err) } // Verify tables still exist. var count int err = db.Conn().QueryRow( "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name IN (?,?,?,?,?)", "_migrations", "artist_settings", "external_releases", "local_albums", "notifications_sent", ).Scan(&count) if err != nil { t.Fatalf("query error: %v", err) } if count != 5 { t.Errorf("expected 5 tables, got %d", count) } db.Close() } // TestClose verifies that Close() properly closes the connection. func TestClose(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } if err := db.Close(); err != nil { t.Fatalf("Close() error: %v", err) } // After close, queries should fail. var dummy int err = db.Conn().QueryRow("SELECT 1").Scan(&dummy) if err == nil { t.Error("expected error querying after Close(), got nil") } } // TestArtistSettingsSchema verifies the artist_settings table has the correct columns. func TestArtistSettingsSchema(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert a row to verify column names and types. _, 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 into artist_settings: %v", err) } var id, name string var ignoreSingles, ignoreCompilations, monitored bool err = db.Conn().QueryRow( "SELECT id, name, ignore_singles, ignore_compilations, monitored FROM artist_settings WHERE id = ?", "artist-1", ).Scan(&id, &name, &ignoreSingles, &ignoreCompilations, &monitored) if err != nil { t.Fatalf("select from artist_settings: %v", err) } if id != "artist-1" || name != "Test Artist" || !ignoreSingles || ignoreCompilations || !monitored { t.Errorf("unexpected row values: id=%q name=%q ignoreSingles=%v ignoreCompilations=%v monitored=%v", id, name, ignoreSingles, ignoreCompilations, monitored) } } // TestExternalReleasesSchema verifies the external_releases table has the correct columns. func TestExternalReleasesSchema(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert parent artist first (FK requirement). _, err = db.Conn().Exec("INSERT INTO artist_settings (id, name) VALUES (?, ?)", "artist-1", "Test Artist") if err != nil { t.Fatalf("insert artist: %v", err) } _, err = db.Conn().Exec( "INSERT INTO external_releases (rgid, artist_id, title, type, release_date, is_ignored) VALUES (?, ?, ?, ?, ?, ?)", "rgid-1", "artist-1", "Test Album", "album", "2024-01-01", false, ) if err != nil { t.Fatalf("insert into external_releases: %v", err) } var rgid, artistID, title, releaseType, releaseDate string var isIgnored bool err = db.Conn().QueryRow( "SELECT rgid, artist_id, title, type, release_date, is_ignored FROM external_releases WHERE rgid = ?", "rgid-1", ).Scan(&rgid, &artistID, &title, &releaseType, &releaseDate, &isIgnored) if err != nil { t.Fatalf("select from external_releases: %v", err) } if rgid != "rgid-1" || artistID != "artist-1" || title != "Test Album" || releaseType != "album" || releaseDate != "2024-01-01" || isIgnored { t.Errorf("unexpected row values") } } // TestNotificationsSentSchema verifies the notifications_sent table has the correct columns. func TestNotificationsSentSchema(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() // Insert parent artist and release first (FK requirements). _, err = db.Conn().Exec("INSERT INTO artist_settings (id, name) VALUES (?, ?)", "artist-1", "Test Artist") if err != nil { t.Fatalf("insert artist: %v", err) } _, err = db.Conn().Exec("INSERT INTO external_releases (rgid, artist_id, title) VALUES (?, ?, ?)", "rgid-1", "artist-1", "Test Album") if err != nil { t.Fatalf("insert release: %v", err) } _, err = db.Conn().Exec( "INSERT INTO notifications_sent (rgid) VALUES (?)", "rgid-1", ) if err != nil { t.Fatalf("insert into notifications_sent: %v", err) } var rgid string var sentAt string err = db.Conn().QueryRow( "SELECT rgid, sent_at FROM notifications_sent WHERE rgid = ?", "rgid-1", ).Scan(&rgid, &sentAt) if err != nil { t.Fatalf("select from notifications_sent: %v", err) } if rgid != "rgid-1" { t.Errorf("expected rgid 'rgid-1', got %q", rgid) } if sentAt == "" { t.Error("expected sent_at to be non-empty") } } // TestMigrationTracking verifies that migrations are recorded in _migrations table. func TestMigrationTracking(t *testing.T) { db, err := New(":memory:") if err != nil { t.Fatalf("New() error: %v", err) } defer db.Close() var count int err = db.Conn().QueryRow("SELECT COUNT(*) FROM _migrations").Scan(&count) if err != nil { t.Fatalf("query migrations count: %v", err) } // We have 5 recorded migrations: artist_settings, external_releases, local_albums, notifications_sent, cached_at column. if count != 5 { t.Errorf("expected 5 applied migrations, got %d", count) } }