diff --git a/code/backend/Cleanuparr.Domain/Entities/Arr/SearchableSeries.cs b/code/backend/Cleanuparr.Domain/Entities/Arr/SearchableSeries.cs index cc2c8532..4dd61b4a 100644 --- a/code/backend/Cleanuparr.Domain/Entities/Arr/SearchableSeries.cs +++ b/code/backend/Cleanuparr.Domain/Entities/Arr/SearchableSeries.cs @@ -19,4 +19,6 @@ public sealed record SearchableSeries public string SeriesType { get; init; } = string.Empty; public SeriesStatistics? Statistics { get; init; } + + public List Seasons { get; init; } = []; } diff --git a/code/backend/Cleanuparr.Domain/Entities/Arr/SeasonStatistics.cs b/code/backend/Cleanuparr.Domain/Entities/Arr/SeasonStatistics.cs new file mode 100644 index 00000000..acb42322 --- /dev/null +++ b/code/backend/Cleanuparr.Domain/Entities/Arr/SeasonStatistics.cs @@ -0,0 +1,12 @@ +namespace Cleanuparr.Domain.Entities.Arr; + +public sealed record SeasonStatistics +{ + public int EpisodeCount { get; init; } + + public int EpisodeFileCount { get; init; } + + public int TotalEpisodeCount { get; init; } + + public DateTimeOffset? NextAiring { get; init; } +} diff --git a/code/backend/Cleanuparr.Domain/Entities/Arr/SeriesSeason.cs b/code/backend/Cleanuparr.Domain/Entities/Arr/SeriesSeason.cs new file mode 100644 index 00000000..042c8289 --- /dev/null +++ b/code/backend/Cleanuparr.Domain/Entities/Arr/SeriesSeason.cs @@ -0,0 +1,10 @@ +namespace Cleanuparr.Domain.Entities.Arr; + +public sealed record SeriesSeason +{ + public int SeasonNumber { get; init; } + + public bool Monitored { get; init; } + + public SeasonStatistics? Statistics { get; init; } +} diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/SonarrClientTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/SonarrClientTests.cs index 6c784636..0c680238 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/SonarrClientTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Arr/SonarrClientTests.cs @@ -346,6 +346,57 @@ public class SonarrClientTests request.Headers.GetValues("x-api-key").ShouldHaveSingleItem().ShouldBe("api-key"); } + [Fact] + public async Task StreamAllSeriesAsync_ReadsSeasonStatistics() + { + // Arrange + const string body = """ + [ + { + "id": 1, + "title": "Show", + "seasons": [ + { + "seasonNumber": 1, + "monitored": true, + "statistics": { "episodeCount": 10, "episodeFileCount": 8, "totalEpisodeCount": 10 } + }, + { + "seasonNumber": 2, + "monitored": false, + "statistics": { "nextAiring": "2026-11-01T20:00:00Z", "episodeCount": 3, "episodeFileCount": 1, "totalEpisodeCount": 12 } + } + ] + } + ] + """; + _httpMessageHandler.SetupResponse((_, _) => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(body, Encoding.UTF8, "application/json"), + })); + + // Act + List result = []; + await foreach (SearchableSeries series in _client.StreamAllSeriesAsync(_arrInstance)) + { + result.Add(series); + } + + // Assert + List seasons = result.ShouldHaveSingleItem().Seasons; + seasons.Count.ShouldBe(2); + seasons[0].SeasonNumber.ShouldBe(1); + seasons[0].Monitored.ShouldBeTrue(); + seasons[0].Statistics!.EpisodeCount.ShouldBe(10); + seasons[0].Statistics!.EpisodeFileCount.ShouldBe(8); + seasons[0].Statistics!.TotalEpisodeCount.ShouldBe(10); + seasons[0].Statistics!.NextAiring.ShouldBeNull(); + seasons[1].SeasonNumber.ShouldBe(2); + seasons[1].Monitored.ShouldBeFalse(); + seasons[1].Statistics!.EpisodeCount.ShouldBe(3); + seasons[1].Statistics!.NextAiring.ShouldBe(new DateTimeOffset(2026, 11, 1, 20, 0, 0, TimeSpan.Zero)); + } + [Fact] public async Task StreamAllSeriesAsync_NullBody_ReturnsEmpty() {