128 lines
3.5 KiB
C#
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
|
|
}
|
|
}
|