Files
FictionArchive/FictionArchive.Service.TranslationService.Tests/Resilience/TranslationResiliencePipelineTests.cs
gamer147 d5a9529d6f
Some checks failed
CI / build-backend (pull_request) Failing after 1m48s
CI / build-frontend (pull_request) Successful in 1m27s
[FA-misc] Translation engine work
2026-08-25 10:38:27 -04:00

128 lines
3.5 KiB
C#

using DeepL;
using FictionArchive.Service.TranslationService.Services.Resilience;
using FluentAssertions;
using Polly;
using Xunit;
namespace FictionArchive.Service.TranslationService.Tests.Resilience;
public class TranslationResiliencePipelineTests
{
[Fact]
public async Task RetriesTransientHttpRequestException_ThenSucceeds()
{
var pipeline = TranslationResiliencePipeline.Build();
var attempts = 0;
var result = await pipeline.ExecuteAsync(async _ =>
{
attempts++;
if (attempts < 3)
{
throw new HttpRequestException("transient");
}
return await ValueTask.FromResult("ok");
});
attempts.Should().Be(3);
result.Should().Be("ok");
}
[Fact]
public async Task RetriesDeepLConnectionException()
{
var pipeline = TranslationResiliencePipeline.Build();
var attempts = 0;
var result = await pipeline.ExecuteAsync(async _ =>
{
attempts++;
if (attempts < 2)
{
throw new ConnectionException("transient", new HttpRequestException("inner"));
}
return await ValueTask.FromResult("ok");
});
attempts.Should().Be(2);
result.Should().Be("ok");
}
[Fact]
public async Task RetriesDeepLTooManyRequestsException()
{
var pipeline = TranslationResiliencePipeline.Build();
var attempts = 0;
var result = await pipeline.ExecuteAsync(async _ =>
{
attempts++;
if (attempts < 2)
{
throw new TooManyRequestsException("rate limited");
}
return await ValueTask.FromResult("ok");
});
attempts.Should().Be(2);
result.Should().Be("ok");
}
[Fact]
public async Task DoesNotRetryNonTransientException()
{
var pipeline = TranslationResiliencePipeline.Build();
var attempts = 0;
var act = async () => await pipeline.ExecuteAsync(async _ =>
{
attempts++;
throw new ArgumentException("permanent");
#pragma warning disable CS0162
return await ValueTask.FromResult("never");
#pragma warning restore CS0162
});
await act.Should().ThrowAsync<ArgumentException>();
attempts.Should().Be(1);
}
[Fact]
public async Task DoesNotRetryDeepLAuthorizationException()
{
var pipeline = TranslationResiliencePipeline.Build();
var attempts = 0;
var act = async () => await pipeline.ExecuteAsync(async _ =>
{
attempts++;
throw new AuthorizationException("bad key");
#pragma warning disable CS0162
return await ValueTask.FromResult("never");
#pragma warning restore CS0162
});
await act.Should().ThrowAsync<AuthorizationException>();
attempts.Should().Be(1);
}
[Fact]
public async Task ExhaustsRetryBudget_ThenRethrows()
{
var pipeline = TranslationResiliencePipeline.Build();
var attempts = 0;
var act = async () => await pipeline.ExecuteAsync(async _ =>
{
attempts++;
throw new HttpRequestException("always transient");
#pragma warning disable CS0162
return await ValueTask.FromResult("never");
#pragma warning restore CS0162
});
await act.Should().ThrowAsync<HttpRequestException>();
attempts.Should().Be(4); // initial attempt + 3 retries
}
}