Files
NaviWatcher/internal/musicbrainz/cache_test.go
Vladimir Zagainov 582202bb86 fix: address second code review findings
- Add context cancellation check between pagination pages in GetArtistReleaseGroups
  for responsive graceful shutdown during large discography fetches.
- Eliminate double DB query on cache hit by having GetCachedReleases return
  []ExternalRelease directly instead of just a count, avoiding a redundant
  second query in SyncArtistDiscography.
- Update cache_test.go to match new GetCachedReleases return type.
- Format main.go (pre-existing whitespace issue).
2026-05-26 15:58:40 +03:00

185 lines
5.0 KiB
Go

package musicbrainz
import (
"testing"
"time"
"naviwatcher/internal/database"
)
func insertTestArtistForCache(db *database.DB, id string) error {
_, err := db.Conn().Exec(
"INSERT OR IGNORE INTO artist_settings (id, name) VALUES (?, ?)",
id, "Test Artist "+id,
)
return err
}
func TestGetCachedReleases_CacheHit(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
artistID := "artist-cache-hit"
if err := insertTestArtistForCache(db, artistID); err != nil {
t.Fatalf("insertTestArtist: %v", err)
}
// Insert releases with recent cached_at timestamps
now := time.Now().Format("2006-01-02 15:04:05")
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, type, cached_at) VALUES (?, ?, ?, ?, ?)",
"rg-hit-1", artistID, "Cached Album 1", "album", now,
)
if err != nil {
t.Fatalf("insert rg-hit-1: %v", err)
}
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, type, cached_at) VALUES (?, ?, ?, ?, ?)",
"rg-hit-2", artistID, "Cached Album 2", "single", now,
)
if err != nil {
t.Fatalf("insert rg-hit-2: %v", err)
}
ttl := 24 * time.Hour
releases, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if len(releases) != 2 {
t.Errorf("GetCachedReleases() returned %d releases, want 2", len(releases))
}
}
func TestGetCachedReleases_CacheMiss_Expired(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
artistID := "artist-cache-miss"
if err := insertTestArtistForCache(db, artistID); err != nil {
t.Fatalf("insertTestArtist: %v", err)
}
// Insert a release with an expired cached_at (48 hours ago)
expired := time.Now().Add(-48 * time.Hour).Format("2006-01-02 15:04:05")
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, type, cached_at) VALUES (?, ?, ?, ?, ?)",
"rg-expired", artistID, "Expired Album", "album", expired,
)
if err != nil {
t.Fatalf("insert expired release: %v", err)
}
ttl := 24 * time.Hour
releases, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if len(releases) != 0 {
t.Errorf("GetCachedReleases() returned %d releases, want 0 (expired entry should not be cached)", len(releases))
}
}
func TestGetCachedReleases_CacheMiss_NoCachedAt(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
artistID := "artist-no-cached"
if err := insertTestArtistForCache(db, artistID); err != nil {
t.Fatalf("insertTestArtist: %v", err)
}
// Insert a release WITHOUT cached_at (NULL)
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, type) VALUES (?, ?, ?, ?)",
"rg-nocached", artistID, "Uncached Album", "album",
)
if err != nil {
t.Fatalf("insert uncached release: %v", err)
}
ttl := 24 * time.Hour
releases, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if len(releases) != 0 {
t.Errorf("GetCachedReleases() returned %d releases, want 0 (NULL cached_at should not be cached)", len(releases))
}
}
func TestGetCachedReleases_EmptyArtist(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
ttl := 24 * time.Hour
releases, err := GetCachedReleases(db, "nonexistent-artist", ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if len(releases) != 0 {
t.Errorf("GetCachedReleases() returned %d releases, want 0 for nonexistent artist", len(releases))
}
}
func TestGetCachedReleases_MixedExpiry(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
artistID := "artist-mixed"
if err := insertTestArtistForCache(db, artistID); err != nil {
t.Fatalf("insertTestArtist: %v", err)
}
now := time.Now().Format("2006-01-02 15:04:05")
expired := time.Now().Add(-48 * time.Hour).Format("2006-01-02 15:04:05")
// Mix of fresh and expired
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, cached_at) VALUES (?, ?, ?, ?)",
"rg-fresh", artistID, "Fresh Album", now,
)
if err != nil {
t.Fatalf("insert fresh: %v", err)
}
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, cached_at) VALUES (?, ?, ?, ?)",
"rg-old", artistID, "Old Album", expired,
)
if err != nil {
t.Fatalf("insert old: %v", err)
}
ttl := 24 * time.Hour
releases, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if len(releases) != 1 {
t.Errorf("GetCachedReleases() returned %d releases, want 1 (only fresh entry)", len(releases))
}
if len(releases) > 0 && releases[0].RGID != "rg-fresh" {
t.Errorf("expected rg-fresh, got %s", releases[0].RGID)
}
}