Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

794 lines
36 KiB
C#

// 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<HttpActionDescriptor>() { 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<IHttpActionInvoker> mockInvoker = new Mock<IHttpActionInvoker>();
mockInvoker
.Setup(invoker => invoker.InvokeActionAsync(It.IsAny<HttpActionContext>(), It.IsAny<CancellationToken>()))
.Returns(() =>
{
TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
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<IHttpActionSelector> mockSelector = new Mock<IHttpActionSelector>();
mockSelector
.Setup(invoker => invoker.SelectAction(It.IsAny<HttpControllerContext>()))
.Returns(() =>
{
Func<HttpResponseMessage> 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<User>().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<User>().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<User>().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<User>(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<User>().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<HttpResponseException>(() =>
{
HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result;
});
Assert.Equal(HttpStatusCode.NotFound, exception.Response.StatusCode);
var content = Assert.IsType<ObjectContent<string>>(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<string> log = new List<string>();
Mock<ApiController> controllerMock = new Mock<ApiController>() { CallBase = true };
var controllerContextMock = new Mock<HttpControllerContext>();
Mock<IActionValueBinder> binderMock = new Mock<IActionValueBinder>();
Mock<HttpActionBinding> actionBindingMock = new Mock<HttpActionBinding>();
actionBindingMock.Setup(b => b.ExecuteBindingAsync(It.IsAny<HttpActionContext>(), It.IsAny<CancellationToken>())).Returns(() => Task.Factory.StartNew(() => { log.Add("model binding"); }));
binderMock.Setup(b => b.GetBinding(It.IsAny<HttpActionDescriptor>())).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<IHttpActionSelector>();
Mock<HttpActionDescriptor> actionDescriptorMock = new Mock<HttpActionDescriptor>();
actionDescriptorMock.Setup(ad => ad.ActionBinding).Returns(actionBindingMock.Object);
actionDescriptorMock.Setup(ad => ad.GetFilterPipeline()).
Returns(new Collection<FilterInfo>(new List<FilterInfo>() { 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<IHttpActionInvoker>();
invokerMock.Setup(i => i.InvokeActionAsync(It.IsAny<HttpActionContext>(), It.IsAny<CancellationToken>()))
.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<DefaultServices> servicesMock = new Mock<DefaultServices> { CallBase = true };
Mock<IFilterProvider> filterProviderMock = new Mock<IFilterProvider>();
servicesMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new object[] { filterProviderMock.Object }).Verifiable();
_configurationInstance.Services = servicesMock.Object;
HttpActionDescriptor actionDescriptorMock = new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
actionDescriptorMock.Configuration = _configurationInstance;
// Act
actionDescriptorMock.GetFilterPipeline();
// Assert
servicesMock.Verify();
}
[Fact]
public void GetFilters_UsesFilterProvidersToGetFilters()
{
// Arrange
Mock<DefaultServices> servicesMock = new Mock<DefaultServices> { CallBase = true };
Mock<IFilterProvider> filterProviderMock = new Mock<IFilterProvider>();
servicesMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new[] { filterProviderMock.Object });
_configurationInstance.Services = servicesMock.Object;
HttpActionDescriptor actionDescriptorMock = new Mock<HttpActionDescriptor>() { 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<ApiController>().Object,
c => c.Request, expectedDefaultValue: null, allowNull: false,
roundTripTestValue: new HttpRequestMessage());
}
[Fact]
public void ConfigurationPropertyGetterSetterWorks()
{
Assert.Reflection.Property(new Mock<ApiController>().Object,
c => c.Configuration, expectedDefaultValue: null, allowNull: false,
roundTripTestValue: new HttpConfiguration());
}
[Fact]
public void ModelStatePropertyGetterWorks()
{
// Arrange
ApiController controller = new Mock<ApiController>().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<HttpActionDescriptor>() { 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<DefaultServices> 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<HttpActionDescriptor>() { 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<DefaultServices> 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<string> log = new List<string>();
Mock<IActionFilter> globalFilterMock = CreateActionFilterMock((ctx, ct, continuation) =>
{
log.Add("globalFilter");
return continuation();
});
Mock<IActionFilter> actionFilterMock = CreateActionFilterMock((ctx, ct, continuation) =>
{
log.Add("actionFilter");
return continuation();
});
Func<Task<HttpResponseMessage>> innerAction = () => Task<HttpResponseMessage>.Factory.StartNew(() =>
{
log.Add("innerAction");
return null;
});
List<IActionFilter> filters = new List<IActionFilter>() {
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<string> log = new List<string>();
Mock<IAuthorizationFilter> globalFilterMock = CreateAuthorizationFilterMock((ctx, ct, continuation) =>
{
log.Add("globalFilter");
return continuation();
});
Mock<IAuthorizationFilter> actionFilterMock = CreateAuthorizationFilterMock((ctx, ct, continuation) =>
{
log.Add("actionFilter");
return continuation();
});
Func<Task<HttpResponseMessage>> innerAction = () => Task<HttpResponseMessage>.Factory.StartNew(() =>
{
log.Add("innerAction");
return null;
});
List<IAuthorizationFilter> filters = new List<IAuthorizationFilter>() {
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<string> log = new List<string>();
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<string> log = new List<string>();
var actionTask = TaskHelpers.Canceled<HttpResponseMessage>();
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<string> log = new List<string>();
var exception = new Exception();
var actionTask = TaskHelpers.FromError<HttpResponseMessage>(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<string> log = new List<string>();
var exception = new Exception();
var actionTask = TaskHelpers.FromError<HttpResponseMessage>(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<ApiController>().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<ApiController> { CallBase = true }.Object;
var mockDescriptor = new Mock<HttpControllerDescriptor>(config, "MyMock", singletonController.GetType()) { CallBase = true };
mockDescriptor.Setup(d => d.CreateController(It.IsAny<HttpRequestMessage>())).Returns(singletonController);
var mockSelector = new Mock<DefaultHttpControllerSelector>(config) { CallBase = true };
mockSelector.Setup(s => s.SelectController(It.IsAny<HttpRequestMessage>())).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<IAuthorizationFilter> CreateAuthorizationFilterMock(Func<HttpActionContext, CancellationToken, Func<Task<HttpResponseMessage>>, Task<HttpResponseMessage>> implementation)
{
Mock<IAuthorizationFilter> filterMock = new Mock<IAuthorizationFilter>();
filterMock.Setup(f => f.ExecuteAuthorizationFilterAsync(It.IsAny<HttpActionContext>(),
CancellationToken.None,
It.IsAny<Func<Task<HttpResponseMessage>>>()))
.Returns(implementation)
.Verifiable();
return filterMock;
}
private Mock<IActionFilter> CreateActionFilterMock(Func<HttpActionContext, CancellationToken, Func<Task<HttpResponseMessage>>, Task<HttpResponseMessage>> implementation)
{
Mock<IActionFilter> filterMock = new Mock<IActionFilter>();
filterMock.Setup(f => f.ExecuteActionFilterAsync(It.IsAny<HttpActionContext>(),
CancellationToken.None,
It.IsAny<Func<Task<HttpResponseMessage>>>()))
.Returns(implementation)
.Verifiable();
return filterMock;
}
private Mock<IExceptionFilter> CreateExceptionFilterMock(Func<HttpActionExecutedContext, CancellationToken, Task> implementation)
{
Mock<IExceptionFilter> filterMock = new Mock<IExceptionFilter>();
filterMock.Setup(f => f.ExecuteExceptionFilterAsync(It.IsAny<HttpActionExecutedContext>(),
CancellationToken.None))
.Returns(implementation)
.Verifiable();
return filterMock;
}
private static Mock<DefaultServices> BuildFilterProvidingServicesMock(HttpConfiguration configuration, HttpActionDescriptor action, params FilterInfo[] filters)
{
var servicesMock = new Mock<DefaultServices> { CallBase = true };
var filterProviderMock = new Mock<IFilterProvider>();
servicesMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new[] { filterProviderMock.Object });
filterProviderMock.Setup(fp => fp.GetFilters(configuration, action)).Returns(filters);
return servicesMock;
}
/// <summary>
/// Simple IFilter implementation with AllowMultiple = true
/// </summary>
public class TestMultiFilter : IFilter
{
public bool AllowMultiple
{
get { return true; }
}
}
/// <summary>
/// Simple IFilter implementation with AllowMultiple = false
/// </summary>
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);
}
}
}