Added inital job entity and services. Added released works API integration.
All checks were successful
ci / build-test (push) Successful in 2m21s
ci / publish-image (push) Successful in 2m19s

This commit is contained in:
2026-03-27 01:32:39 -04:00
parent 1c016ac62e
commit d9e421178f
36 changed files with 5596 additions and 43 deletions

View File

@@ -1,9 +1,60 @@
namespace JSMR.Tests.Http;
using Microsoft.AspNetCore.WebUtilities;
using Shouldly;
namespace JSMR.Tests.Http;
internal sealed class FakeHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> handler) : HttpMessageHandler
{
public List<HttpRequestMessage> Requests { get; } = [];
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Requests.Add(request);
return Task.FromResult(handler(request));
}
}
}
public static class HttpRequestMessageAssertions
{
public static void ShouldHavePath(this HttpRequestMessage request, string expectedPath)
{
request.RequestUri.ShouldNotBeNull();
request.RequestUri!.AbsolutePath.ShouldBe(expectedPath);
}
public static void ShouldHaveQueryParam(this HttpRequestMessage request, string key, string expectedValue)
{
request.RequestUri.ShouldNotBeNull();
var query = QueryHelpers.ParseQuery(request.RequestUri!.Query);
query[key].ToString().ShouldBe(expectedValue);
}
}
//internal sealed class FakeHttpMessageHandler : HttpMessageHandler
//{
// private readonly Func<HttpRequestMessage, CancellationToken, HttpResponseMessage> _handler;
// public List<HttpRequestMessage> Requests { get; } = [];
// public FakeHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, HttpResponseMessage> handler)
// {
// _handler = handler;
// }
// public FakeHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> handler)
// : this((request, _) => handler(request))
// {
// }
// protected override Task<HttpResponseMessage> SendAsync(
// HttpRequestMessage request,
// CancellationToken cancellationToken)
// {
// Requests.Add(request);
// HttpResponseMessage response = _handler(request, cancellationToken);
// return Task.FromResult(response);
// }
//}

View File

@@ -1,9 +1,12 @@
using JSMR.Application.Integrations.DLSite.Models;
using JSMR.Application.Enums;
using JSMR.Application.Integrations.DLSite.Models;
using JSMR.Application.Integrations.DLSite.Models.ReleasedWorks;
using JSMR.Domain.Enums;
using JSMR.Domain.ValueObjects;
using JSMR.Infrastructure.Integrations.DLSite;
using JSMR.Infrastructure.Integrations.DLSite.Mapping;
using JSMR.Infrastructure.Integrations.DLSite.Models;
using JSMR.Infrastructure.Integrations.DLSite.Models.NewWorks;
using JSMR.Tests.Http;
using JSMR.Tests.Utilities;
using Microsoft.Extensions.Logging;
@@ -21,11 +24,11 @@ public class DLSiteClientTests
return await ResourceHelper.ReadAsync($"JSMR.Tests.Integrations.DLSite.{resourceName}");
}
private static async Task<DLSiteClient> GetDLSiteClientThatReturns(string resourceName)
private static async Task<(DLSiteClient Client, FakeHttpMessageHandler Handler)> GetDLSiteClientThatReturns(string resourceName)
{
string content = await ReadJsonResourceAsync(resourceName);
FakeHttpMessageHandler handler = new(request =>
FakeHttpMessageHandler handler = new(_ =>
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
@@ -41,13 +44,27 @@ public class DLSiteClientTests
var logger = Substitute.For<ILogger<DLSiteClient>>();
var client = new DLSiteClient(httpClient, logger);
return client;
return (client, handler);
}
[Fact]
public async Task GetVoiceWorkDetailsAsync_Should_Call_Expected_Request()
{
var (client, handler) = await GetDLSiteClientThatReturns("Product-Info.json");
await client.GetVoiceWorkDetailsAsync(["RJ01230163", "RJ01536422"], CancellationToken.None);
handler.Requests.Count.ShouldBe(1);
HttpRequestMessage httpRequest = handler.Requests[0];
httpRequest.Method.ShouldBe(HttpMethod.Get);
httpRequest.ShouldHavePath("/maniax/product/info/ajax");
httpRequest.ShouldHaveQueryParam("product_id", "RJ01230163,RJ01536422");
}
[Fact]
public async Task Deserialize_Product_Info_Collection()
{
var client = await GetDLSiteClientThatReturns("Product-Info.json");
var (client, handler) = await GetDLSiteClientThatReturns("Product-Info.json");
var result = await client.GetVoiceWorkDetailsAsync(["RJ01230163", "RJ01536422"], CancellationToken.None);
result.Count.ShouldBe(2);
@@ -144,4 +161,78 @@ public class DLSiteClientTests
secondVoiceWorkDetails.Translation.Language.ShouldBe(Language.English);
secondVoiceWorkDetails.Translation.Kind.ShouldBe(TranslationKind.Official | TranslationKind.Recommended);
}
[Fact]
public async Task GetReleasedWorksAsync_Should_Call_Expected_Request()
{
var (client, handler) = await GetDLSiteClientThatReturns("Released-Works.json");
ReleasedWorksRequest request = new(Locale.English, new(2025, 1, 1), 1);
await client.GetReleasedWorksAsync(request, CancellationToken.None);
handler.Requests.Count.ShouldBe(1);
HttpRequestMessage httpRequest = handler.Requests[0];
httpRequest.Method.ShouldBe(HttpMethod.Get);
httpRequest.ShouldHavePath("/maniax/new/work/api");
httpRequest.ShouldHaveQueryParam("locale", "en-us");
httpRequest.ShouldHaveQueryParam("date", "2025-01-01");
httpRequest.ShouldHaveQueryParam("period", "1");
}
[Fact]
public async Task Map_Basic_Released_Work_Collection()
{
NewWorksApiResponse response = new()
{
Meta = new()
{
Code = 200
},
Data = new()
{
Products =
[
new()
{
Id = "RG1",
Name = "The Title",
NameMasked = "Masked Title",
Description = "The description",
DescriptionMasked = "The masked description"
}
]
}
};
ReleasedWorksCollection collection = DLSiteReleasedWorksMapper.Map(response);
collection.Count.ShouldBe(1);
collection.ShouldContainKey("RG1");
ReleasedWork releasedWork = collection["RG1"];
releasedWork.ProductId.ShouldBe("RG1");
releasedWork.Title.ShouldBe("The Title");
releasedWork.MaskedTitle.ShouldBe("Masked Title");
releasedWork.Description.ShouldBe("The description");
releasedWork.MaskedDescription.ShouldBe("The masked description");
}
[Fact]
public async Task Deserialize_Released_Work_Collection()
{
var (client, handler) = await GetDLSiteClientThatReturns("Released-Works.json");
var request = new ReleasedWorksRequest(Locale.English, new(2025, 1, 1), 1);
var result = await client.GetReleasedWorksAsync(request, CancellationToken.None);
result.Count.ShouldBe(13);
result.ShouldContainKey("RJ01588345");
ReleasedWork releasedWork = result["RJ01588345"];
releasedWork.Title.ShouldBe("魔都一贅肉のスゴイデブ");
releasedWork.MaskedTitle.ShouldBe("魔都一贅肉のスゴイデブ");
releasedWork.Description.ShouldBe("圧巻の超連続・激太りご褒美パラダイス、ここに全24ページ描き下ろし太→激太への肥満化シークエンスが11作収録餌付け、自己肥育、お風呂、縛り…などなど多種多様の太り方でご褒美を堪能せよ");
releasedWork.MaskedDescription.ShouldBe("圧巻の超連続・激太りご褒美パラダイス、ここに全24ページ描き下ろし太→激太への肥満化シークエンスが11作収録餌付け、自己肥育、お風呂、縛り…などなど多種多様の太り方でご褒美を堪能せよ");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@
<EmbeddedResource Include="Integrations\Chobit\Sample-Chobit-Result-No-Data.jsonp" />
<EmbeddedResource Include="Integrations\Chobit\Sample-Chobit-Result-Collection.jsonp" />
<EmbeddedResource Include="Integrations\Chobit\Sample-Chobit-Result.jsonp" />
<EmbeddedResource Include="Integrations\DLSite\Released-Works.json" />
<EmbeddedResource Include="Integrations\DLSite\Product-Info.json" />
<EmbeddedResource Include="Scanning\English-Page-Updated.html" />
<EmbeddedResource Include="Scanning\Japanese-Page-Updated.html" />
@@ -29,6 +30,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />

View File

@@ -1,6 +1,6 @@
using JSMR.Application.Integrations.Chobit.Models;
using JSMR.Application.Integrations.DLSite.Models;
using JSMR.Application.Integrations.Ports;
using JSMR.Application.Integrations.DLSite.Ports;
using JSMR.Application.Scanning.Contracts;
using JSMR.Application.Scanning.Ports;
using JSMR.Infrastructure.Http;