2020-05-12 16:53:17 +01:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Net;
|
2022-11-18 11:30:09 +00:00
|
|
|
using System.Threading;
|
2020-05-12 16:53:17 +01:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Moq;
|
|
|
|
using NUnit.Framework;
|
|
|
|
using SpotifyAPI.Web.Http;
|
|
|
|
|
|
|
|
namespace SpotifyAPI.Web
|
|
|
|
{
|
|
|
|
[TestFixture]
|
|
|
|
public class SimpleRetryHandlerTest
|
|
|
|
{
|
2020-07-03 21:59:39 +01:00
|
|
|
[Test]
|
|
|
|
public void HandleRetry_WorksWithLowerCaseHeader()
|
|
|
|
{
|
|
|
|
var setup = new Setup();
|
|
|
|
setup.Response.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.TooManyRequests);
|
|
|
|
setup.Response.SetupGet(r => r.Headers).Returns(new Dictionary<string, string> {
|
|
|
|
{ "retry-after", "50" }
|
|
|
|
});
|
|
|
|
|
|
|
|
var retryCalled = 0;
|
2022-11-18 11:30:09 +00:00
|
|
|
setup.Retry = (IRequest request, CancellationToken ct) =>
|
2020-07-03 21:59:39 +01:00
|
|
|
{
|
|
|
|
retryCalled++;
|
|
|
|
return Task.FromResult(setup.Response.Object);
|
|
|
|
};
|
|
|
|
|
|
|
|
var handler = new SimpleRetryHandler(setup.Sleep.Object)
|
|
|
|
{
|
|
|
|
TooManyRequestsConsumesARetry = true,
|
|
|
|
RetryTimes = 1
|
|
|
|
};
|
|
|
|
Assert.DoesNotThrowAsync(async () =>
|
|
|
|
{
|
|
|
|
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
|
|
|
|
});
|
|
|
|
|
2024-02-10 10:41:47 +00:00
|
|
|
Assert.That(1, Is.EqualTo(retryCalled));
|
2020-07-03 21:59:39 +01:00
|
|
|
}
|
|
|
|
|
2020-05-12 16:53:17 +01:00
|
|
|
[Test]
|
|
|
|
public async Task HandleRetry_TooManyRequestsWithNoSuccess()
|
|
|
|
{
|
2020-05-12 18:57:29 +01:00
|
|
|
var setup = new Setup();
|
|
|
|
setup.Response.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.TooManyRequests);
|
|
|
|
setup.Response.SetupGet(r => r.Headers).Returns(new Dictionary<string, string> {
|
2020-05-12 16:53:17 +01:00
|
|
|
{ "Retry-After", "50" }
|
|
|
|
});
|
|
|
|
|
|
|
|
var retryCalled = 0;
|
2022-11-18 11:30:09 +00:00
|
|
|
setup.Retry = (IRequest request, CancellationToken ct) =>
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
retryCalled++;
|
2020-05-12 18:57:29 +01:00
|
|
|
return Task.FromResult(setup.Response.Object);
|
|
|
|
};
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2020-05-12 18:57:29 +01:00
|
|
|
var handler = new SimpleRetryHandler(setup.Sleep.Object)
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
TooManyRequestsConsumesARetry = true,
|
|
|
|
RetryTimes = 2
|
|
|
|
};
|
2020-05-12 18:57:29 +01:00
|
|
|
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2024-02-10 10:41:47 +00:00
|
|
|
Assert.That(2, Is.EqualTo(retryCalled));
|
|
|
|
Assert.That(setup.Response.Object, Is.EqualTo(response));
|
2020-06-03 22:57:28 +01:00
|
|
|
setup.Sleep.Verify(s => s(TimeSpan.FromSeconds(50)), Times.Exactly(2));
|
2020-05-12 16:53:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public async Task HandleRetry_TooManyRetriesWithSuccess()
|
|
|
|
{
|
2020-05-12 18:57:29 +01:00
|
|
|
var setup = new Setup();
|
|
|
|
setup.Response.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.TooManyRequests);
|
|
|
|
setup.Response.SetupGet(r => r.Headers).Returns(new Dictionary<string, string> {
|
2020-05-12 16:53:17 +01:00
|
|
|
{ "Retry-After", "50" }
|
|
|
|
});
|
2020-05-12 18:57:29 +01:00
|
|
|
|
2020-05-12 16:53:17 +01:00
|
|
|
var successResponse = new Mock<IResponse>();
|
|
|
|
successResponse.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.OK);
|
|
|
|
|
|
|
|
var retryCalled = 0;
|
2022-11-18 11:30:09 +00:00
|
|
|
setup.Retry = (request, ct) =>
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
retryCalled++;
|
|
|
|
return Task.FromResult(successResponse.Object);
|
2020-05-12 18:57:29 +01:00
|
|
|
};
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2020-05-12 18:57:29 +01:00
|
|
|
var handler = new SimpleRetryHandler(setup.Sleep.Object)
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
TooManyRequestsConsumesARetry = true,
|
|
|
|
RetryTimes = 10
|
|
|
|
};
|
2020-05-12 18:57:29 +01:00
|
|
|
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2024-02-10 10:41:47 +00:00
|
|
|
Assert.That(1, Is.EqualTo(retryCalled));
|
|
|
|
Assert.That(successResponse.Object, Is.EqualTo(response));
|
2020-06-03 22:57:28 +01:00
|
|
|
setup.Sleep.Verify(s => s(TimeSpan.FromSeconds(50)), Times.Once);
|
2020-05-12 16:53:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public async Task HandleRetry_TooManyRetriesWithSuccessNoConsume()
|
|
|
|
{
|
2020-05-12 18:57:29 +01:00
|
|
|
var setup = new Setup();
|
|
|
|
setup.Response.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.TooManyRequests);
|
|
|
|
setup.Response.SetupGet(r => r.Headers).Returns(new Dictionary<string, string> {
|
2020-05-12 16:53:17 +01:00
|
|
|
{ "Retry-After", "50" }
|
|
|
|
});
|
|
|
|
var successResponse = new Mock<IResponse>();
|
|
|
|
successResponse.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.OK);
|
|
|
|
|
|
|
|
var retryCalled = 0;
|
2022-11-18 11:30:09 +00:00
|
|
|
setup.Retry = (IRequest request, CancellationToken ct) =>
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
retryCalled++;
|
|
|
|
return Task.FromResult(successResponse.Object);
|
2020-05-12 18:57:29 +01:00
|
|
|
};
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2020-05-12 18:57:29 +01:00
|
|
|
var handler = new SimpleRetryHandler(setup.Sleep.Object)
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
TooManyRequestsConsumesARetry = false,
|
|
|
|
RetryTimes = 0
|
|
|
|
};
|
2020-05-12 18:57:29 +01:00
|
|
|
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2024-02-10 10:41:47 +00:00
|
|
|
Assert.That(1, Is.EqualTo(retryCalled));
|
|
|
|
Assert.That(successResponse.Object, Is.EqualTo(response));
|
2020-06-03 22:57:28 +01:00
|
|
|
setup.Sleep.Verify(s => s(TimeSpan.FromSeconds(50)), Times.Once);
|
2020-05-12 16:53:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public async Task HandleRetry_ServerErrors()
|
|
|
|
{
|
2020-05-12 18:57:29 +01:00
|
|
|
var setup = new Setup();
|
|
|
|
setup.Response.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.BadGateway);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
|
|
|
var retryCalled = 0;
|
2022-11-18 11:30:09 +00:00
|
|
|
setup.Retry = (request, ct) =>
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
retryCalled++;
|
2020-05-12 18:57:29 +01:00
|
|
|
return Task.FromResult(setup.Response.Object);
|
|
|
|
};
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2020-05-12 18:57:29 +01:00
|
|
|
var handler = new SimpleRetryHandler(setup.Sleep.Object)
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
TooManyRequestsConsumesARetry = true,
|
|
|
|
RetryTimes = 10,
|
2020-06-03 18:12:12 +01:00
|
|
|
RetryAfter = TimeSpan.FromMilliseconds(50)
|
2020-05-12 16:53:17 +01:00
|
|
|
};
|
2020-05-12 18:57:29 +01:00
|
|
|
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2024-02-10 10:41:47 +00:00
|
|
|
Assert.That(10, Is.EqualTo(retryCalled));
|
|
|
|
Assert.That(setup.Response.Object, Is.EqualTo(response));
|
2020-06-03 18:12:12 +01:00
|
|
|
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(10));
|
2020-05-12 16:53:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public async Task HandleRetry_DirectSuccess()
|
|
|
|
{
|
2020-05-12 18:57:29 +01:00
|
|
|
var setup = new Setup();
|
|
|
|
setup.Response.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.OK);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
|
|
|
var retryCalled = 0;
|
2022-11-18 11:30:09 +00:00
|
|
|
setup.Retry = (request, ct) =>
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
retryCalled++;
|
2020-05-12 18:57:29 +01:00
|
|
|
return Task.FromResult(setup.Response.Object);
|
|
|
|
};
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2020-05-12 18:57:29 +01:00
|
|
|
var handler = new SimpleRetryHandler(setup.Sleep.Object)
|
2020-05-12 16:53:17 +01:00
|
|
|
{
|
|
|
|
TooManyRequestsConsumesARetry = true,
|
|
|
|
RetryTimes = 10,
|
2020-06-03 18:12:12 +01:00
|
|
|
RetryAfter = TimeSpan.FromMilliseconds(50)
|
2020-05-12 16:53:17 +01:00
|
|
|
};
|
2020-05-12 18:57:29 +01:00
|
|
|
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
|
2020-05-12 16:53:17 +01:00
|
|
|
|
2024-02-10 10:41:47 +00:00
|
|
|
Assert.That(0, Is.EqualTo(retryCalled));
|
|
|
|
Assert.That(setup.Response.Object, Is.EqualTo(response));
|
2020-06-03 18:12:12 +01:00
|
|
|
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(0));
|
2020-05-12 18:57:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private class Setup
|
|
|
|
{
|
2020-06-03 18:12:12 +01:00
|
|
|
public Mock<Func<TimeSpan, Task>> Sleep { get; set; } = new Mock<Func<TimeSpan, Task>>();
|
2020-05-12 18:57:29 +01:00
|
|
|
public Mock<IResponse> Response { get; set; } = new Mock<IResponse>();
|
|
|
|
public Mock<IRequest> Request { get; set; } = new Mock<IRequest>();
|
2020-06-03 16:44:13 +01:00
|
|
|
public IRetryHandler.RetryFunc Retry { get; set; }
|
2020-05-12 16:53:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|