// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using System.Web.Http.Filters; using System.Web.Http.ModelBinding; using System.Web.Http.Routing; using System.Web.Http.Services; using Microsoft.TestCommon; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Web.Http { public class ApiControllerTest { private readonly HttpActionContext _actionContextInstance = ContextUtil.CreateActionContext(); private readonly HttpConfiguration _configurationInstance = new HttpConfiguration(); private readonly HttpActionDescriptor _actionDescriptorInstance = new Mock() { CallBase = true }.Object; [Fact] public void Setting_CustomActionInvoker() { // Arrange ApiController api = new UsersController(); string responseText = "Hello World"; HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(); HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController)); controllerContext.ControllerDescriptor = controllerDescriptor; Mock mockInvoker = new Mock(); mockInvoker .Setup(invoker => invoker.InvokeActionAsync(It.IsAny(), It.IsAny())) .Returns(() => { TaskCompletionSource tcs = new TaskCompletionSource(); tcs.TrySetResult(new HttpResponseMessage() { Content = new StringContent(responseText) }); return tcs.Task; }); controllerDescriptor.HttpActionInvoker = mockInvoker.Object; // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; // Assert Assert.Equal(responseText, message.Content.ReadAsStringAsync().Result); } [Fact] public void Setting_CustomActionSelector() { // Arrange ApiController api = new UsersController(); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(); HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController)); controllerContext.ControllerDescriptor = controllerDescriptor; Mock mockSelector = new Mock(); mockSelector .Setup(invoker => invoker.SelectAction(It.IsAny())) .Returns(() => { Func testDelegate = () => new HttpResponseMessage { Content = new StringContent("This is a test") }; return new ReflectedHttpActionDescriptor { Configuration = controllerContext.Configuration, ControllerDescriptor = controllerDescriptor, MethodInfo = testDelegate.Method }; }); controllerDescriptor.HttpActionSelector = mockSelector.Object; // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; // Assert Assert.Equal("This is a test", message.Content.ReadAsStringAsync().Result); } [Fact] public void Default_Get() { // Arrange ApiController api = new UsersController(); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Get }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController)); // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; // Assert Assert.Equal("Default User", message.Content.ReadAsStringAsync().Result); } [Fact] public void Default_Post() { // Arrange ApiController api = new UsersController(); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Post }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController)); // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; // Assert Assert.Equal("User Posted", message.Content.ReadAsStringAsync().Result); } [Fact] public void Default_Put() { // Arrange ApiController api = new UsersController(); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Put }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController)); // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; // Assert Assert.Equal("User Updated", message.Content.ReadAsStringAsync().Result); } [Fact] public void Default_Delete() { // Arrange ApiController api = new UsersController(); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Delete }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController)); // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; // Assert Assert.Equal("User Deleted", message.Content.ReadAsStringAsync().Result); } [Fact] public void Route_ActionName() { // Arrange ApiController api = new UsersRpcController(); HttpRouteData route = new HttpRouteData(new HttpRoute()); route.Values.Add("action", "Admin"); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Post }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController)); // Act HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result; User user = message.Content.ReadAsAsync().Result; // Assert Assert.Equal("Yao", user.FirstName); Assert.Equal("Huang", user.LastName); } [Fact] public void Route_Get_Action_With_Route_Parameters() { // Arrange ApiController api = new UsersRpcController(); HttpRouteData route = new HttpRouteData(new HttpRoute()); route.Values.Add("action", "EchoUser"); route.Values.Add("firstName", "RouteFirstName"); route.Values.Add("lastName", "RouteLastName"); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Post }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController)); // Act HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result; User user = message.Content.ReadAsAsync().Result; // Assert Assert.Equal("RouteFirstName", user.FirstName); Assert.Equal("RouteLastName", user.LastName); } [Fact] public void Route_Get_Action_With_Query_Parameters() { // Arrange ApiController api = new UsersRpcController(); HttpRouteData route = new HttpRouteData(new HttpRoute()); route.Values.Add("action", "EchoUser"); Uri requestUri = new Uri("http://localhost/?firstName=QueryFirstName&lastName=QueryLastName"); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Post, RequestUri = requestUri }); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController)); // Act HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result; User user = message.Content.ReadAsAsync().Result; // Assert Assert.Equal("QueryFirstName", user.FirstName); Assert.Equal("QueryLastName", user.LastName); } [Fact] public void Route_Post_Action_With_Content_Parameter() { // Arrange ApiController api = new UsersRpcController(); HttpRouteData route = new HttpRouteData(new HttpRoute()); route.Values.Add("action", "EchoUserObject"); User postedUser = new User() { FirstName = "SampleFirstName", LastName = "SampleLastName" }; HttpRequestMessage request = new HttpRequestMessage() { Method = HttpMethod.Post }; // Create a serialized request because this test directly calls the controller // which would have normally been working with a serialized request content. string serializedUserAsString = null; using (HttpRequestMessage tempRequest = new HttpRequestMessage() { Content = new ObjectContent(postedUser, new XmlMediaTypeFormatter()) }) { serializedUserAsString = tempRequest.Content.ReadAsStringAsync().Result; } StringContent stringContent = new StringContent(serializedUserAsString); stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/xml"); request.Content = stringContent; HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: request); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController)); // Act HttpResponseMessage message = api.ExecuteAsync( controllerContext, CancellationToken.None).Result; User user = message.Content.ReadAsAsync().Result; // Assert Assert.Equal(postedUser.FirstName, user.FirstName); Assert.Equal(postedUser.LastName, user.LastName); } [Fact] public void Invalid_Action_In_Route() { // Arrange ApiController api = new UsersController(); HttpRouteData route = new HttpRouteData(new HttpRoute()); string actionName = "invalidOp"; route.Values.Add("action", actionName); HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Get }); Type controllerType = typeof(UsersController); controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, controllerType.Name, controllerType); // Act & Assert var exception = Assert.Throws(() => { HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result; }); Assert.Equal(HttpStatusCode.NotFound, exception.Response.StatusCode); var content = Assert.IsType>(exception.Response.Content); Assert.Equal("No action was found on the controller 'UsersController' that matches the name 'invalidOp'.", content.Value); } [Fact] public void ExecuteAsync_InvokesAuthorizationFilters_ThenInvokesModelBinding_ThenInvokesActionFilters_ThenInvokesAction() { List log = new List(); Mock controllerMock = new Mock() { CallBase = true }; var controllerContextMock = new Mock(); Mock binderMock = new Mock(); Mock actionBindingMock = new Mock(); actionBindingMock.Setup(b => b.ExecuteBindingAsync(It.IsAny(), It.IsAny())).Returns(() => Task.Factory.StartNew(() => { log.Add("model binding"); })); binderMock.Setup(b => b.GetBinding(It.IsAny())).Returns(actionBindingMock.Object); HttpConfiguration configuration = new HttpConfiguration(); HttpControllerContext controllerContext = controllerContextMock.Object; controllerContext.Configuration = configuration; controllerContext.ControllerDescriptor = new HttpControllerDescriptor(configuration, "test", typeof(object)); var actionFilterMock = CreateActionFilterMock((ac, ct, cont) => { log.Add("action filters"); return cont(); }); var authFilterMock = CreateAuthorizationFilterMock((ac, ct, cont) => { log.Add("auth filters"); return cont(); }); var selectorMock = new Mock(); Mock actionDescriptorMock = new Mock(); actionDescriptorMock.Setup(ad => ad.ActionBinding).Returns(actionBindingMock.Object); actionDescriptorMock.Setup(ad => ad.GetFilterPipeline()). Returns(new Collection(new List() { new FilterInfo(actionFilterMock.Object, FilterScope.Action), new FilterInfo(authFilterMock.Object, FilterScope.Action) })); selectorMock.Setup(s => s.SelectAction(controllerContext)).Returns(actionDescriptorMock.Object); ApiController controller = controllerMock.Object; var invokerMock = new Mock(); invokerMock.Setup(i => i.InvokeActionAsync(It.IsAny(), It.IsAny())) .Returns(() => Task.Factory.StartNew(() => { log.Add("action"); return new HttpResponseMessage(); })); controllerContext.ControllerDescriptor.HttpActionInvoker = invokerMock.Object; controllerContext.ControllerDescriptor.HttpActionSelector = selectorMock.Object; controllerContext.ControllerDescriptor.ActionValueBinder = binderMock.Object; var task = controller.ExecuteAsync(controllerContext, CancellationToken.None); Assert.NotNull(task); task.WaitUntilCompleted(); Assert.Equal(new string[] { "auth filters", "model binding", "action filters", "action" }, log.ToArray()); } [Fact] public void GetFilters_QueriesFilterProvidersFromServices() { // Arrange Mock servicesMock = new Mock { CallBase = true }; Mock filterProviderMock = new Mock(); servicesMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new object[] { filterProviderMock.Object }).Verifiable(); _configurationInstance.Services = servicesMock.Object; HttpActionDescriptor actionDescriptorMock = new Mock() { CallBase = true }.Object; actionDescriptorMock.Configuration = _configurationInstance; // Act actionDescriptorMock.GetFilterPipeline(); // Assert servicesMock.Verify(); } [Fact] public void GetFilters_UsesFilterProvidersToGetFilters() { // Arrange Mock servicesMock = new Mock { CallBase = true }; Mock filterProviderMock = new Mock(); servicesMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new[] { filterProviderMock.Object }); _configurationInstance.Services = servicesMock.Object; HttpActionDescriptor actionDescriptorMock = new Mock() { CallBase = true }.Object; actionDescriptorMock.Configuration = _configurationInstance; // Act actionDescriptorMock.GetFilterPipeline().ToList(); // Assert filterProviderMock.Verify(fp => fp.GetFilters(_configurationInstance, actionDescriptorMock)); } [Fact] public void RequestPropertyGetterSetterWorks() { Assert.Reflection.Property(new Mock().Object, c => c.Request, expectedDefaultValue: null, allowNull: false, roundTripTestValue: new HttpRequestMessage()); } [Fact] public void ConfigurationPropertyGetterSetterWorks() { Assert.Reflection.Property(new Mock().Object, c => c.Configuration, expectedDefaultValue: null, allowNull: false, roundTripTestValue: new HttpConfiguration()); } [Fact] public void ModelStatePropertyGetterWorks() { // Arrange ApiController controller = new Mock().Object; // Act ModelStateDictionary expected = new ModelStateDictionary(); expected.Add("a", new ModelState() { Value = new ValueProviders.ValueProviderResult("result", "attempted", CultureInfo.InvariantCulture) }); controller.ModelState.Add("a", new ModelState() { Value = new ValueProviders.ValueProviderResult("result", "attempted", CultureInfo.InvariantCulture) }); // Assert Assert.Equal(expected.Count, controller.ModelState.Count); } // TODO: Move these tests to ActionDescriptorTest [Fact] public void GetFilters_OrdersFilters() { // Arrange HttpActionDescriptor actionDescriptorMock = new Mock() { CallBase = true }.Object; actionDescriptorMock.Configuration = _configurationInstance; var globalFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Global); var actionFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Action); var controllerFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Controller); Mock servicesMock = BuildFilterProvidingServicesMock(_configurationInstance, actionDescriptorMock, globalFilter, actionFilter, controllerFilter); _configurationInstance.Services = servicesMock.Object; // Act var result = actionDescriptorMock.GetFilterPipeline().ToArray(); // Assert Assert.Equal(new[] { globalFilter, controllerFilter, actionFilter }, result); } [Fact] public void GetFilters_RemovesDuplicateUniqueFiltersKeepingMostSpecificScope() { // Arrange HttpActionDescriptor actionDescriptorMock = new Mock() { CallBase = true }.Object; actionDescriptorMock.Configuration = _configurationInstance; var multiActionFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Action); var multiGlobalFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Global); var uniqueControllerFilter = new FilterInfo(new TestUniqueFilter(), FilterScope.Controller); var uniqueActionFilter = new FilterInfo(new TestUniqueFilter(), FilterScope.Action); Mock servicesMock = BuildFilterProvidingServicesMock( _configurationInstance, actionDescriptorMock, multiActionFilter, multiGlobalFilter, uniqueControllerFilter, uniqueActionFilter); _configurationInstance.Services = servicesMock.Object; // Act var result = actionDescriptorMock.GetFilterPipeline().ToArray(); // Assert Assert.Equal(new[] { multiGlobalFilter, multiActionFilter, uniqueActionFilter }, result); } [Fact] public void InvokeActionWithActionFilters_ChainsFiltersInOrderFollowedByInnerActionContinuation() { // Arrange List log = new List(); Mock globalFilterMock = CreateActionFilterMock((ctx, ct, continuation) => { log.Add("globalFilter"); return continuation(); }); Mock actionFilterMock = CreateActionFilterMock((ctx, ct, continuation) => { log.Add("actionFilter"); return continuation(); }); Func> innerAction = () => Task.Factory.StartNew(() => { log.Add("innerAction"); return null; }); List filters = new List() { globalFilterMock.Object, actionFilterMock.Object, }; // Act var result = ApiController.InvokeActionWithActionFilters(_actionContextInstance, CancellationToken.None, filters, innerAction); // Assert Assert.NotNull(result); var resultTask = result(); Assert.NotNull(resultTask); resultTask.WaitUntilCompleted(); Assert.Equal(new[] { "globalFilter", "actionFilter", "innerAction" }, log.ToArray()); globalFilterMock.Verify(); actionFilterMock.Verify(); } [Fact] public void InvokeActionWithAuthorizationFilters_ChainsFiltersInOrderFollowedByInnerActionContinuation() { // Arrange List log = new List(); Mock globalFilterMock = CreateAuthorizationFilterMock((ctx, ct, continuation) => { log.Add("globalFilter"); return continuation(); }); Mock actionFilterMock = CreateAuthorizationFilterMock((ctx, ct, continuation) => { log.Add("actionFilter"); return continuation(); }); Func> innerAction = () => Task.Factory.StartNew(() => { log.Add("innerAction"); return null; }); List filters = new List() { globalFilterMock.Object, actionFilterMock.Object, }; // Act var result = ApiController.InvokeActionWithAuthorizationFilters(_actionContextInstance, CancellationToken.None, filters, innerAction); // Assert Assert.NotNull(result); var resultTask = result(); Assert.NotNull(resultTask); resultTask.WaitUntilCompleted(); Assert.Equal(new[] { "globalFilter", "actionFilter", "innerAction" }, log.ToArray()); globalFilterMock.Verify(); actionFilterMock.Verify(); } [Fact] public void InvokeActionWithExceptionFilters_IfActionTaskIsSuccessful_ReturnsSuccessTask() { // Arrange List log = new List(); var response = new HttpResponseMessage(); var actionTask = TaskHelpers.FromResult(response); var exceptionFilterMock = CreateExceptionFilterMock((ec, ct) => { log.Add("exceptionFilter"); return Task.Factory.StartNew(() => { }); }); var filters = new[] { exceptionFilterMock.Object }; // Act var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters); // Assert Assert.NotNull(result); result.WaitUntilCompleted(); Assert.Equal(TaskStatus.RanToCompletion, result.Status); Assert.Same(response, result.Result); Assert.Equal(new string[] { }, log.ToArray()); } [Fact] public void InvokeActionWithExceptionFilters_IfActionTaskIsCanceled_ReturnsCanceledTask() { // Arrange List log = new List(); var actionTask = TaskHelpers.Canceled(); var exceptionFilterMock = CreateExceptionFilterMock((ec, ct) => { log.Add("exceptionFilter"); return Task.Factory.StartNew(() => { }); }); var filters = new[] { exceptionFilterMock.Object }; // Act var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters); // Assert Assert.NotNull(result); result.WaitUntilCompleted(); Assert.Equal(TaskStatus.Canceled, result.Status); Assert.Equal(new string[] { }, log.ToArray()); } [Fact] public void InvokeActionWithExceptionFilters_IfActionTaskIsFaulted_ExecutesFiltersAndReturnsFaultedTaskIfNotHandled() { // Arrange List log = new List(); var exception = new Exception(); var actionTask = TaskHelpers.FromError(exception); Exception exceptionSeenByFilter = null; var exceptionFilterMock = CreateExceptionFilterMock((ec, ct) => { exceptionSeenByFilter = ec.Exception; log.Add("exceptionFilter"); return Task.Factory.StartNew(() => { }); }); var filters = new[] { exceptionFilterMock.Object }; // Act var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters); // Assert Assert.NotNull(result); result.WaitUntilCompleted(); Assert.Equal(TaskStatus.Faulted, result.Status); Assert.Same(exception, result.Exception.InnerException); Assert.Same(exception, exceptionSeenByFilter); Assert.Equal(new string[] { "exceptionFilter" }, log.ToArray()); } [Fact] public void InvokeActionWithExceptionFilters_IfActionTaskIsFaulted_ExecutesFiltersAndReturnsResultIfHandled() { // Arrange List log = new List(); var exception = new Exception(); var actionTask = TaskHelpers.FromError(exception); HttpResponseMessage globalFilterResponse = new HttpResponseMessage(); HttpResponseMessage actionFilterResponse = new HttpResponseMessage(); HttpResponseMessage resultSeenByGlobalFilter = null; var globalFilterMock = CreateExceptionFilterMock((ec, ct) => { log.Add("globalFilter"); resultSeenByGlobalFilter = ec.Response; ec.Response = globalFilterResponse; return Task.Factory.StartNew(() => { }); }); var actionFilterMock = CreateExceptionFilterMock((ec, ct) => { log.Add("actionFilter"); ec.Response = actionFilterResponse; return Task.Factory.StartNew(() => { }); }); var filters = new[] { globalFilterMock.Object, actionFilterMock.Object }; // Act var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters); // Assert Assert.NotNull(result); result.WaitUntilCompleted(); Assert.Equal(TaskStatus.RanToCompletion, result.Status); Assert.Same(globalFilterResponse, result.Result); Assert.Same(actionFilterResponse, resultSeenByGlobalFilter); Assert.Equal(new string[] { "actionFilter", "globalFilter" }, log.ToArray()); } [Fact, RestoreThreadPrincipal] public void User_ReturnsThreadPrincipal() { // Arrange ApiController controller = new Mock().Object; IPrincipal principal = new GenericPrincipal(new GenericIdentity("joe"), new string[0]); Thread.CurrentPrincipal = principal; // Act IPrincipal result = controller.User; // Assert Assert.Same(result, principal); } [Fact] public void ApiControllerCannotBeReused() { // Arrange var config = new HttpConfiguration(); var singletonController = new Mock { CallBase = true }.Object; var mockDescriptor = new Mock(config, "MyMock", singletonController.GetType()) { CallBase = true }; mockDescriptor.Setup(d => d.CreateController(It.IsAny())).Returns(singletonController); var mockSelector = new Mock(config) { CallBase = true }; mockSelector.Setup(s => s.SelectController(It.IsAny())).Returns(mockDescriptor.Object); config.Routes.MapHttpRoute("default", "", new { controller = "MyMock" }); config.Services.Replace(typeof(IHttpControllerSelector), mockSelector.Object); var server = new HttpServer(config); var invoker = new HttpMessageInvoker(server); // Act HttpResponseMessage response1 = invoker.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://localhost/"), CancellationToken.None).Result; HttpResponseMessage response2 = invoker.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://localhost/"), CancellationToken.None).Result; // Assert Assert.NotEqual(HttpStatusCode.InternalServerError, response1.StatusCode); Assert.Equal(HttpStatusCode.InternalServerError, response2.StatusCode); Assert.Contains("Cannot reuse an 'ApiController' instance. 'ApiController' has to be constructed per incoming message.", response2.Content.ReadAsStringAsync().Result); } [Fact] public void ApiControllerPutsSelfInRequestResourcesToBeDisposed() { // Arrange var config = new HttpConfiguration(); config.Routes.MapHttpRoute("default", "", new { controller = "SpyDispose" }); var server = new HttpServer(config); var invoker = new HttpMessageInvoker(server); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/"); invoker.SendAsync(request, CancellationToken.None).WaitUntilCompleted(); // Act request.DisposeRequestResources(); // Assert Assert.True(SpyDisposeController.DisposeWasCalled); } private Mock CreateAuthorizationFilterMock(Func>, Task> implementation) { Mock filterMock = new Mock(); filterMock.Setup(f => f.ExecuteAuthorizationFilterAsync(It.IsAny(), CancellationToken.None, It.IsAny>>())) .Returns(implementation) .Verifiable(); return filterMock; } private Mock CreateActionFilterMock(Func>, Task> implementation) { Mock filterMock = new Mock(); filterMock.Setup(f => f.ExecuteActionFilterAsync(It.IsAny(), CancellationToken.None, It.IsAny>>())) .Returns(implementation) .Verifiable(); return filterMock; } private Mock CreateExceptionFilterMock(Func implementation) { Mock filterMock = new Mock(); filterMock.Setup(f => f.ExecuteExceptionFilterAsync(It.IsAny(), CancellationToken.None)) .Returns(implementation) .Verifiable(); return filterMock; } private static Mock BuildFilterProvidingServicesMock(HttpConfiguration configuration, HttpActionDescriptor action, params FilterInfo[] filters) { var servicesMock = new Mock { CallBase = true }; var filterProviderMock = new Mock(); servicesMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new[] { filterProviderMock.Object }); filterProviderMock.Setup(fp => fp.GetFilters(configuration, action)).Returns(filters); return servicesMock; } /// /// Simple IFilter implementation with AllowMultiple = true /// public class TestMultiFilter : IFilter { public bool AllowMultiple { get { return true; } } } /// /// Simple IFilter implementation with AllowMultiple = false /// public class TestUniqueFilter : IFilter { public bool AllowMultiple { get { return false; } } } } public class SpyDisposeController : System.Web.Http.ApiController { public static bool DisposeWasCalled = false; public SpyDisposeController() { } public void Get() { } protected override void Dispose(bool disposing) { DisposeWasCalled = true; base.Dispose(disposing); } } }