/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/HttpClientFactory/Polly/test/PolicyHttpMessageHandlerTest.cs
463 строки
15 KB
Pranav K
Fixup whitespace issues from top-level statement change (#39161)
23 дек 2021, 09:35
Не верифицирован
23 дек 2021, 09:35
c65dac7
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Internal; using Polly; using Polly.Extensions.Http; using Polly.Timeout; using Xunit; namespace Microsoft.Extensions.Http; public class PolicyHttpMessageHandlerTest { [Fact] public async Task SendAsync_StaticPolicy_PolicyTriggers_CanReexecuteSendAsync() { // Arrange var policy = Policy<HttpResponseMessage> .Handle<HttpRequestException>() .RetryAsync(retryCount: 5); var handler = new TestPolicyHttpMessageHandler(policy); var callCount = 0; var expected = new HttpResponseMessage(); handler.OnSendAsync = (req, c, ct) => { if (callCount == 0) { callCount++; throw new HttpRequestException(); } else if (callCount == 1) { callCount++; return Task.FromResult(expected); } else { throw new InvalidOperationException(); } }; // Act var response = await handler.SendAsync(new HttpRequestMessage(), CancellationToken.None); // Assert Assert.Equal(2, callCount); Assert.Same(expected, response); } [Fact] public async Task SendAsync_DynamicPolicy_PolicyTriggers_CanReexecuteSendAsync() { // Arrange var policy = Policy<HttpResponseMessage> .Handle<HttpRequestException>() .RetryAsync(retryCount: 5); var expectedRequest = new HttpRequestMessage(); HttpRequestMessage policySelectorRequest = null; var handler = new TestPolicyHttpMessageHandler((req) => { policySelectorRequest = req; return policy; }); var callCount = 0; var expected = new HttpResponseMessage(); handler.OnSendAsync = (req, c, ct) => { if (callCount == 0) { callCount++; throw new HttpRequestException(); } else if (callCount == 1) { callCount++; return Task.FromResult(expected); } else { throw new InvalidOperationException(); } }; // Act var response = await handler.SendAsync(expectedRequest, CancellationToken.None); // Assert Assert.Equal(2, callCount); Assert.Same(expected, response); Assert.Same(expectedRequest, policySelectorRequest); } [Fact] public async Task SendAsync_StaticPolicy_PolicyTriggers_CanReexecuteSendAsync_FirstResponseDisposed() { // Arrange var policy = HttpPolicyExtensions.HandleTransientHttpError() .RetryAsync(retryCount: 1); var callCount = 0; var fakeContent = new FakeContent(); var firstResponse = new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.InternalServerError, Content = fakeContent, }; var expected = new HttpResponseMessage(); var handler = new PolicyHttpMessageHandler(policy); handler.InnerHandler = new TestHandler() { OnSendAsync = (req, ct) => { if (callCount == 0) { callCount++; return Task.FromResult(firstResponse); } else if (callCount == 1) { callCount++; return Task.FromResult(expected); } else { throw new InvalidOperationException(); } } }; var invoke = new HttpMessageInvoker(handler); // Act var response = await invoke.SendAsync(new HttpRequestMessage(), CancellationToken.None); // Assert Assert.Equal(2, callCount); Assert.Same(expected, response); Assert.True(fakeContent.Disposed); } [Fact] public async Task MultipleHandlers_CanReexecuteSendAsync_FirstResponseDisposed() { // Arrange var policy1 = HttpPolicyExtensions.HandleTransientHttpError() .RetryAsync(retryCount: 1); var policy2 = HttpPolicyExtensions.HandleTransientHttpError() .CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 2, durationOfBreak: TimeSpan.FromSeconds(10)); var callCount = 0; var fakeContent = new FakeContent(); var firstResponse = new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.InternalServerError, Content = fakeContent, }; var expected = new HttpResponseMessage(); var handler1 = new PolicyHttpMessageHandler(policy1); var handler2 = new PolicyHttpMessageHandler(policy2); handler1.InnerHandler = handler2; handler2.InnerHandler = new TestHandler() { OnSendAsync = (req, ct) => { if (callCount == 0) { callCount++; return Task.FromResult(firstResponse); } else if (callCount == 1) { callCount++; return Task.FromResult(expected); } else { throw new InvalidOperationException(); } } }; var invoke = new HttpMessageInvoker(handler1); // Act var response = await invoke.SendAsync(new HttpRequestMessage(), CancellationToken.None); // Assert Assert.Equal(2, callCount); Assert.Same(expected, response); Assert.True(fakeContent.Disposed); } [Fact] public async Task SendAsync_DynamicPolicy_PolicySelectorReturnsNull_ThrowsException() { // Arrange var handler = new TestPolicyHttpMessageHandler((req) => { return null; }); var expected = new HttpResponseMessage(); // Act var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => { await handler.SendAsync(new HttpRequestMessage(), CancellationToken.None); }); // Assert Assert.Equal( "The 'policySelector' function must return a non-null policy instance. To create a policy that takes no action, use 'Policy.NoOpAsync<HttpResponseMessage>()'.", exception.Message); } [Fact] public async Task SendAsync_PolicyCancellation_CanTriggerRequestCancellation() { // Arrange var policy = Policy<HttpResponseMessage> .Handle<TimeoutRejectedException>() // Handle timeouts by retrying .RetryAsync(retryCount: 5) .WrapAsync(Policy .TimeoutAsync<HttpResponseMessage>(TimeSpan.FromMilliseconds(50)) // Apply a 50ms timeout .WrapAsync(Policy.NoOpAsync<HttpResponseMessage>())); var handler = new TestPolicyHttpMessageHandler(policy); var @event = new ManualResetEventSlim(initialState: false); var callCount = 0; var expected = new HttpResponseMessage(); handler.OnSendAsync = (req, c, ct) => { // The inner cancellation token is created by Polly, it will trigger the timeout. Assert.True(ct.CanBeCanceled); if (callCount == 0) { callCount++; @event.Wait(ct); throw null; // unreachable, previous line should throw } else if (callCount == 1) { callCount++; return Task.FromResult(expected); } else { throw new InvalidOperationException(); } }; // Act var response = await handler.SendAsync(new HttpRequestMessage(), CancellationToken.None); // Assert Assert.Equal(2, callCount); Assert.Same(expected, response); } [Fact] public async Task SendAsync_NoContextSet_CreatesNewContext() { // Arrange var policy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)); var handler = new TestPolicyHttpMessageHandler(policy); Context context = null; var expected = new HttpResponseMessage(); handler.OnSendAsync = (req, c, ct) => { context = c; Assert.NotNull(context); Assert.Same(context, req.GetPolicyExecutionContext()); return Task.FromResult(expected); }; var request = new HttpRequestMessage(); // Act var response = await handler.SendAsync(request, CancellationToken.None); // Assert Assert.NotNull(context); Assert.Null(request.GetPolicyExecutionContext()); // We clean up the context if it was generated by the handler rather than caller supplied. Assert.Same(expected, response); } [Fact] public async Task SendAsync_ExistingContext_ReusesContext() { // Arrange var policy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)); var handler = new TestPolicyHttpMessageHandler(policy); var expected = new HttpResponseMessage(); var expectedContext = new Context(Guid.NewGuid().ToString()); Context context = null; handler.OnSendAsync = (req, c, ct) => { context = c; Assert.NotNull(c); Assert.Same(c, req.GetPolicyExecutionContext()); return Task.FromResult(expected); }; var request = new HttpRequestMessage(); request.SetPolicyExecutionContext(expectedContext); // Act var response = await handler.SendAsync(request, CancellationToken.None); // Assert Assert.Same(expectedContext, context); Assert.Same(expectedContext, request.GetPolicyExecutionContext()); // We don't clean up the context if the caller or earlier delegating handlers had supplied it. Assert.Same(expected, response); } [Fact] public async Task SendAsync_NoContextSet_DynamicPolicySelectorThrows_CleansUpContext() { // Arrange var handler = new TestPolicyHttpMessageHandler((req) => { throw new InvalidOperationException(); }); var request = new HttpRequestMessage(); // Act var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => { await handler.SendAsync(request, CancellationToken.None); }); // Assert Assert.Null(request.GetPolicyExecutionContext()); // We do clean up a context we generated, when the policy selector throws. } [Fact] public async Task SendAsync_NoContextSet_RequestThrows_CleansUpContext() { // Arrange var policy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)); var handler = new TestPolicyHttpMessageHandler(policy); Context context = null; handler.OnSendAsync = (req, c, ct) => { context = c; throw new OperationCanceledException(); }; var request = new HttpRequestMessage(); // Act var exception = await Assert.ThrowsAsync<OperationCanceledException>(async () => { await handler.SendAsync(request, CancellationToken.None); }); // Assert Assert.NotNull(context); // The handler did generate a context for the execution. Assert.Null(request.GetPolicyExecutionContext()); // We do clean up a context we generated, when the execution throws. } [Fact] public void SendAsync_WorksInSingleThreadedSyncContext() { // Arrange var policy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)); var handler = new TestPolicyHttpMessageHandler(policy); handler.OnSendAsync = async (req, c, ct) => { await Task.Delay(1).ConfigureAwait(false); return null; }; var hangs = true; // Act using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3))) { var token = cts.Token; token.Register(() => throw new OperationCanceledException(token)); SingleThreadedSynchronizationContext.Run(() => { // Act var request = new HttpRequestMessage(); handler.SendAsync(request, CancellationToken.None).GetAwaiter().GetResult(); hangs = false; }); } // Assert Assert.False(hangs); } private class TestPolicyHttpMessageHandler : PolicyHttpMessageHandler { public Func<HttpRequestMessage, Context, CancellationToken, Task<HttpResponseMessage>> OnSendAsync { get; set; } public TestPolicyHttpMessageHandler(IAsyncPolicy<HttpResponseMessage> policy) : base(policy) { } public TestPolicyHttpMessageHandler(Func<HttpRequestMessage, IAsyncPolicy<HttpResponseMessage>> policySelector) : base(policySelector) { } public new Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken); } protected override Task<HttpResponseMessage> SendCoreAsync(HttpRequestMessage request, Context context, CancellationToken cancellationToken) { Assert.NotNull(OnSendAsync); return OnSendAsync(request, context, cancellationToken); } } private class TestHandler : HttpMessageHandler { public Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> OnSendAsync { get; set; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Assert.NotNull(OnSendAsync); return OnSendAsync(request, cancellationToken); } } private class FakeContent : StringContent { public FakeContent() : base("hello world") { } public bool Disposed { get; set; } protected override void Dispose(bool disposing) { Disposed = true; base.Dispose(disposing); } } }