[FA-misc] Translation engine work
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user