using JSMR.Application.Enums; using JSMR.Application.Integrations.DLSite.Models.ReleasedWorks; using JSMR.Application.Integrations.DLSite.Ports; using JSMR.Application.Scanning.Contracts; using JSMR.Infrastructure.Scanning; using NSubstitute; using Shouldly; namespace JSMR.Tests.Unit; public class ReleasedWorksProviderTests { private readonly IDLSiteClient _dlsiteClient = Substitute.For(); [Fact] public async Task GetReleasedWorksAsync_WhenNoSalesDates_ReturnsEmptyAndDoesNotCallClient() { VoiceWorkScanResult scanResult = new( Works: [ new DLSiteWork { ProductId = "RJ001", SalesDate = null, ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" } ], EndOfResults: false ); ReleasedWorksProvider provider = new(_dlsiteClient); ReleasedWorksCollection result = await provider.GetReleasedWorksAsync(scanResult, TestContext.Current.CancellationToken); result.ShouldBeEmpty(); await _dlsiteClient.DidNotReceiveWithAnyArgs().GetReleasedWorksAsync(default!, TestContext.Current.CancellationToken); } [Fact] public async Task GetReleasedWorksAsync_WhenRangeIsUnder60Days_CallsClientOnce() { VoiceWorkScanResult scanResult = new( Works: [ new DLSiteWork { ProductId = "RJ001", SalesDate = new DateOnly(2024, 1, 10), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" }, new DLSiteWork { ProductId = "RJ002", SalesDate = new DateOnly(2024, 1, 20), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" } ], EndOfResults: false ); ReleasedWorksCollection apiResult = new() { ["RJ001"] = new ReleasedWork { ProductId = "RJ001", Title = "English title 1", Description = "Description", MaskedTitle = "English title 1", MaskedDescription = "Description", }, ["RJ002"] = new ReleasedWork { ProductId = "RJ002", Title = "English title 2", Description = "Description", MaskedTitle = "English title 2", MaskedDescription = "Description", } }; _dlsiteClient .GetReleasedWorksAsync(Arg.Any(), Arg.Any()) .Returns(apiResult); ReleasedWorksProvider provider = new(_dlsiteClient); ReleasedWorksCollection result = await provider.GetReleasedWorksAsync(scanResult, TestContext.Current.CancellationToken); result.Keys.ShouldBe(["RJ001", "RJ002"], ignoreOrder: true); await _dlsiteClient.Received(1).GetReleasedWorksAsync( Arg.Is(x => x.Locale == Locale.English && x.Date == new DateOnly(2024, 1, 20) && x.Period == 11), Arg.Any()); } [Fact] public async Task GetReleasedWorksAsync_WhenRangeExceeds60Days_SplitsIntoMultipleRequests() { VoiceWorkScanResult scanResult = new( Works: [ new DLSiteWork { ProductId = "RJ001", SalesDate = new DateOnly(2024, 1, 1), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" }, new DLSiteWork { ProductId = "RJ002", SalesDate = new DateOnly(2024, 3, 5), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" } ], EndOfResults: false ); _dlsiteClient .GetReleasedWorksAsync(Arg.Any(), Arg.Any()) .Returns([]); ReleasedWorksProvider provider = new(_dlsiteClient); await provider.GetReleasedWorksAsync(scanResult, TestContext.Current.CancellationToken); await _dlsiteClient.Received(1).GetReleasedWorksAsync( Arg.Is(x => x.Date == new DateOnly(2024, 2, 29) && x.Period == 60), Arg.Any()); await _dlsiteClient.Received(1).GetReleasedWorksAsync( Arg.Is(x => x.Date == new DateOnly(2024, 3, 5) && x.Period == 5), Arg.Any()); await _dlsiteClient.Received(2).GetReleasedWorksAsync( Arg.Any(), Arg.Any()); } [Fact] public async Task GetReleasedWorksAsync_FiltersOutProductsNotInScanResult() { VoiceWorkScanResult scanResult = new( Works: [ new DLSiteWork { ProductId = "RJ001", SalesDate = new DateOnly(2024, 1, 10), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" } ], EndOfResults: false ); ReleasedWorksCollection apiResult = new() { ["RJ001"] = new ReleasedWork { ProductId = "RJ001", Title = "Keep me", Description = "Description", MaskedTitle = "Keep me", MaskedDescription = "Description", }, ["RJ999"] = new ReleasedWork { ProductId = "RJ999", Title = "Ignore me", Description = "Description", MaskedTitle = "Ignore me", MaskedDescription = "Description", }, }; _dlsiteClient .GetReleasedWorksAsync(Arg.Any(), Arg.Any()) .Returns(apiResult); ReleasedWorksProvider provider = new(_dlsiteClient); ReleasedWorksCollection result = await provider.GetReleasedWorksAsync(scanResult, TestContext.Current.CancellationToken); result.Keys.ShouldBe(["RJ001"]); } [Fact] public async Task GetReleasedWorksAsync_WhenSameProductReturnedTwice_KeepsFirstResult() { VoiceWorkScanResult scanResult = new( Works: [ new DLSiteWork { ProductId = "RJ001", SalesDate = new DateOnly(2024, 1, 1), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" }, new DLSiteWork { ProductId = "RJ002", SalesDate = new DateOnly(2024, 3, 5), ProductName = "", MakerId = "", Maker = "", ImageUrl = "", SmallImageUrl = "" } ], EndOfResults: false ); _dlsiteClient .GetReleasedWorksAsync( Arg.Is(x => x.Period == 60), Arg.Any()) .Returns(new ReleasedWorksCollection { ["RJ001"] = new ReleasedWork { ProductId = "RJ001", Title = "First", Description = "Description", MaskedTitle = "First", MaskedDescription = "Description", } }); _dlsiteClient .GetReleasedWorksAsync( Arg.Is(x => x.Period == 5), Arg.Any()) .Returns(new ReleasedWorksCollection { ["RJ001"] = new ReleasedWork { ProductId = "RJ001", Title = "Second", Description = "Description", MaskedTitle = "Second", MaskedDescription = "Description", } }); ReleasedWorksProvider provider = new(_dlsiteClient); ReleasedWorksCollection result = await provider.GetReleasedWorksAsync(scanResult, TestContext.Current.CancellationToken); result["RJ001"].Title.ShouldBe("First"); } }