PageRenderTime 69ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/test/System.Web.Http.Test/Controllers/ApiControllerTest.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 730 lines | 552 code | 101 blank | 77 comment | 0 complexity | 4573d4409b3702161a24b2dcd35e65f1 MD5 | raw file
  1. using System.Collections.Generic;
  2. using System.Collections.ObjectModel;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.Http.Formatting;
  8. using System.Net.Http.Headers;
  9. using System.Security.Principal;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using System.Web.Http.Controllers;
  13. using System.Web.Http.Filters;
  14. using System.Web.Http.ModelBinding;
  15. using System.Web.Http.Routing;
  16. using System.Web.Http.Services;
  17. using Microsoft.TestCommon;
  18. using Moq;
  19. using Xunit;
  20. using Assert = Microsoft.TestCommon.AssertEx;
  21. namespace System.Web.Http
  22. {
  23. public class ApiControllerTest
  24. {
  25. private readonly HttpActionContext _actionContextInstance = ContextUtil.CreateActionContext();
  26. private readonly HttpConfiguration _configurationInstance = new HttpConfiguration();
  27. private readonly HttpActionDescriptor _actionDescriptorInstance = new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
  28. [Fact]
  29. public void Setting_CustomActionInvoker()
  30. {
  31. // Arrange
  32. ApiController api = new UsersController();
  33. string responseText = "Hello World";
  34. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext();
  35. HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController));
  36. controllerContext.ControllerDescriptor = controllerDescriptor;
  37. Mock<IHttpActionInvoker> mockInvoker = new Mock<IHttpActionInvoker>();
  38. mockInvoker
  39. .Setup(invoker => invoker.InvokeActionAsync(It.IsAny<HttpActionContext>(), It.IsAny<CancellationToken>()))
  40. .Returns(() =>
  41. {
  42. TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
  43. tcs.TrySetResult(new HttpResponseMessage() { Content = new StringContent(responseText) });
  44. return tcs.Task;
  45. });
  46. controllerDescriptor.HttpActionInvoker = mockInvoker.Object;
  47. // Act
  48. HttpResponseMessage message = api.ExecuteAsync(
  49. controllerContext,
  50. CancellationToken.None).Result;
  51. // Assert
  52. Assert.Equal(responseText, message.Content.ReadAsStringAsync().Result);
  53. }
  54. [Fact]
  55. public void Setting_CustomActionSelector()
  56. {
  57. // Arrange
  58. ApiController api = new UsersController();
  59. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext();
  60. HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController));
  61. controllerContext.ControllerDescriptor = controllerDescriptor;
  62. Mock<IHttpActionSelector> mockSelector = new Mock<IHttpActionSelector>();
  63. mockSelector
  64. .Setup(invoker => invoker.SelectAction(It.IsAny<HttpControllerContext>()))
  65. .Returns(() =>
  66. {
  67. Func<HttpResponseMessage> testDelegate =
  68. () => new HttpResponseMessage { Content = new StringContent("This is a test") };
  69. return new ReflectedHttpActionDescriptor
  70. {
  71. Configuration = controllerContext.Configuration,
  72. ControllerDescriptor = controllerDescriptor,
  73. MethodInfo = testDelegate.Method
  74. };
  75. });
  76. controllerDescriptor.HttpActionSelector = mockSelector.Object;
  77. // Act
  78. HttpResponseMessage message = api.ExecuteAsync(
  79. controllerContext,
  80. CancellationToken.None).Result;
  81. // Assert
  82. Assert.Equal("This is a test", message.Content.ReadAsStringAsync().Result);
  83. }
  84. [Fact]
  85. public void Default_Get()
  86. {
  87. // Arrange
  88. ApiController api = new UsersController();
  89. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Get });
  90. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController));
  91. // Act
  92. HttpResponseMessage message = api.ExecuteAsync(
  93. controllerContext,
  94. CancellationToken.None).Result;
  95. // Assert
  96. Assert.Equal("Default User", message.Content.ReadAsStringAsync().Result);
  97. }
  98. [Fact]
  99. public void Default_Post()
  100. {
  101. // Arrange
  102. ApiController api = new UsersController();
  103. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Post });
  104. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController));
  105. // Act
  106. HttpResponseMessage message = api.ExecuteAsync(
  107. controllerContext,
  108. CancellationToken.None).Result;
  109. // Assert
  110. Assert.Equal("User Posted", message.Content.ReadAsStringAsync().Result);
  111. }
  112. [Fact]
  113. public void Default_Put()
  114. {
  115. // Arrange
  116. ApiController api = new UsersController();
  117. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Put });
  118. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController));
  119. // Act
  120. HttpResponseMessage message = api.ExecuteAsync(
  121. controllerContext,
  122. CancellationToken.None).Result;
  123. // Assert
  124. Assert.Equal("User Updated", message.Content.ReadAsStringAsync().Result);
  125. }
  126. [Fact]
  127. public void Default_Delete()
  128. {
  129. // Arrange
  130. ApiController api = new UsersController();
  131. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, request: new HttpRequestMessage() { Method = HttpMethod.Delete });
  132. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersController));
  133. // Act
  134. HttpResponseMessage message = api.ExecuteAsync(
  135. controllerContext,
  136. CancellationToken.None).Result;
  137. // Assert
  138. Assert.Equal("User Deleted", message.Content.ReadAsStringAsync().Result);
  139. }
  140. [Fact]
  141. public void Route_ActionName()
  142. {
  143. // Arrange
  144. ApiController api = new UsersRpcController();
  145. HttpRouteData route = new HttpRouteData(new HttpRoute());
  146. route.Values.Add("action", "Admin");
  147. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Post });
  148. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController));
  149. // Act
  150. HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result;
  151. User user = message.Content.ReadAsAsync<User>().Result;
  152. // Assert
  153. Assert.Equal("Yao", user.FirstName);
  154. Assert.Equal("Huang", user.LastName);
  155. }
  156. [Fact]
  157. public void Route_Get_Action_With_Route_Parameters()
  158. {
  159. // Arrange
  160. ApiController api = new UsersRpcController();
  161. HttpRouteData route = new HttpRouteData(new HttpRoute());
  162. route.Values.Add("action", "EchoUser");
  163. route.Values.Add("firstName", "RouteFirstName");
  164. route.Values.Add("lastName", "RouteLastName");
  165. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Post });
  166. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController));
  167. // Act
  168. HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result;
  169. User user = message.Content.ReadAsAsync<User>().Result;
  170. // Assert
  171. Assert.Equal("RouteFirstName", user.FirstName);
  172. Assert.Equal("RouteLastName", user.LastName);
  173. }
  174. [Fact]
  175. public void Route_Get_Action_With_Query_Parameters()
  176. {
  177. // Arrange
  178. ApiController api = new UsersRpcController();
  179. HttpRouteData route = new HttpRouteData(new HttpRoute());
  180. route.Values.Add("action", "EchoUser");
  181. Uri requestUri = new Uri("http://localhost/?firstName=QueryFirstName&lastName=QueryLastName");
  182. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage()
  183. {
  184. Method = HttpMethod.Post,
  185. RequestUri = requestUri
  186. });
  187. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController));
  188. // Act
  189. HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result;
  190. User user = message.Content.ReadAsAsync<User>().Result;
  191. // Assert
  192. Assert.Equal("QueryFirstName", user.FirstName);
  193. Assert.Equal("QueryLastName", user.LastName);
  194. }
  195. [Fact]
  196. public void Route_Post_Action_With_Content_Parameter()
  197. {
  198. // Arrange
  199. ApiController api = new UsersRpcController();
  200. HttpRouteData route = new HttpRouteData(new HttpRoute());
  201. route.Values.Add("action", "EchoUserObject");
  202. User postedUser = new User()
  203. {
  204. FirstName = "SampleFirstName",
  205. LastName = "SampleLastName"
  206. };
  207. HttpRequestMessage request = new HttpRequestMessage() { Method = HttpMethod.Post };
  208. // Create a serialized request because this test directly calls the controller
  209. // which would have normally been working with a serialized request content.
  210. string serializedUserAsString = null;
  211. using (HttpRequestMessage tempRequest = new HttpRequestMessage() { Content = new ObjectContent<User>(postedUser, new XmlMediaTypeFormatter()) })
  212. {
  213. serializedUserAsString = tempRequest.Content.ReadAsStringAsync().Result;
  214. }
  215. StringContent stringContent = new StringContent(serializedUserAsString);
  216. stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
  217. request.Content = stringContent;
  218. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: request);
  219. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(UsersRpcController));
  220. // Act
  221. HttpResponseMessage message = api.ExecuteAsync(
  222. controllerContext,
  223. CancellationToken.None).Result;
  224. User user = message.Content.ReadAsAsync<User>().Result;
  225. // Assert
  226. Assert.Equal(postedUser.FirstName, user.FirstName);
  227. Assert.Equal(postedUser.LastName, user.LastName);
  228. }
  229. [Fact]
  230. public void Invalid_Action_In_Route()
  231. {
  232. // Arrange
  233. ApiController api = new UsersController();
  234. HttpRouteData route = new HttpRouteData(new HttpRoute());
  235. string actionName = "invalidOp";
  236. route.Values.Add("action", actionName);
  237. HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(instance: api, routeData: route, request: new HttpRequestMessage() { Method = HttpMethod.Get });
  238. Type controllerType = typeof(UsersController);
  239. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, controllerType.Name, controllerType);
  240. // Act & Assert
  241. var exception = Assert.Throws<HttpResponseException>(() =>
  242. {
  243. HttpResponseMessage message = api.ExecuteAsync(controllerContext, CancellationToken.None).Result;
  244. });
  245. Assert.Equal(HttpStatusCode.NotFound, exception.Response.StatusCode);
  246. var content = Assert.IsType<ObjectContent<string>>(exception.Response.Content);
  247. Assert.Equal("No action was found on the controller 'UsersController' that matches the name 'invalidOp'.",
  248. content.Value);
  249. }
  250. [Fact]
  251. public void ExecuteAsync_InvokesAuthorizationFilters_ThenInvokesModelBinding_ThenInvokesActionFilters_ThenInvokesAction()
  252. {
  253. List<string> log = new List<string>();
  254. Mock<ApiController> controllerMock = new Mock<ApiController>() { CallBase = true };
  255. var controllerContextMock = new Mock<HttpControllerContext>();
  256. Mock<IActionValueBinder> binderMock = new Mock<IActionValueBinder>();
  257. Mock<HttpActionBinding> actionBindingMock = new Mock<HttpActionBinding>();
  258. actionBindingMock.Setup(b => b.ExecuteBindingAsync(It.IsAny<HttpActionContext>(), It.IsAny<CancellationToken>())).Returns(() => Task.Factory.StartNew(() => { log.Add("model binding"); }));
  259. binderMock.Setup(b => b.GetBinding(It.IsAny<HttpActionDescriptor>())).Returns(actionBindingMock.Object);
  260. HttpConfiguration configuration = new HttpConfiguration();
  261. HttpControllerContext controllerContext = controllerContextMock.Object;
  262. controllerContext.Configuration = configuration;
  263. controllerContext.ControllerDescriptor = new HttpControllerDescriptor(configuration, "test", typeof(object));
  264. var actionFilterMock = CreateActionFilterMock((ac, ct, cont) =>
  265. {
  266. log.Add("action filters");
  267. return cont();
  268. });
  269. var authFilterMock = CreateAuthorizationFilterMock((ac, ct, cont) =>
  270. {
  271. log.Add("auth filters");
  272. return cont();
  273. });
  274. var selectorMock = new Mock<IHttpActionSelector>();
  275. Mock<HttpActionDescriptor> actionDescriptorMock = new Mock<HttpActionDescriptor>();
  276. actionDescriptorMock.Setup( ad => ad.ActionBinding).Returns(actionBindingMock.Object);
  277. actionDescriptorMock.Setup( ad => ad.GetFilterPipeline()).
  278. Returns(new Collection<FilterInfo>(new List<FilterInfo>() { new FilterInfo(actionFilterMock.Object, FilterScope.Action), new FilterInfo(authFilterMock.Object, FilterScope.Action) }));
  279. selectorMock.Setup(s => s.SelectAction(controllerContext)).Returns(actionDescriptorMock.Object);
  280. ApiController controller = controllerMock.Object;
  281. var invokerMock = new Mock<IHttpActionInvoker>();
  282. invokerMock.Setup(i => i.InvokeActionAsync(It.IsAny<HttpActionContext>(), It.IsAny<CancellationToken>()))
  283. .Returns(() => Task.Factory.StartNew(() =>
  284. {
  285. log.Add("action");
  286. return new HttpResponseMessage();
  287. }));
  288. controllerContext.ControllerDescriptor.HttpActionInvoker = invokerMock.Object;
  289. controllerContext.ControllerDescriptor.HttpActionSelector = selectorMock.Object;
  290. controllerContext.ControllerDescriptor.ActionValueBinder = binderMock.Object;
  291. var task = controller.ExecuteAsync(controllerContext, CancellationToken.None);
  292. Assert.NotNull(task);
  293. task.WaitUntilCompleted();
  294. Assert.Equal(new string[] { "auth filters", "model binding", "action filters", "action" }, log.ToArray());
  295. }
  296. [Fact]
  297. public void GetFilters_QueriesFilterProvidersFromServiceResolver()
  298. {
  299. // Arrange
  300. Mock<IDependencyResolver> resolverMock = new Mock<IDependencyResolver>();
  301. Mock<IFilterProvider> filterProviderMock = new Mock<IFilterProvider>();
  302. resolverMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new object[] { filterProviderMock.Object }).Verifiable();
  303. _configurationInstance.ServiceResolver.SetResolver(resolverMock.Object);
  304. HttpActionDescriptor actionDescriptorMock = new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
  305. actionDescriptorMock.Configuration = _configurationInstance;
  306. // Act
  307. actionDescriptorMock.GetFilterPipeline();
  308. // Assert
  309. resolverMock.Verify();
  310. }
  311. [Fact]
  312. public void GetFilters_UsesFilterProvidersToGetFilters()
  313. {
  314. // Arrange
  315. Mock<IDependencyResolver> resolverMock = new Mock<IDependencyResolver>();
  316. Mock<IFilterProvider> filterProviderMock = new Mock<IFilterProvider>();
  317. resolverMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new[] { filterProviderMock.Object });
  318. _configurationInstance.ServiceResolver.SetResolver(resolverMock.Object);
  319. HttpActionDescriptor actionDescriptorMock = new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
  320. actionDescriptorMock.Configuration = _configurationInstance;
  321. // Act
  322. actionDescriptorMock.GetFilterPipeline().ToList();
  323. // Assert
  324. filterProviderMock.Verify(fp => fp.GetFilters(_configurationInstance, actionDescriptorMock));
  325. }
  326. [Fact]
  327. public void RequestPropertyGetterSetterWorks()
  328. {
  329. Assert.Reflection.Property(new Mock<ApiController>().Object,
  330. c => c.Request, expectedDefaultValue: null, allowNull: false,
  331. roundTripTestValue: new HttpRequestMessage());
  332. }
  333. [Fact]
  334. public void ConfigurationPropertyGetterSetterWorks()
  335. {
  336. Assert.Reflection.Property(new Mock<ApiController>().Object,
  337. c => c.Configuration, expectedDefaultValue: null, allowNull: false,
  338. roundTripTestValue: new HttpConfiguration());
  339. }
  340. [Fact]
  341. public void ModelStatePropertyGetterWorks()
  342. {
  343. // Arrange
  344. ApiController controller = new Mock<ApiController>().Object;
  345. // Act
  346. ModelStateDictionary expected = new ModelStateDictionary();
  347. expected.Add("a", new ModelState() { Value = new ValueProviders.ValueProviderResult("result", "attempted", CultureInfo.InvariantCulture) });
  348. controller.ModelState.Add("a", new ModelState() { Value = new ValueProviders.ValueProviderResult("result", "attempted", CultureInfo.InvariantCulture) });
  349. // Assert
  350. Assert.Equal(expected.Count, controller.ModelState.Count);
  351. }
  352. // TODO: Move these tests to ActionDescriptorTest
  353. [Fact]
  354. public void GetFilters_OrdersFilters()
  355. {
  356. // Arrange
  357. HttpActionDescriptor actionDescriptorMock = new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
  358. actionDescriptorMock.Configuration = _configurationInstance;
  359. var globalFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Global);
  360. var actionFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Action);
  361. var controllerFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Controller);
  362. Mock<IDependencyResolver> resolverMock = BuildFilterProvidingDependencyResolver(_configurationInstance, actionDescriptorMock, globalFilter, actionFilter, controllerFilter);
  363. _configurationInstance.ServiceResolver.SetResolver(resolverMock.Object);
  364. // Act
  365. var result = actionDescriptorMock.GetFilterPipeline().ToArray();
  366. // Assert
  367. Assert.Equal(new[] { globalFilter, controllerFilter, actionFilter }, result);
  368. }
  369. [Fact]
  370. public void GetFilters_RemovesDuplicateUniqueFiltersKeepingMostSpecificScope()
  371. {
  372. // Arrange
  373. HttpActionDescriptor actionDescriptorMock = new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
  374. actionDescriptorMock.Configuration = _configurationInstance;
  375. var multiActionFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Action);
  376. var multiGlobalFilter = new FilterInfo(new TestMultiFilter(), FilterScope.Global);
  377. var uniqueControllerFilter = new FilterInfo(new TestUniqueFilter(), FilterScope.Controller);
  378. var uniqueActionFilter = new FilterInfo(new TestUniqueFilter(), FilterScope.Action);
  379. Mock<IDependencyResolver> resolverMock = BuildFilterProvidingDependencyResolver(
  380. _configurationInstance, actionDescriptorMock,
  381. multiActionFilter, multiGlobalFilter, uniqueControllerFilter, uniqueActionFilter);
  382. _configurationInstance.ServiceResolver.SetResolver(resolverMock.Object);
  383. // Act
  384. var result = actionDescriptorMock.GetFilterPipeline().ToArray();
  385. // Assert
  386. Assert.Equal(new[] { multiGlobalFilter, multiActionFilter, uniqueActionFilter }, result);
  387. }
  388. [Fact]
  389. public void InvokeActionWithActionFilters_ChainsFiltersInOrderFollowedByInnerActionContinuation()
  390. {
  391. // Arrange
  392. List<string> log = new List<string>();
  393. Mock<IActionFilter> globalFilterMock = CreateActionFilterMock((ctx, ct, continuation) =>
  394. {
  395. log.Add("globalFilter");
  396. return continuation();
  397. });
  398. Mock<IActionFilter> actionFilterMock = CreateActionFilterMock((ctx, ct, continuation) =>
  399. {
  400. log.Add("actionFilter");
  401. return continuation();
  402. });
  403. Func<Task<HttpResponseMessage>> innerAction = () => Task<HttpResponseMessage>.Factory.StartNew(() =>
  404. {
  405. log.Add("innerAction");
  406. return null;
  407. });
  408. List<IActionFilter> filters = new List<IActionFilter>() {
  409. globalFilterMock.Object,
  410. actionFilterMock.Object,
  411. };
  412. // Act
  413. var result = ApiController.InvokeActionWithActionFilters(_actionContextInstance, CancellationToken.None, filters, innerAction);
  414. // Assert
  415. Assert.NotNull(result);
  416. var resultTask = result();
  417. Assert.NotNull(resultTask);
  418. resultTask.WaitUntilCompleted();
  419. Assert.Equal(new[] { "globalFilter", "actionFilter", "innerAction" }, log.ToArray());
  420. globalFilterMock.Verify();
  421. actionFilterMock.Verify();
  422. }
  423. [Fact]
  424. public void InvokeActionWithAuthorizationFilters_ChainsFiltersInOrderFollowedByInnerActionContinuation()
  425. {
  426. // Arrange
  427. List<string> log = new List<string>();
  428. Mock<IAuthorizationFilter> globalFilterMock = CreateAuthorizationFilterMock((ctx, ct, continuation) =>
  429. {
  430. log.Add("globalFilter");
  431. return continuation();
  432. });
  433. Mock<IAuthorizationFilter> actionFilterMock = CreateAuthorizationFilterMock((ctx, ct, continuation) =>
  434. {
  435. log.Add("actionFilter");
  436. return continuation();
  437. });
  438. Func<Task<HttpResponseMessage>> innerAction = () => Task<HttpResponseMessage>.Factory.StartNew(() =>
  439. {
  440. log.Add("innerAction");
  441. return null;
  442. });
  443. List<IAuthorizationFilter> filters = new List<IAuthorizationFilter>() {
  444. globalFilterMock.Object,
  445. actionFilterMock.Object,
  446. };
  447. // Act
  448. var result = ApiController.InvokeActionWithAuthorizationFilters(_actionContextInstance, CancellationToken.None, filters, innerAction);
  449. // Assert
  450. Assert.NotNull(result);
  451. var resultTask = result();
  452. Assert.NotNull(resultTask);
  453. resultTask.WaitUntilCompleted();
  454. Assert.Equal(new[] { "globalFilter", "actionFilter", "innerAction" }, log.ToArray());
  455. globalFilterMock.Verify();
  456. actionFilterMock.Verify();
  457. }
  458. [Fact]
  459. public void InvokeActionWithExceptionFilters_IfActionTaskIsSuccessful_ReturnsSuccessTask()
  460. {
  461. // Arrange
  462. List<string> log = new List<string>();
  463. var response = new HttpResponseMessage();
  464. var actionTask = TaskHelpers.FromResult(response);
  465. var exceptionFilterMock = CreateExceptionFilterMock((ec, ct) =>
  466. {
  467. log.Add("exceptionFilter");
  468. return Task.Factory.StartNew(() => { });
  469. });
  470. var filters = new[] { exceptionFilterMock.Object };
  471. // Act
  472. var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters);
  473. // Assert
  474. Assert.NotNull(result);
  475. result.WaitUntilCompleted();
  476. Assert.Equal(TaskStatus.RanToCompletion, result.Status);
  477. Assert.Same(response, result.Result);
  478. Assert.Equal(new string[] { }, log.ToArray());
  479. }
  480. [Fact]
  481. public void InvokeActionWithExceptionFilters_IfActionTaskIsCanceled_ReturnsCanceledTask()
  482. {
  483. // Arrange
  484. List<string> log = new List<string>();
  485. var actionTask = TaskHelpers.Canceled<HttpResponseMessage>();
  486. var exceptionFilterMock = CreateExceptionFilterMock((ec, ct) =>
  487. {
  488. log.Add("exceptionFilter");
  489. return Task.Factory.StartNew(() => { });
  490. });
  491. var filters = new[] { exceptionFilterMock.Object };
  492. // Act
  493. var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters);
  494. // Assert
  495. Assert.NotNull(result);
  496. result.WaitUntilCompleted();
  497. Assert.Equal(TaskStatus.Canceled, result.Status);
  498. Assert.Equal(new string[] { }, log.ToArray());
  499. }
  500. [Fact]
  501. public void InvokeActionWithExceptionFilters_IfActionTaskIsFaulted_ExecutesFiltersAndReturnsFaultedTaskIfNotHandled()
  502. {
  503. // Arrange
  504. List<string> log = new List<string>();
  505. var exception = new Exception();
  506. var actionTask = TaskHelpers.FromError<HttpResponseMessage>(exception);
  507. Exception exceptionSeenByFilter = null;
  508. var exceptionFilterMock = CreateExceptionFilterMock((ec, ct) =>
  509. {
  510. exceptionSeenByFilter = ec.Exception;
  511. log.Add("exceptionFilter");
  512. return Task.Factory.StartNew(() => { });
  513. });
  514. var filters = new[] { exceptionFilterMock.Object };
  515. // Act
  516. var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters);
  517. // Assert
  518. Assert.NotNull(result);
  519. result.WaitUntilCompleted();
  520. Assert.Equal(TaskStatus.Faulted, result.Status);
  521. Assert.Same(exception, result.Exception.InnerException);
  522. Assert.Same(exception, exceptionSeenByFilter);
  523. Assert.Equal(new string[] { "exceptionFilter" }, log.ToArray());
  524. }
  525. [Fact]
  526. public void InvokeActionWithExceptionFilters_IfActionTaskIsFaulted_ExecutesFiltersAndReturnsResultIfHandled()
  527. {
  528. // Arrange
  529. List<string> log = new List<string>();
  530. var exception = new Exception();
  531. var actionTask = TaskHelpers.FromError<HttpResponseMessage>(exception);
  532. HttpResponseMessage globalFilterResponse = new HttpResponseMessage();
  533. HttpResponseMessage actionFilterResponse = new HttpResponseMessage();
  534. HttpResponseMessage resultSeenByGlobalFilter = null;
  535. var globalFilterMock = CreateExceptionFilterMock((ec, ct) =>
  536. {
  537. log.Add("globalFilter");
  538. resultSeenByGlobalFilter = ec.Response;
  539. ec.Response = globalFilterResponse;
  540. return Task.Factory.StartNew(() => { });
  541. });
  542. var actionFilterMock = CreateExceptionFilterMock((ec, ct) =>
  543. {
  544. log.Add("actionFilter");
  545. ec.Response = actionFilterResponse;
  546. return Task.Factory.StartNew(() => { });
  547. });
  548. var filters = new[] { globalFilterMock.Object, actionFilterMock.Object };
  549. // Act
  550. var result = ApiController.InvokeActionWithExceptionFilters(actionTask, _actionContextInstance, CancellationToken.None, filters);
  551. // Assert
  552. Assert.NotNull(result);
  553. result.WaitUntilCompleted();
  554. Assert.Equal(TaskStatus.RanToCompletion, result.Status);
  555. Assert.Same(globalFilterResponse, result.Result);
  556. Assert.Same(actionFilterResponse, resultSeenByGlobalFilter);
  557. Assert.Equal(new string[] { "actionFilter", "globalFilter" }, log.ToArray());
  558. }
  559. [Fact, RestoreThreadPrincipal]
  560. public void User_ReturnsThreadPrincipal()
  561. {
  562. // Arrange
  563. ApiController controller = new Mock<ApiController>().Object;
  564. IPrincipal principal = new GenericPrincipal(new GenericIdentity("joe"), new string[0]);
  565. Thread.CurrentPrincipal = principal;
  566. // Act
  567. IPrincipal result = controller.User;
  568. // Assert
  569. Assert.Same(result, principal);
  570. }
  571. private Mock<IAuthorizationFilter> CreateAuthorizationFilterMock(Func<HttpActionContext, CancellationToken, Func<Task<HttpResponseMessage>>, Task<HttpResponseMessage>> implementation)
  572. {
  573. Mock<IAuthorizationFilter> filterMock = new Mock<IAuthorizationFilter>();
  574. filterMock.Setup(f => f.ExecuteAuthorizationFilterAsync(It.IsAny<HttpActionContext>(),
  575. CancellationToken.None,
  576. It.IsAny<Func<Task<HttpResponseMessage>>>()))
  577. .Returns(implementation)
  578. .Verifiable();
  579. return filterMock;
  580. }
  581. private Mock<IActionFilter> CreateActionFilterMock(Func<HttpActionContext, CancellationToken, Func<Task<HttpResponseMessage>>, Task<HttpResponseMessage>> implementation)
  582. {
  583. Mock<IActionFilter> filterMock = new Mock<IActionFilter>();
  584. filterMock.Setup(f => f.ExecuteActionFilterAsync(It.IsAny<HttpActionContext>(),
  585. CancellationToken.None,
  586. It.IsAny<Func<Task<HttpResponseMessage>>>()))
  587. .Returns(implementation)
  588. .Verifiable();
  589. return filterMock;
  590. }
  591. private Mock<IExceptionFilter> CreateExceptionFilterMock(Func<HttpActionExecutedContext, CancellationToken, Task> implementation)
  592. {
  593. Mock<IExceptionFilter> filterMock = new Mock<IExceptionFilter>();
  594. filterMock.Setup(f => f.ExecuteExceptionFilterAsync(It.IsAny<HttpActionExecutedContext>(),
  595. CancellationToken.None))
  596. .Returns(implementation)
  597. .Verifiable();
  598. return filterMock;
  599. }
  600. private static Mock<IDependencyResolver> BuildFilterProvidingDependencyResolver(HttpConfiguration configuration, HttpActionDescriptor action, params FilterInfo[] filters)
  601. {
  602. Mock<IDependencyResolver> resolverMock = new Mock<IDependencyResolver>();
  603. Mock<IFilterProvider> filterProviderMock = new Mock<IFilterProvider>();
  604. resolverMock.Setup(r => r.GetServices(typeof(IFilterProvider))).Returns(new[] { filterProviderMock.Object });
  605. filterProviderMock.Setup(fp => fp.GetFilters(configuration, action)).Returns(filters);
  606. return resolverMock;
  607. }
  608. /// <summary>
  609. /// Simple IFilter implementation with AllowMultiple = true
  610. /// </summary>
  611. public class TestMultiFilter : IFilter
  612. {
  613. public bool AllowMultiple
  614. {
  615. get { return true; }
  616. }
  617. }
  618. /// <summary>
  619. /// Simple IFilter implementation with AllowMultiple = false
  620. /// </summary>
  621. public class TestUniqueFilter : IFilter
  622. {
  623. public bool AllowMultiple
  624. {
  625. get { return false; }
  626. }
  627. }
  628. }
  629. }