Updated logic for getting released work information (take 60 day period max limit into consideration).
This commit is contained in:
@@ -4,11 +4,8 @@ namespace JSMR.Application.Scanning.Contracts;
|
||||
|
||||
public class DLSiteWork
|
||||
{
|
||||
//public DLSiteWorkType Type { get; set; }
|
||||
//public DLSiteWorkCategory Category { get; set; }
|
||||
public required string ProductName { get; set; }
|
||||
public required string ProductId { get; set; }
|
||||
//public DateOnly? AnnouncedDate { get; set; }
|
||||
public DateOnly? ExpectedDate { get; set; }
|
||||
public DateOnly? SalesDate { get; set; }
|
||||
public int Downloads { get; set; }
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace JSMR.Infrastructure.Scanning;
|
||||
|
||||
public class ReleasedWorksProvider(IDLSiteClient dlsiteClient) : IReleasedWorksProvider
|
||||
{
|
||||
private const int MaxPeriodDays = 60;
|
||||
|
||||
public async Task<ReleasedWorksCollection> GetReleasedWorksAsync(VoiceWorkScanResult scanResult, CancellationToken cancellationToken)
|
||||
{
|
||||
DateOnly[] salesDates =
|
||||
@@ -20,20 +22,72 @@ public class ReleasedWorksProvider(IDLSiteClient dlsiteClient) : IReleasedWorksP
|
||||
if (salesDates.Length == 0)
|
||||
return [];
|
||||
|
||||
HashSet<string> productIds = [.. scanResult.Works.Select(x => x.ProductId)];
|
||||
|
||||
DateOnly minDate = salesDates.Min();
|
||||
DateOnly maxDate = salesDates.Max();
|
||||
|
||||
DateOnly requestDate = minDate.AddDays(-1);
|
||||
DateOnly requestEndDate = maxDate.AddDays(1);
|
||||
ReleasedWorksCollection collection = [];
|
||||
|
||||
int period = (requestEndDate.DayNumber - requestDate.DayNumber) + 1;
|
||||
DateOnly chunkStart = minDate;
|
||||
|
||||
ReleasedWorksRequest releasedWorksRequest = new(
|
||||
while (chunkStart <= maxDate)
|
||||
{
|
||||
int endDayNumber = Math.Min(chunkStart.DayNumber + MaxPeriodDays - 1, maxDate.DayNumber);
|
||||
DateOnly chunkEnd = DateOnly.FromDayNumber(endDayNumber);
|
||||
|
||||
int period = chunkEnd.DayNumber - chunkStart.DayNumber + 1;
|
||||
|
||||
ReleasedWorksRequest request = new(
|
||||
Locale: Locale.English,
|
||||
Date: requestEndDate,
|
||||
Period: period
|
||||
);
|
||||
Date: chunkEnd,
|
||||
Period: period);
|
||||
|
||||
return await dlsiteClient.GetReleasedWorksAsync(releasedWorksRequest, cancellationToken);
|
||||
ReleasedWorksCollection chunk = await dlsiteClient.GetReleasedWorksAsync(request, cancellationToken);
|
||||
|
||||
foreach (string productId in chunk.Keys)
|
||||
{
|
||||
if (productIds.Contains(productId) == false)
|
||||
continue;
|
||||
|
||||
if (collection.ContainsKey(productId))
|
||||
continue;
|
||||
|
||||
collection.Add(productId, chunk[productId]);
|
||||
}
|
||||
|
||||
chunkStart = chunkEnd.AddDays(1);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
//public async Task<ReleasedWorksCollection> GetReleasedWorksAsync(VoiceWorkScanResult scanResult, CancellationToken cancellationToken)
|
||||
//{
|
||||
// DateOnly[] salesDates =
|
||||
// [
|
||||
// .. scanResult.Works
|
||||
// .Where(x => x.SalesDate.HasValue)
|
||||
// .Select(x => x.SalesDate!.Value)
|
||||
// ];
|
||||
|
||||
// if (salesDates.Length == 0)
|
||||
// return [];
|
||||
|
||||
// DateOnly minDate = salesDates.Min();
|
||||
// DateOnly maxDate = salesDates.Max();
|
||||
|
||||
// DateOnly requestDate = minDate.AddDays(-1);
|
||||
// DateOnly requestEndDate = maxDate.AddDays(1);
|
||||
|
||||
// int period = (requestEndDate.DayNumber - requestDate.DayNumber) + 1;
|
||||
|
||||
// ReleasedWorksRequest releasedWorksRequest = new(
|
||||
// Locale: Locale.English,
|
||||
// Date: requestEndDate,
|
||||
// Period: period
|
||||
// );
|
||||
|
||||
// return await dlsiteClient.GetReleasedWorksAsync(releasedWorksRequest, cancellationToken);
|
||||
//}
|
||||
}
|
||||
288
JSMR.Tests/Unit/ReleasedWorksProviderTests.cs
Normal file
288
JSMR.Tests/Unit/ReleasedWorksProviderTests.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
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<IDLSiteClient>();
|
||||
|
||||
[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<ReleasedWorksRequest>(), Arg.Any<CancellationToken>())
|
||||
.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<ReleasedWorksRequest>(x =>
|
||||
x.Locale == Locale.English &&
|
||||
x.Date == new DateOnly(2024, 1, 20) &&
|
||||
x.Period == 11),
|
||||
Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[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<ReleasedWorksRequest>(), Arg.Any<CancellationToken>())
|
||||
.Returns([]);
|
||||
|
||||
ReleasedWorksProvider provider = new(_dlsiteClient);
|
||||
|
||||
await provider.GetReleasedWorksAsync(scanResult, TestContext.Current.CancellationToken);
|
||||
|
||||
await _dlsiteClient.Received(1).GetReleasedWorksAsync(
|
||||
Arg.Is<ReleasedWorksRequest>(x =>
|
||||
x.Date == new DateOnly(2024, 2, 29) &&
|
||||
x.Period == 60),
|
||||
Arg.Any<CancellationToken>());
|
||||
|
||||
await _dlsiteClient.Received(1).GetReleasedWorksAsync(
|
||||
Arg.Is<ReleasedWorksRequest>(x =>
|
||||
x.Date == new DateOnly(2024, 3, 5) &&
|
||||
x.Period == 5),
|
||||
Arg.Any<CancellationToken>());
|
||||
|
||||
await _dlsiteClient.Received(2).GetReleasedWorksAsync(
|
||||
Arg.Any<ReleasedWorksRequest>(),
|
||||
Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[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<ReleasedWorksRequest>(), Arg.Any<CancellationToken>())
|
||||
.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<ReleasedWorksRequest>(x => x.Period == 60),
|
||||
Arg.Any<CancellationToken>())
|
||||
.Returns(new ReleasedWorksCollection
|
||||
{
|
||||
["RJ001"] = new ReleasedWork
|
||||
{
|
||||
ProductId = "RJ001",
|
||||
Title = "First",
|
||||
Description = "Description",
|
||||
MaskedTitle = "First",
|
||||
MaskedDescription = "Description",
|
||||
}
|
||||
});
|
||||
|
||||
_dlsiteClient
|
||||
.GetReleasedWorksAsync(
|
||||
Arg.Is<ReleasedWorksRequest>(x => x.Period == 5),
|
||||
Arg.Any<CancellationToken>())
|
||||
.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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user