fix: address code review findings

Remove dead code: duplicate ExternalRelease/Artist/ParsedArtist structs in
model.go, ParseArtist/mbArtist/mbArtistData in client.go, ArtistTypeFilter
and related filtering functions in api.go, SyncArtistDiscographyWithFilter
in sync.go, and CacheStats/IsArtistCacheValid in cache.go.

Fix bugs: SaveExternalRelease now stores NULL instead of empty string for
zero CachedAt; sync upserts are now transactional with stale release cleanup;
getCachedReleases returns int instead of *CacheStats; doGet uses url.Values
for proper query encoding of MBID.

Fix tests: removed duplicate TestRun_GracefulShutdown, removed dead code
(_ = dbPath) from TestNewApp, fixed assertions in httptest handler goroutine
to avoid data race, increased rate limiter timing tolerance, removed
Client.Close() calls (no-op removed), fixed sync test cache expiry to use
UPDATE instead of 0 TTL races.

Fix formatting: cancel()}() formatting in main.go, error format string in sync.go.
This commit is contained in:
2026-05-26 14:10:22 +03:00
parent 34ea84fc77
commit a5911c257c
13 changed files with 199 additions and 864 deletions

View File

@@ -45,20 +45,13 @@ func TestGetCachedReleases_CacheHit(t *testing.T) {
}
ttl := 24 * time.Hour
stats, err := GetCachedReleases(db, artistID, ttl)
count, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if stats.CacheHitCount != 2 {
t.Errorf("CacheHitCount = %d, want 2", stats.CacheHitCount)
}
if !stats.IsCached("rg-hit-1") {
t.Error("expected rg-hit-1 to be cached")
}
if !stats.IsCached("rg-hit-2") {
t.Error("expected rg-hit-2 to be cached")
if count != 2 {
t.Errorf("GetCachedReleases() = %d, want 2", count)
}
}
@@ -85,17 +78,13 @@ func TestGetCachedReleases_CacheMiss_Expired(t *testing.T) {
}
ttl := 24 * time.Hour
stats, err := GetCachedReleases(db, artistID, ttl)
count, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if stats.CacheHitCount != 0 {
t.Errorf("CacheHitCount = %d, want 0 (expired entry should not be cached)", stats.CacheHitCount)
}
if stats.IsCached("rg-expired") {
t.Error("expected rg-expired to NOT be cached")
if count != 0 {
t.Errorf("GetCachedReleases() = %d, want 0 (expired entry should not be cached)", count)
}
}
@@ -121,13 +110,13 @@ func TestGetCachedReleases_CacheMiss_NoCachedAt(t *testing.T) {
}
ttl := 24 * time.Hour
stats, err := GetCachedReleases(db, artistID, ttl)
count, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if stats.CacheHitCount != 0 {
t.Errorf("CacheHitCount = %d, want 0 (NULL cached_at should not be cached)", stats.CacheHitCount)
if count != 0 {
t.Errorf("GetCachedReleases() = %d, want 0 (NULL cached_at should not be cached)", count)
}
}
@@ -139,70 +128,13 @@ func TestGetCachedReleases_EmptyArtist(t *testing.T) {
defer db.Close()
ttl := 24 * time.Hour
stats, err := GetCachedReleases(db, "nonexistent-artist", ttl)
count, err := GetCachedReleases(db, "nonexistent-artist", ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if stats.CacheHitCount != 0 {
t.Errorf("CacheHitCount = %d, want 0 for nonexistent artist", stats.CacheHitCount)
}
}
func TestIsArtistCacheValid_Valid(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
artistID := "artist-valid-cache"
if err := insertTestArtistForCache(db, artistID); err != nil {
t.Fatalf("insertTestArtist: %v", err)
}
now := time.Now().Format("2006-01-02 15:04:05")
_, err = db.Conn().Exec(
"INSERT INTO external_releases (rgid, artist_id, title, cached_at) VALUES (?, ?, ?, ?)",
"rg-valid", artistID, "Valid Album", now,
)
if err != nil {
t.Fatalf("insert: %v", err)
}
ttl := 24 * time.Hour
valid, err := IsArtistCacheValid(db, artistID, ttl)
if err != nil {
t.Fatalf("IsArtistCacheValid() error: %v", err)
}
if !valid {
t.Error("expected cache to be valid")
}
}
func TestIsArtistCacheValid_Invalid(t *testing.T) {
db, err := database.New(":memory:")
if err != nil {
t.Fatalf("New() error: %v", err)
}
defer db.Close()
ttl := 24 * time.Hour
// No releases at all
valid, err := IsArtistCacheValid(db, "no-releases", ttl)
if err != nil {
t.Fatalf("IsArtistCacheValid() error: %v", err)
}
if valid {
t.Error("expected cache to be invalid for artist with no releases")
}
}
func TestCacheStats_IsCached_Empty(t *testing.T) {
stats := &CacheStats{}
if stats.IsCached("anything") {
t.Error("expected IsCached to return false for empty stats")
if count != 0 {
t.Errorf("GetCachedReleases() = %d, want 0 for nonexistent artist", count)
}
}
@@ -238,18 +170,12 @@ func TestGetCachedReleases_MixedExpiry(t *testing.T) {
}
ttl := 24 * time.Hour
stats, err := GetCachedReleases(db, artistID, ttl)
count, err := GetCachedReleases(db, artistID, ttl)
if err != nil {
t.Fatalf("GetCachedReleases() error: %v", err)
}
if stats.CacheHitCount != 1 {
t.Errorf("CacheHitCount = %d, want 1 (only fresh entry)", stats.CacheHitCount)
}
if !stats.IsCached("rg-fresh") {
t.Error("expected rg-fresh to be cached")
}
if stats.IsCached("rg-old") {
t.Error("expected rg-old to NOT be cached (expired)")
if count != 1 {
t.Errorf("GetCachedReleases() = %d, want 1 (only fresh entry)", count)
}
}