Updated logic for getting released work information (take 60 day period max limit into consideration).

This commit is contained in:
2026-05-04 01:52:25 -04:00
parent f6674e0382
commit abcc82437f
3 changed files with 351 additions and 12 deletions

View File

@@ -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; }

View File

@@ -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(
Locale: Locale.English,
Date: requestEndDate,
Period: period
);
while (chunkStart <= maxDate)
{
int endDayNumber = Math.Min(chunkStart.DayNumber + MaxPeriodDays - 1, maxDate.DayNumber);
DateOnly chunkEnd = DateOnly.FromDayNumber(endDayNumber);
return await dlsiteClient.GetReleasedWorksAsync(releasedWorksRequest, cancellationToken);
int period = chunkEnd.DayNumber - chunkStart.DayNumber + 1;
ReleasedWorksRequest request = new(
Locale: Locale.English,
Date: chunkEnd,
Period: period);
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);
//}
}

View 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");
}
}