diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs index a6c40529..6a26207c 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/RadarrClientTests.cs @@ -143,6 +143,28 @@ public class RadarrClientTests _httpMessageHandler.CapturedRequests.ShouldBeEmpty(); } + [Fact] + public async Task SearchItemsAsync_WithAnEmptyCommandBody_ReturnsEmpty() + { + // An arr answers some commands with an empty body. + _httpMessageHandler.SetupResponse((req, _) => + { + if (req.Method == HttpMethod.Post && req.RequestUri!.AbsolutePath.EndsWith("/command")) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(string.Empty, Encoding.UTF8, "application/json"), + }); + } + + return Task.FromResult(JsonNullResponse()); + }); + + var ids = await _client.SearchItemsAsync(_arrInstance, new HashSet { new() { Id = 10 } }); + + ids.ShouldBeEmpty(); + } + [Fact] public async Task SearchItemsAsync_PostsMoviesSearchCommandWithAllIds() { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs b/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs index 8f33e8fa..5c5a4ebd 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Arr/ArrClient.cs @@ -1,3 +1,4 @@ +using System.Net; using Cleanuparr.Domain.Entities.Arr; using Cleanuparr.Domain.Entities.Arr.Queue; using Cleanuparr.Domain.Enums; @@ -307,8 +308,20 @@ public abstract class ArrClient : IArrClient return response; } + /// + /// Reads the body of a response from an arr. + /// + /// + /// An arr answers some requests with an empty body. An empty body gives null, + /// because each caller has a value for a null result. + /// protected static async Task DeserializeStreamAsync(HttpResponseMessage response, CancellationToken cancellationToken = default) { + if (response.StatusCode is HttpStatusCode.NoContent || response.Content.Headers.ContentLength is 0) + { + return default; + } + await using Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken); return await JsonSerializer.DeserializeAsync(stream, CleanuparrJsonOptions.ExternalApiRead, cancellationToken); }