Add missing episodes feature: implement FindMissingEpisodes endpoint, update metadata providers to retrieve season episode counts, and enhance frontend components for displaying missing episodes report. Update translations for new UI elements.
build / backend (push) Successful in 3m5s
build / frontend (push) Successful in 44s
tests / backend-tests (push) Successful in 2m38s

This commit is contained in:
Leonid Pershin
2026-07-26 02:15:47 +03:00
parent 8962840654
commit 44e954920c
11 changed files with 344 additions and 2 deletions
@@ -0,0 +1,77 @@
using NSubstitute;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Metadata.FindMissingEpisodes;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using Xunit;
namespace TeleWave.Application.Tests.Metadata;
public class FindMissingEpisodesTests
{
[Fact]
public async Task ReportsMissingNumbers_PerLoadedSeason()
{
var fixture = new TestDb();
var show = Show.Create("A", ShowKind.Series);
show.ApplyMetadata("tmdb", "123", "desc", 2000, null);
// Сезон 1: загружены 1 и 3 (из 5). Сезон 2: загружена только 1 (из 3).
var e11 = show.AddEpisode(Guid.NewGuid());
e11.SetNumbers(1, 1);
var e13 = show.AddEpisode(Guid.NewGuid());
e13.SetNumbers(1, 3);
var e21 = show.AddEpisode(Guid.NewGuid());
e21.SetNumbers(2, 1);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
var provider = Substitute.For<IMetadataProvider>();
provider
.GetSeasonEpisodeCountAsync("123", 1, Arg.Any<CancellationToken>())
.Returns(5);
provider
.GetSeasonEpisodeCountAsync("123", 2, Arg.Any<CancellationToken>())
.Returns(3);
var resolver = Substitute.For<IMetadataProviderResolver>();
resolver.Resolve("tmdb").Returns(provider);
await using var db = fixture.New();
var result = await new FindMissingEpisodesQueryHandler(db, resolver).Handle(
new FindMissingEpisodesQuery(show.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
var s1 = result.Value.Seasons.Single(x => x.Season == 1);
Assert.Equal(5, s1.Expected);
Assert.Equal(2, s1.Loaded);
Assert.Equal([2, 4, 5], s1.Missing);
var s2 = result.Value.Seasons.Single(x => x.Season == 2);
Assert.Equal([2, 3], s2.Missing);
}
[Fact]
public async Task Fails_WhenNoLinkedSource()
{
var fixture = new TestDb();
var show = Show.Create("A", ShowKind.Series);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
var resolver = Substitute.For<IMetadataProviderResolver>();
await using var db = fixture.New();
var result = await new FindMissingEpisodesQueryHandler(db, resolver).Handle(
new FindMissingEpisodesQuery(show.Id),
CancellationToken.None
);
Assert.False(result.IsSuccess);
}
}