159 lines
7.9 KiB
C#
Raw Normal View History

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Http.Tracing.Tracers
{
public class MessageHandlerTracerTest
{
[Fact]
public void SendAsync_Traces_And_Invokes_Inner()
{
// Arrange
HttpResponseMessage response = new HttpResponseMessage();
MockDelegatingHandler mockHandler = new MockDelegatingHandler((rqst, cancellation) =>
TaskHelpers.FromResult<HttpResponseMessage>(response));
TestTraceWriter traceWriter = new TestTraceWriter();
MessageHandlerTracer tracer = new MessageHandlerTracer(mockHandler, traceWriter);
MockHttpMessageHandler mockInnerHandler = new MockHttpMessageHandler((rqst, cancellation) =>
TaskHelpers.FromResult<HttpResponseMessage>(response));
tracer.InnerHandler = mockInnerHandler;
HttpRequestMessage request = new HttpRequestMessage();
TraceRecord[] expectedTraces = new TraceRecord[]
{
new TraceRecord(request, TraceCategories.MessageHandlersCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "SendAsync" },
new TraceRecord(request, TraceCategories.MessageHandlersCategory, TraceLevel.Info) { Kind = TraceKind.End, Operation = "SendAsync" }
};
MethodInfo method = typeof (DelegatingHandler).GetMethod("SendAsync",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance);
// Act
Task<HttpResponseMessage> task = method.Invoke(tracer, new object[] {request, CancellationToken.None}) as Task<HttpResponseMessage>;
HttpResponseMessage actualResponse = task.Result;
// Assert
Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer());
Assert.Same(response, actualResponse);
}
[Fact]
public void SendAsync_Traces_And_Throws_When_Inner_Throws()
{
// Arrange
InvalidOperationException exception = new InvalidOperationException("test");
MockDelegatingHandler mockHandler = new MockDelegatingHandler((rqst, cancellation) => { throw exception; });
TestTraceWriter traceWriter = new TestTraceWriter();
MessageHandlerTracer tracer = new MessageHandlerTracer(mockHandler, traceWriter);
// DelegatingHandlers require an InnerHandler to run. We create a mock one to simulate what
// would happen when a DelegatingHandler executing after the tracer throws.
MockHttpMessageHandler mockInnerHandler = new MockHttpMessageHandler((rqst, cancellation) => { throw exception; });
tracer.InnerHandler = mockInnerHandler;
HttpRequestMessage request = new HttpRequestMessage();
TraceRecord[] expectedTraces = new TraceRecord[]
{
new TraceRecord(request, TraceCategories.MessageHandlersCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "SendAsync" },
new TraceRecord(request, TraceCategories.MessageHandlersCategory, TraceLevel.Error) { Kind = TraceKind.End, Operation = "SendAsync" }
};
MethodInfo method = typeof(DelegatingHandler).GetMethod("SendAsync",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance);
// Act
Exception thrown =
Assert.Throws<TargetInvocationException>(
() => method.Invoke(tracer, new object[] {request, CancellationToken.None}));
// Assert
Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer());
Assert.Same(exception, thrown.InnerException);
Assert.Same(exception, traceWriter.Traces[1].Exception);
}
[Fact]
public void SendAsync_Traces_And_Faults_When_Inner_Faults()
{
// Arrange
InvalidOperationException exception = new InvalidOperationException("test");
TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
tcs.TrySetException(exception);
MockDelegatingHandler mockHandler = new MockDelegatingHandler((rqst, cancellation) => { return tcs.Task; });
TestTraceWriter traceWriter = new TestTraceWriter();
MessageHandlerTracer tracer = new MessageHandlerTracer(mockHandler, traceWriter);
// DelegatingHandlers require an InnerHandler to run. We create a mock one to simulate what
// would happen when a DelegatingHandler executing after the tracer returns a Task that throws.
MockHttpMessageHandler mockInnerHandler = new MockHttpMessageHandler((rqst, cancellation) => { return tcs.Task; });
tracer.InnerHandler = mockInnerHandler;
HttpRequestMessage request = new HttpRequestMessage();
TraceRecord[] expectedTraces = new TraceRecord[]
{
new TraceRecord(request, TraceCategories.MessageHandlersCategory, TraceLevel.Info) { Kind = TraceKind.Begin, Operation = "SendAsync" },
new TraceRecord(request, TraceCategories.MessageHandlersCategory, TraceLevel.Error) { Kind = TraceKind.End, Operation = "SendAsync" }
};
MethodInfo method = typeof(DelegatingHandler).GetMethod("SendAsync",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance);
// Act
Task<HttpResponseMessage> task =
method.Invoke(tracer, new object[] {request, CancellationToken.None}) as Task<HttpResponseMessage>;
// Assert
Exception thrown = Assert.Throws<InvalidOperationException>(() => task.Wait());
Assert.Equal<TraceRecord>(expectedTraces, traceWriter.Traces, new TraceRecordComparer());
Assert.Same(exception, thrown);
Assert.Same(exception, traceWriter.Traces[1].Exception);
}
// DelegatingHandler cannot be mocked with Moq
private class MockDelegatingHandler : DelegatingHandler
{
private Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _callback;
public MockDelegatingHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> callback) : base()
{
_callback = callback;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _callback(request, cancellationToken);
}
}
// HttpMessageHandler cannot be mocked with Moq
private class MockHttpMessageHandler : HttpMessageHandler
{
private Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _callback;
public MockHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> callback)
: base()
{
_callback = callback;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _callback(request, cancellationToken);
}
}
}
}