diff --git a/Brainarr.Plugin/Services/Enrichment/ArtistMbidResolver.cs b/Brainarr.Plugin/Services/Enrichment/ArtistMbidResolver.cs index d449c967..f28530e4 100644 --- a/Brainarr.Plugin/Services/Enrichment/ArtistMbidResolver.cs +++ b/Brainarr.Plugin/Services/Enrichment/ArtistMbidResolver.cs @@ -43,6 +43,8 @@ public async Task> EnrichArtistsAsync(List return new List(); var result = new List(recommendations.Count); + var resolvableCount = 0; + var resolvedCount = 0; foreach (var rec in recommendations) { @@ -53,24 +55,34 @@ public async Task> EnrichArtistsAsync(List continue; } + resolvableCount++; + try { var enriched = await ResolveArtistAsync(rec, ct); if (enriched != null) { + resolvedCount++; result.Add(enriched); } + else + { + // Best-effort: preserve the original recommendation when MBID resolution fails. + // Downstream safety gates (RequireMbids) can still enforce MBID presence when configured. + result.Add(rec); + } } catch (Exception ex) { _logger.Debug(ex, $"Artist MBID resolution error for '{rec.Artist}'"); + result.Add(rec); } // Be nice to MusicBrainz await Task.Delay(200, ct); } - _logger.Info($"Artist MBID resolution complete: {result.Count}/{recommendations.Count} resolvable artists"); + _logger.Info($"Artist MBID resolution complete: resolved {resolvedCount}/{resolvableCount} artists (returned {result.Count})"); return result; } diff --git a/Brainarr.Plugin/Services/Enrichment/MusicBrainzResolver.cs b/Brainarr.Plugin/Services/Enrichment/MusicBrainzResolver.cs index 5e30e6b6..5aea20fb 100644 --- a/Brainarr.Plugin/Services/Enrichment/MusicBrainzResolver.cs +++ b/Brainarr.Plugin/Services/Enrichment/MusicBrainzResolver.cs @@ -45,6 +45,8 @@ public async Task> EnrichWithMbidsAsync(List(); var result = new List(recommendations.Count); + var resolvableCount = 0; + var resolvedCount = 0; // Deduplicate within this batch to avoid repeated queries foreach (var rec in recommendations) { @@ -58,6 +60,8 @@ public async Task> EnrichWithMbidsAsync(List> EnrichWithMbidsAsync(List> EnrichWithMbidsAsync(List> EnrichWithMbidsAsync(List SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable) + { + Content = new StringContent("{}") + }; + return Task.FromResult(response); + } + } + + [Fact] + public async Task EnrichArtistsAsync_PreservesOriginalRecommendation_WhenLookupFails() + { + // Arrange + var httpClient = new HttpClient(new StubHandler()); + var resolver = new ArtistMbidResolver(TestLogger.CreateNullLogger(), httpClient); + var rec = new Recommendation { Artist = "Radiohead", Confidence = 0.9 }; + + // Act + var result = await resolver.EnrichArtistsAsync(new List { rec }, CancellationToken.None); + + // Assert + result.Should().HaveCount(1); + result[0].Artist.Should().Be("Radiohead"); + result[0].ArtistMusicBrainzId.Should().BeNull(); + } + } +} + diff --git a/Brainarr.Tests/Services/Enrichment/MusicBrainzResolverCacheTests.cs b/Brainarr.Tests/Services/Enrichment/MusicBrainzResolverCacheTests.cs index dbf08ca8..1bbb6724 100644 --- a/Brainarr.Tests/Services/Enrichment/MusicBrainzResolverCacheTests.cs +++ b/Brainarr.Tests/Services/Enrichment/MusicBrainzResolverCacheTests.cs @@ -19,10 +19,12 @@ private class StubHandler : HttpMessageHandler { private int _calls; private readonly string _content; + private readonly HttpStatusCode _statusCode; - public StubHandler(string content) + public StubHandler(string content, HttpStatusCode statusCode = HttpStatusCode.OK) { _content = content; + _statusCode = statusCode; } public int Calls => _calls; @@ -30,7 +32,7 @@ public StubHandler(string content) protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Interlocked.Increment(ref _calls); - var response = new HttpResponseMessage(HttpStatusCode.OK) + var response = new HttpResponseMessage(_statusCode) { Content = new StringContent(_content) }; @@ -59,5 +61,27 @@ public async Task EnrichWithMbidsAsync_UsesInProcessCache_ForDuplicateItems() result.Should().HaveCount(2); handler.Calls.Should().Be(1); // Second item served from in-process cache } + + [Fact] + public async Task EnrichWithMbidsAsync_PreservesOriginalRecommendation_WhenLookupFails() + { + // Arrange + var handler = new StubHandler("{}", HttpStatusCode.ServiceUnavailable); + var httpClient = new HttpClient(handler); + var logger = TestLogger.CreateNullLogger(); + var resolver = new MusicBrainzResolver(logger, httpClient); + + var rec = new Recommendation { Artist = "Radiohead", Album = "OK Computer", Confidence = 0.9 }; + + // Act + var result = await resolver.EnrichWithMbidsAsync(new List { rec }, CancellationToken.None); + + // Assert + result.Should().HaveCount(1); + result[0].Artist.Should().Be("Radiohead"); + result[0].Album.Should().Be("OK Computer"); + result[0].ArtistMusicBrainzId.Should().BeNull(); + result[0].AlbumMusicBrainzId.Should().BeNull(); + } } }