112 lines
4.1 KiB
C#
112 lines
4.1 KiB
C#
using System.Net;
|
|
|
|
namespace Zcbot.WindowsNode.Tests;
|
|
|
|
public sealed class TransferRetryPolicyTests
|
|
{
|
|
[Theory]
|
|
[InlineData(HttpStatusCode.Unauthorized)]
|
|
[InlineData(HttpStatusCode.Forbidden)]
|
|
public void AuthenticationFailuresAreClassifiedSeparately(HttpStatusCode statusCode)
|
|
{
|
|
Assert.Equal(
|
|
TransferFailureKind.Authentication,
|
|
TransferRetryPolicy.Classify(statusCode));
|
|
Assert.Null(TransferRetryPolicy.RetryDelay(HttpMethod.Get, 0, statusCode));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(HttpStatusCode.RequestTimeout)]
|
|
[InlineData(HttpStatusCode.TooManyRequests)]
|
|
[InlineData(HttpStatusCode.InternalServerError)]
|
|
[InlineData(HttpStatusCode.ServiceUnavailable)]
|
|
public void SafeDownloadsRetryTransientResponses(HttpStatusCode statusCode)
|
|
{
|
|
Assert.Equal(TransferFailureKind.Transient, TransferRetryPolicy.Classify(statusCode));
|
|
Assert.NotNull(TransferRetryPolicy.RetryDelay(HttpMethod.Get, 0, statusCode));
|
|
Assert.Null(TransferRetryPolicy.RetryDelay(HttpMethod.Post, 0, statusCode));
|
|
}
|
|
|
|
[Fact]
|
|
public void CancellationIsNeverClassifiedAsANetworkFailure()
|
|
{
|
|
Assert.Equal(
|
|
TransferFailureKind.Cancelled,
|
|
TransferRetryPolicy.Classify(new OperationCanceledException()));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HttpClientRetriesGetAndInjectsAuthenticationWithoutSecretsInTheUrl()
|
|
{
|
|
var handler = new SequenceHandler(
|
|
HttpStatusCode.ServiceUnavailable,
|
|
HttpStatusCode.OK);
|
|
using var client = new NodeHttpClient(CreateConfig(), handler);
|
|
|
|
using var response = await client.SendWithRetryAsync(
|
|
() => client.CreateRequest(HttpMethod.Get, "/v1/software-jobs/example/input"),
|
|
HttpCompletionOption.ResponseHeadersRead,
|
|
CancellationToken.None);
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.Equal(2, handler.RequestCount);
|
|
Assert.Equal("Bearer node-secret", handler.Authorization);
|
|
Assert.Equal("node-secret", CreateConfig().NodeToken);
|
|
Assert.DoesNotContain("node-secret", handler.RequestUri!.ToString());
|
|
Assert.StartsWith("zcbot-windows-node/", handler.UserAgent);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HttpClientDoesNotRetryAuthenticationFailure()
|
|
{
|
|
var handler = new SequenceHandler(HttpStatusCode.Unauthorized, HttpStatusCode.OK);
|
|
using var client = new NodeHttpClient(CreateConfig(), handler);
|
|
|
|
using var response = await client.SendWithRetryAsync(
|
|
() => client.CreateRequest(HttpMethod.Get, "/v1/software-jobs/example/input"),
|
|
HttpCompletionOption.ResponseHeadersRead,
|
|
CancellationToken.None);
|
|
|
|
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
|
Assert.Equal(1, handler.RequestCount);
|
|
}
|
|
|
|
private static NodeConfig CreateConfig() => new(
|
|
new Uri("https://node.example/"),
|
|
Guid.Parse("11111111-1111-1111-1111-111111111111"),
|
|
Guid.Parse("22222222-2222-2222-2222-222222222222"),
|
|
"test-node",
|
|
"node-secret",
|
|
30,
|
|
["origin.plot@v2"]);
|
|
|
|
private sealed class SequenceHandler(params HttpStatusCode[] statuses) : HttpMessageHandler
|
|
{
|
|
private int index;
|
|
|
|
internal int RequestCount { get; private set; }
|
|
|
|
internal string? Authorization { get; private set; }
|
|
|
|
internal Uri? RequestUri { get; private set; }
|
|
|
|
internal string UserAgent { get; private set; } = "";
|
|
|
|
protected override Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
RequestCount++;
|
|
Authorization = request.Headers.Authorization?.ToString();
|
|
RequestUri = request.RequestUri;
|
|
UserAgent = request.Headers.UserAgent.ToString();
|
|
var status = statuses[Math.Min(index++, statuses.Length - 1)];
|
|
return Task.FromResult(new HttpResponseMessage(status)
|
|
{
|
|
Content = new ByteArrayContent([]),
|
|
RequestMessage = request,
|
|
});
|
|
}
|
|
}
|
|
}
|