// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Web.Http.ExceptionHandling; using System.Web.Http.Hosting; using Microsoft.TestCommon; using Moq; using Owin; namespace System.Web.Http.Owin { public class WebApiAppBuilderExtensionsTest { [Fact] public void UseWebApiWithConfiguration_IfBuilderIsNull_Throws() { // Arrange IAppBuilder builder = null; using (HttpConfiguration configuration = CreateConfiguration()) { // Act & Assert Assert.ThrowsArgumentNull(() => WebApiAppBuilderExtensions.UseWebApi(builder, configuration), "builder"); } } [Fact] public void UseWebApiWithConfiguration_IfConfigurationIsNull_Throws() { // Arrange IAppBuilder builder = CreateDummyAppBuilder(); HttpConfiguration configuration = null; // Act & Assert Assert.ThrowsArgumentNull(() => WebApiAppBuilderExtensions.UseWebApi(builder, configuration), "configuration"); } [Fact] public void UseWebApiWithServer_IfBuilderIsNull_Throws() { // Arrange IAppBuilder builder = null; using (HttpServer httpServer = CreateServer()) { // Act & Assert Assert.ThrowsArgumentNull(() => WebApiAppBuilderExtensions.UseWebApi(builder, httpServer), "builder"); } } [Fact] public void UseWebApiWithServer_IfServerIsNull_Throws() { // Arrange IAppBuilder builder = CreateDummyAppBuilder(); HttpServer httpServer = null; // Act & Assert Assert.ThrowsArgumentNull(() => WebApiAppBuilderExtensions.UseWebApi(builder, httpServer), "httpServer"); } [Fact] public void UseWebApi_UsesAdapter() { var config = new HttpConfiguration(); var appBuilder = new Mock(); appBuilder .Setup(ab => ab.Use( typeof(HttpMessageHandlerAdapter), It.Is((o) => ((HttpServer)o.MessageHandler).Configuration == config))) .Returns(appBuilder.Object) .Verifiable(); IAppBuilder returnedAppBuilder = appBuilder.Object.UseWebApi(config); Assert.Equal(appBuilder.Object, returnedAppBuilder); appBuilder.Verify(); } [Fact] public async Task UseWebApi_UsesAdapterAndConfigServices() { using (CancellationTokenSource tokenSource = CreateCancellationTokenSource()) { var config = new HttpConfiguration(); var bufferPolicySelector = new Mock().Object; Mock loggerMock = new Mock(); Mock handlerMock = new Mock(); config.Services.Replace(typeof(IHostBufferPolicySelector), bufferPolicySelector); config.Services.Replace(typeof(IExceptionLogger), loggerMock.Object); config.Services.Replace(typeof(IExceptionHandler), handlerMock.Object); IExceptionLogger exceptionLogger = null; IExceptionHandler exceptionHandler = null; var appBuilder = new Mock(); CancellationToken expectedAppDisposing = tokenSource.Token; IDictionary properties = new Dictionary { { "host.OnAppDisposing", expectedAppDisposing } }; appBuilder.SetupGet(b => b.Properties).Returns(properties); appBuilder .Setup(ab => ab.Use( typeof(HttpMessageHandlerAdapter), It.Is((o) => ((HttpServer)o.MessageHandler).Configuration == config && o.BufferPolicySelector == bufferPolicySelector && o.AppDisposing == expectedAppDisposing))) .Callback((i, args) => { HttpMessageHandlerOptions options = (HttpMessageHandlerOptions)args[0]; exceptionLogger = options.ExceptionLogger; exceptionHandler = options.ExceptionHandler; }) .Returns(appBuilder.Object) .Verifiable(); IAppBuilder returnedAppBuilder = appBuilder.Object.UseWebApi(config); Assert.Equal(appBuilder.Object, returnedAppBuilder); appBuilder.Verify(); await AssertDelegatesToAsync(loggerMock, exceptionLogger); await AssertDelegatesToAsync(handlerMock, exceptionHandler); } } [Fact] public void UseWebApiWithHttpServer_UsesAdapter() { // Arrange HttpServer httpServer = new Mock().Object; Mock appBuilderMock = new Mock(); appBuilderMock .Setup(ab => ab.Use( typeof(HttpMessageHandlerAdapter), It.Is((o) => o.MessageHandler == httpServer))) .Returns(appBuilderMock.Object) .Verifiable(); // Act IAppBuilder returnedAppBuilder = appBuilderMock.Object.UseWebApi(httpServer); // Assert Assert.Equal(appBuilderMock.Object, returnedAppBuilder); appBuilderMock.Verify(); } [Fact] public async Task UseWebApiWithHttpServer_UsesAdapterAndConfigServices() { // Arrange using (CancellationTokenSource tokenSource = CreateCancellationTokenSource()) { HttpConfiguration config = new HttpConfiguration(); IHostBufferPolicySelector bufferPolicySelector = new Mock().Object; Mock loggerMock = new Mock(); Mock handlerMock = new Mock(); config.Services.Replace(typeof(IHostBufferPolicySelector), bufferPolicySelector); config.Services.Replace(typeof(IExceptionLogger), loggerMock.Object); config.Services.Replace(typeof(IExceptionHandler), handlerMock.Object); HttpServer httpServer = new Mock(config).Object; IExceptionLogger exceptionLogger = null; IExceptionHandler exceptionHandler = null; Mock appBuilderMock = new Mock(); CancellationToken expectedAppDisposing = tokenSource.Token; IDictionary properties = new Dictionary { { "host.OnAppDisposing", expectedAppDisposing } }; appBuilderMock.SetupGet(b => b.Properties).Returns(properties); appBuilderMock .Setup(ab => ab.Use( typeof(HttpMessageHandlerAdapter), It.Is((o) => o.MessageHandler == httpServer && o.BufferPolicySelector == bufferPolicySelector && o.AppDisposing == expectedAppDisposing))) .Callback((i, args) => { HttpMessageHandlerOptions options = (HttpMessageHandlerOptions)args[0]; exceptionLogger = options.ExceptionLogger; exceptionHandler = options.ExceptionHandler; }) .Returns(appBuilderMock.Object) .Verifiable(); // Act IAppBuilder returnedAppBuilder = appBuilderMock.Object.UseWebApi(httpServer); // Assert Assert.Equal(appBuilderMock.Object, returnedAppBuilder); appBuilderMock.Verify(); await AssertDelegatesToAsync(loggerMock, exceptionLogger); await AssertDelegatesToAsync(handlerMock, exceptionHandler); } } [Fact] public void GetOnAppDisposingProperty_IfPropertiesIsPresent_ReturnsSpecifiedValue() { // Arrange using (CancellationTokenSource tokenSource = CreateCancellationTokenSource()) { CancellationToken expectedOnAppDisposing = tokenSource.Token; IDictionary properties = CreateStubOnAppDisposingDictionary(expectedOnAppDisposing); IAppBuilder builder = CreateStubAppBuilder(properties); // Act CancellationToken onAppDisposing = builder.GetOnAppDisposingProperty(); // Assert Assert.Equal(expectedOnAppDisposing, onAppDisposing); } } [Fact] public void GetOnAppDisposingProperty_IfPropertiesIsNull_ReturnsCancellationTokenNone() { // Arrange IDictionary properties = null; IAppBuilder builder = CreateStubAppBuilder(properties); // Act CancellationToken onAppDisposing = WebApiAppBuilderExtensions.GetOnAppDisposingProperty(builder); // Assert Assert.Equal(CancellationToken.None, onAppDisposing); } [Fact] public void GetOnAppDisposingProperty_IfPropertyIsAbsent_ReturnsCancellationTokenNone() { // Arrange IDictionary properties = CreateStubOnAppDisposingDictionary(onAppDisposing: null, hasOnAppDisposing: false); IAppBuilder builder = CreateStubAppBuilder(properties); // Act CancellationToken onAppDisposing = WebApiAppBuilderExtensions.GetOnAppDisposingProperty(builder); // Assert Assert.Equal(CancellationToken.None, onAppDisposing); } [Fact] public void GetOnAppDisposingProperty_IfPropertyIsNonCancellationToken_ReturnsCancellationTokenNone() { // Arrange object nonCancellationToken = new object(); IDictionary properties = CreateStubOnAppDisposingDictionary(nonCancellationToken); IAppBuilder builder = CreateStubAppBuilder(properties); // Act CancellationToken onAppDisposing = WebApiAppBuilderExtensions.GetOnAppDisposingProperty(builder); // Assert Assert.Equal(CancellationToken.None, onAppDisposing); } private static async Task AssertDelegatesToAsync(Mock expected, IExceptionHandler actual) { Assert.NotNull(actual); ExceptionHandlerContext context = new ExceptionHandlerContext(new ExceptionContext(new Exception(), ExceptionCatchBlocks.HttpServer)); CancellationToken cancellationToken = CancellationToken.None; expected .Setup((l) => l.HandleAsync(It.IsAny(), It.IsAny())) .Returns(CreateCanceledTask()); await Assert.ThrowsAsync(() => actual.HandleAsync(context, cancellationToken)); expected.Verify((l) => l.HandleAsync(context, cancellationToken), Times.Once()); } private static async Task AssertDelegatesToAsync(Mock expected, IExceptionLogger actual) { Assert.NotNull(actual); ExceptionLoggerContext context = new ExceptionLoggerContext(new ExceptionContext(new Exception(), ExceptionCatchBlocks.HttpServer)); CancellationToken cancellationToken = CancellationToken.None; expected .Setup((l) => l.LogAsync(It.IsAny(), It.IsAny())) .Returns(CreateCanceledTask()); await Assert.ThrowsAsync(() => actual.LogAsync(context, cancellationToken)); expected.Verify((l) => l.LogAsync(context, cancellationToken), Times.Once()); } private static Task CreateCanceledTask() { TaskCompletionSource source = new TaskCompletionSource(); source.SetCanceled(); return source.Task; } private static CancellationTokenSource CreateCancellationTokenSource() { return new CancellationTokenSource(); } private static HttpConfiguration CreateConfiguration() { return new HttpConfiguration(); } private static IAppBuilder CreateDummyAppBuilder() { return new Mock(MockBehavior.Strict).Object; } private static HttpServer CreateServer() { return new HttpServer(); } private static IAppBuilder CreateStubAppBuilder(IDictionary properties) { Mock mock = new Mock(MockBehavior.Strict); mock.SetupGet(b => b.Properties).Returns(properties); return mock.Object; } private static IDictionary CreateStubOnAppDisposingDictionary(object onAppDisposing) { return CreateStubOnAppDisposingDictionary(onAppDisposing, hasOnAppDisposing: true); } private static IDictionary CreateStubOnAppDisposingDictionary(object onAppDisposing, bool hasOnAppDisposing) { Mock> mock = new Mock>(); mock.Setup(d => d.TryGetValue("host.OnAppDisposing", out onAppDisposing)).Returns(hasOnAppDisposing); return mock.Object; } } }