PageRenderTime 732ms CodeModel.GetById 47ms RepoModel.GetById 5ms app.codeStats 1ms

/test/System.Web.Mvc.Test/Test/ControllerActionInvokerTest.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 1084 lines | 739 code | 200 blank | 145 comment | 1 complexity | 6f04a89a462aa58d962227ec5d187fa2 MD5 | raw file
  1. using System.Collections.Generic;
  2. using System.ComponentModel;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Threading;
  7. using Microsoft.Web.UnitTestUtil;
  8. using Moq;
  9. using Xunit;
  10. using Assert = Microsoft.TestCommon.AssertEx;
  11. namespace System.Web.Mvc.Test
  12. {
  13. [CLSCompliant(false)]
  14. public class ControllerActionInvokerTest
  15. {
  16. [Fact]
  17. public void CreateActionResultWithActionResultParameterReturnsParameterUnchanged()
  18. {
  19. // Arrange
  20. ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper();
  21. ActionResult originalResult = new JsonResult();
  22. // Act
  23. ActionResult returnedActionResult = invoker.PublicCreateActionResult(null, null, originalResult);
  24. // Assert
  25. Assert.Same(originalResult, returnedActionResult);
  26. }
  27. [Fact]
  28. public void CreateActionResultWithNullParameterReturnsEmptyResult()
  29. {
  30. // Arrange
  31. ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper();
  32. // Act
  33. ActionResult returnedActionResult = invoker.PublicCreateActionResult(null, null, null);
  34. // Assert
  35. Assert.IsType<EmptyResult>(returnedActionResult);
  36. }
  37. [Fact]
  38. public void CreateActionResultWithObjectParameterReturnsContentResult()
  39. {
  40. // Arrange
  41. ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper();
  42. object originalReturnValue = new CultureReflector();
  43. // Act
  44. ActionResult returnedActionResult = invoker.PublicCreateActionResult(null, null, originalReturnValue);
  45. // Assert
  46. ContentResult contentResult = Assert.IsType<ContentResult>(returnedActionResult);
  47. Assert.Equal("ivl", contentResult.Content);
  48. }
  49. [Fact]
  50. public void FindAction()
  51. {
  52. // Arrange
  53. EmptyController controller = new EmptyController();
  54. ControllerContext controllerContext = GetControllerContext(controller);
  55. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  56. ActionDescriptor expectedAd = new Mock<ActionDescriptor>().Object;
  57. Mock<ControllerDescriptor> mockCd = new Mock<ControllerDescriptor>();
  58. mockCd.Setup(cd => cd.FindAction(controllerContext, "someAction")).Returns(expectedAd);
  59. // Act
  60. ActionDescriptor returnedAd = helper.PublicFindAction(controllerContext, mockCd.Object, "someAction");
  61. // Assert
  62. Assert.Equal(expectedAd, returnedAd);
  63. }
  64. [Fact]
  65. public void FindActionDoesNotMatchConstructor()
  66. {
  67. // FindActionMethod() shouldn't match special-named methods like type constructors.
  68. // Arrange
  69. Controller controller = new FindMethodController();
  70. ControllerContext context = GetControllerContext(controller);
  71. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  72. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  73. // Act
  74. ActionDescriptor ad = helper.PublicFindAction(context, cd, ".ctor");
  75. ActionDescriptor ad2 = helper.PublicFindAction(context, cd, "FindMethodController");
  76. // Assert
  77. Assert.Null(ad);
  78. Assert.Null(ad2);
  79. }
  80. [Fact]
  81. public void FindActionDoesNotMatchEvent()
  82. {
  83. // FindActionMethod() should skip methods that aren't publicly visible.
  84. // Arrange
  85. Controller controller = new FindMethodController();
  86. ControllerContext context = GetControllerContext(controller);
  87. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  88. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  89. // Act
  90. ActionDescriptor ad = helper.PublicFindAction(context, cd, "add_Event");
  91. // Assert
  92. Assert.Null(ad);
  93. }
  94. [Fact]
  95. public void FindActionDoesNotMatchInternalMethod()
  96. {
  97. // FindActionMethod() should skip methods that aren't publicly visible.
  98. // Arrange
  99. Controller controller = new FindMethodController();
  100. ControllerContext context = GetControllerContext(controller);
  101. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  102. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  103. // Act
  104. ActionDescriptor ad = helper.PublicFindAction(context, cd, "InternalMethod");
  105. // Assert
  106. Assert.Null(ad);
  107. }
  108. [Fact]
  109. public void FindActionDoesNotMatchMethodsDefinedOnControllerType()
  110. {
  111. // FindActionMethod() shouldn't match methods originally defined on the Controller type, e.g. Dispose().
  112. // Arrange
  113. Controller controller = new BlankController();
  114. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(BlankController));
  115. ControllerContext context = GetControllerContext(controller);
  116. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  117. var methods = typeof(Controller).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
  118. // Act & Assert
  119. foreach (var method in methods)
  120. {
  121. bool wasFound = true;
  122. try
  123. {
  124. ActionDescriptor ad = helper.PublicFindAction(context, cd, method.Name);
  125. wasFound = (ad != null);
  126. }
  127. finally
  128. {
  129. Assert.False(wasFound, "FindAction() should return false for methods defined on the Controller class: " + method);
  130. }
  131. }
  132. }
  133. [Fact]
  134. public void FindActionDoesNotMatchMethodsDefinedOnObjectType()
  135. {
  136. // FindActionMethod() shouldn't match methods originally defined on the Object type, e.g. ToString().
  137. // Arrange
  138. Controller controller = new FindMethodController();
  139. ControllerContext context = GetControllerContext(controller);
  140. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  141. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  142. // Act
  143. ActionDescriptor ad = helper.PublicFindAction(context, cd, "ToString");
  144. // Assert
  145. Assert.Null(ad);
  146. }
  147. [Fact]
  148. public void FindActionDoesNotMatchNonActionMethod()
  149. {
  150. // FindActionMethod() should respect the [NonAction] attribute.
  151. // Arrange
  152. Controller controller = new FindMethodController();
  153. ControllerContext context = GetControllerContext(controller);
  154. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  155. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  156. // Act
  157. ActionDescriptor ad = helper.PublicFindAction(context, cd, "NonActionMethod");
  158. // Assert
  159. Assert.Null(ad);
  160. }
  161. [Fact]
  162. public void FindActionDoesNotMatchOverriddenNonActionMethod()
  163. {
  164. // FindActionMethod() should trace the method's inheritance chain looking for the [NonAction] attribute.
  165. // Arrange
  166. Controller controller = new DerivedFindMethodController();
  167. ControllerContext context = GetControllerContext(controller);
  168. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(DerivedFindMethodController));
  169. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  170. // Act
  171. ActionDescriptor ad = helper.PublicFindAction(context, cd, "InternalMethod");
  172. // Assert
  173. Assert.Null(ad);
  174. }
  175. [Fact]
  176. public void FindActionDoesNotMatchPrivateMethod()
  177. {
  178. // FindActionMethod() should skip methods that aren't publicly visible.
  179. // Arrange
  180. Controller controller = new FindMethodController();
  181. ControllerContext context = GetControllerContext(controller);
  182. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  183. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  184. // Act
  185. ActionDescriptor ad = helper.PublicFindAction(context, cd, "PrivateMethod");
  186. // Assert
  187. Assert.Null(ad);
  188. }
  189. [Fact]
  190. public void FindActionDoesNotMatchProperty()
  191. {
  192. // FindActionMethod() shouldn't match special-named methods like property getters.
  193. // Arrange
  194. Controller controller = new FindMethodController();
  195. ControllerContext context = GetControllerContext(controller);
  196. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  197. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  198. // Act
  199. ActionDescriptor ad = helper.PublicFindAction(context, cd, "get_Property");
  200. // Assert
  201. Assert.Null(ad);
  202. }
  203. [Fact]
  204. public void FindActionDoesNotMatchProtectedMethod()
  205. {
  206. // FindActionMethod() should skip methods that aren't publicly visible.
  207. // Arrange
  208. Controller controller = new FindMethodController();
  209. ControllerContext context = GetControllerContext(controller);
  210. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  211. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  212. // Act
  213. ActionDescriptor ad = helper.PublicFindAction(context, cd, "ProtectedMethod");
  214. // Assert
  215. Assert.Null(ad);
  216. }
  217. [Fact]
  218. public void FindActionIsCaseInsensitive()
  219. {
  220. // Arrange
  221. Controller controller = new FindMethodController();
  222. ControllerContext context = GetControllerContext(controller);
  223. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(FindMethodController));
  224. MethodInfo expectedMethodInfo = typeof(FindMethodController).GetMethod("ValidActionMethod");
  225. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  226. // Act
  227. ActionDescriptor ad1 = helper.PublicFindAction(context, cd, "validactionmethod");
  228. ActionDescriptor ad2 = helper.PublicFindAction(context, cd, "VALIDACTIONMETHOD");
  229. // Assert
  230. ReflectedActionDescriptor rad1 = Assert.IsType<ReflectedActionDescriptor>(ad1);
  231. Assert.Same(expectedMethodInfo, rad1.MethodInfo);
  232. ReflectedActionDescriptor rad2 = Assert.IsType<ReflectedActionDescriptor>(ad2);
  233. Assert.Same(expectedMethodInfo, rad2.MethodInfo);
  234. }
  235. [Fact]
  236. public void FindActionMatchesActionMethodWithClosedGenerics()
  237. {
  238. // FindActionMethod() should work with generic methods as long as there are no open types.
  239. // Arrange
  240. Controller controller = new GenericFindMethodController<int>();
  241. ControllerContext context = GetControllerContext(controller);
  242. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(GenericFindMethodController<int>));
  243. MethodInfo expectedMethodInfo = typeof(GenericFindMethodController<int>).GetMethod("ClosedGenericMethod");
  244. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  245. // Act
  246. ActionDescriptor ad = helper.PublicFindAction(context, cd, "ClosedGenericMethod");
  247. // Assert
  248. ReflectedActionDescriptor rad = Assert.IsType<ReflectedActionDescriptor>(ad);
  249. Assert.Same(expectedMethodInfo, rad.MethodInfo);
  250. }
  251. [Fact]
  252. public void FindActionMatchesNewActionMethodsHidingNonActionMethods()
  253. {
  254. // FindActionMethod() should stop looking for [NonAction] in the method's inheritance chain when it sees
  255. // that a method in a derived class hides the a method in the base class.
  256. // Arrange
  257. Controller controller = new DerivedFindMethodController();
  258. ControllerContext context = GetControllerContext(controller);
  259. ControllerDescriptor cd = new ReflectedControllerDescriptor(typeof(DerivedFindMethodController));
  260. MethodInfo expectedMethodInfo = typeof(DerivedFindMethodController).GetMethod("DerivedIsActionMethod");
  261. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  262. // Act
  263. ActionDescriptor ad = helper.PublicFindAction(context, cd, "DerivedIsActionMethod");
  264. // Assert
  265. ReflectedActionDescriptor rad = Assert.IsType<ReflectedActionDescriptor>(ad);
  266. Assert.Same(expectedMethodInfo, rad.MethodInfo);
  267. }
  268. [Fact]
  269. public void GetControllerDescriptor()
  270. {
  271. // Arrange
  272. EmptyController controller = new EmptyController();
  273. ControllerContext controllerContext = GetControllerContext(controller);
  274. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  275. // Act
  276. ControllerDescriptor cd = helper.PublicGetControllerDescriptor(controllerContext);
  277. // Assert
  278. Assert.IsType<ReflectedControllerDescriptor>(cd);
  279. Assert.Equal(typeof(EmptyController), cd.ControllerType);
  280. }
  281. [Fact]
  282. public void GetFiltersSplitsFilterObjectsIntoFilterInfo()
  283. {
  284. // Arrange
  285. IActionFilter actionFilter = new Mock<IActionFilter>().Object;
  286. IResultFilter resultFilter = new Mock<IResultFilter>().Object;
  287. IAuthorizationFilter authFilter = new Mock<IAuthorizationFilter>().Object;
  288. IExceptionFilter exFilter = new Mock<IExceptionFilter>().Object;
  289. object noneOfTheAbove = new object();
  290. ControllerActionInvokerHelper invoker = new ControllerActionInvokerHelper(actionFilter, authFilter, exFilter, resultFilter, noneOfTheAbove);
  291. ControllerContext context = new ControllerContext();
  292. ActionDescriptor descriptor = new Mock<ActionDescriptor>().Object;
  293. // Act
  294. FilterInfo result = invoker.PublicGetFilters(context, descriptor);
  295. // Assert
  296. Assert.Same(actionFilter, result.ActionFilters.Single());
  297. Assert.Same(authFilter, result.AuthorizationFilters.Single());
  298. Assert.Same(exFilter, result.ExceptionFilters.Single());
  299. Assert.Same(resultFilter, result.ResultFilters.Single());
  300. }
  301. [Fact]
  302. public void GetParameterValueAllowsAllSubpropertiesIfBindAttributeNotSpecified()
  303. {
  304. // Arrange
  305. CustomConverterController controller = new CustomConverterController();
  306. ControllerContext controllerContext = GetControllerContext(controller);
  307. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  308. ParameterInfo paramWithoutBindAttribute = typeof(CustomConverterController).GetMethod("ParameterWithoutBindAttribute").GetParameters()[0];
  309. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithoutBindAttribute, new Mock<ActionDescriptor>().Object);
  310. // Act
  311. object valueWithoutBindAttribute = helper.PublicGetParameterValue(controllerContext, pd);
  312. // Assert
  313. Assert.Equal("foo=True&bar=True", valueWithoutBindAttribute);
  314. }
  315. [Fact]
  316. public void GetParameterValueResolvesConvertersInCorrectOrderOfPrecedence()
  317. {
  318. // Order of precedence:
  319. // 1. Attributes on the parameter itself
  320. // 2. Query the global converter provider
  321. // Arrange
  322. CustomConverterController controller = new CustomConverterController();
  323. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" } };
  324. ControllerContext controllerContext = GetControllerContext(controller, values);
  325. controller.ControllerContext = controllerContext;
  326. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  327. ParameterInfo paramWithOneConverter = typeof(CustomConverterController).GetMethod("ParameterHasOneConverter").GetParameters()[0];
  328. ReflectedParameterDescriptor pdOneConverter = new ReflectedParameterDescriptor(paramWithOneConverter, new Mock<ActionDescriptor>().Object);
  329. ParameterInfo paramWithNoConverters = typeof(CustomConverterController).GetMethod("ParameterHasNoConverters").GetParameters()[0];
  330. ReflectedParameterDescriptor pdNoConverters = new ReflectedParameterDescriptor(paramWithNoConverters, new Mock<ActionDescriptor>().Object);
  331. // Act
  332. object valueWithOneConverter = helper.PublicGetParameterValue(controllerContext, pdOneConverter);
  333. object valueWithNoConverters = helper.PublicGetParameterValue(controllerContext, pdNoConverters);
  334. // Assert
  335. Assert.Equal("foo_String", valueWithOneConverter);
  336. Assert.Equal("fooValue", valueWithNoConverters);
  337. }
  338. [Fact]
  339. public void GetParameterValueRespectsBindAttribute()
  340. {
  341. // Arrange
  342. CustomConverterController controller = new CustomConverterController();
  343. ControllerContext controllerContext = GetControllerContext(controller);
  344. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  345. ParameterInfo paramWithBindAttribute = typeof(CustomConverterController).GetMethod("ParameterHasBindAttribute").GetParameters()[0];
  346. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithBindAttribute, new Mock<ActionDescriptor>().Object);
  347. // Act
  348. object valueWithBindAttribute = helper.PublicGetParameterValue(controllerContext, pd);
  349. // Assert
  350. Assert.Equal("foo=True&bar=False", valueWithBindAttribute);
  351. }
  352. [Fact]
  353. public void GetParameterValueRespectsBindAttributePrefix()
  354. {
  355. // Arrange
  356. CustomConverterController controller = new CustomConverterController();
  357. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" }, { "bar", "barValue" } };
  358. ControllerContext controllerContext = GetControllerContext(controller, values);
  359. controller.ControllerContext = controllerContext;
  360. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  361. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasFieldPrefix").GetParameters()[0];
  362. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock<ActionDescriptor>().Object);
  363. // Act
  364. object parameterValue = helper.PublicGetParameterValue(controllerContext, pd);
  365. // Assert
  366. Assert.Equal("barValue", parameterValue);
  367. }
  368. [Fact]
  369. public void GetParameterValueRespectsBindAttributePrefixOnComplexType()
  370. {
  371. // Arrange
  372. CustomConverterController controller = new CustomConverterController();
  373. Dictionary<string, object> values = new Dictionary<string, object> { { "intprop", "123" }, { "stringprop", "hello" } };
  374. ControllerContext controllerContext = GetControllerContext(controller, values);
  375. controller.ControllerContext = controllerContext;
  376. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  377. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasPrefixAndComplexType").GetParameters()[0];
  378. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock<ActionDescriptor>().Object);
  379. // Act
  380. MySimpleModel parameterValue = helper.PublicGetParameterValue(controllerContext, pd) as MySimpleModel;
  381. // Assert
  382. Assert.Null(parameterValue);
  383. }
  384. [Fact]
  385. public void GetParameterValueRespectsBindAttributeNullPrefix()
  386. {
  387. // Arrange
  388. CustomConverterController controller = new CustomConverterController();
  389. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" }, { "bar", "barValue" } };
  390. ControllerContext controllerContext = GetControllerContext(controller, values);
  391. controller.ControllerContext = controllerContext;
  392. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  393. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasNullFieldPrefix").GetParameters()[0];
  394. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock<ActionDescriptor>().Object);
  395. // Act
  396. object parameterValue = helper.PublicGetParameterValue(controllerContext, pd);
  397. // Assert
  398. Assert.Equal("fooValue", parameterValue);
  399. }
  400. [Fact]
  401. public void GetParameterValueRespectsBindAttributeNullPrefixOnComplexType()
  402. {
  403. // Arrange
  404. CustomConverterController controller = new CustomConverterController();
  405. Dictionary<string, object> values = new Dictionary<string, object> { { "intprop", "123" }, { "stringprop", "hello" } };
  406. ControllerContext controllerContext = GetControllerContext(controller, values);
  407. controller.ControllerContext = controllerContext;
  408. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  409. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasNoPrefixAndComplexType").GetParameters()[0];
  410. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock<ActionDescriptor>().Object);
  411. // Act
  412. MySimpleModel parameterValue = helper.PublicGetParameterValue(controllerContext, pd) as MySimpleModel;
  413. // Assert
  414. Assert.NotNull(parameterValue);
  415. Assert.Equal(123, parameterValue.IntProp);
  416. Assert.Equal("hello", parameterValue.StringProp);
  417. }
  418. [Fact]
  419. public void GetParameterValueRespectsBindAttributeEmptyPrefix()
  420. {
  421. // Arrange
  422. CustomConverterController controller = new CustomConverterController();
  423. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" }, { "bar", "barValue" }, { "intprop", "123" }, { "stringprop", "hello" } };
  424. ControllerContext controllerContext = GetControllerContext(controller, values);
  425. controller.ControllerContext = controllerContext;
  426. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  427. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasEmptyFieldPrefix").GetParameters()[0];
  428. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithFieldPrefix, new Mock<ActionDescriptor>().Object);
  429. // Act
  430. MySimpleModel parameterValue = helper.PublicGetParameterValue(controllerContext, pd) as MySimpleModel;
  431. // Assert
  432. Assert.NotNull(parameterValue);
  433. Assert.Equal(123, parameterValue.IntProp);
  434. Assert.Equal("hello", parameterValue.StringProp);
  435. }
  436. [Fact]
  437. public void GetParameterValueRespectsDefaultValueAttribute()
  438. {
  439. // Arrange
  440. CustomConverterController controller = new CustomConverterController();
  441. ControllerContext controllerContext = GetControllerContext(controller);
  442. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  443. controller.ValueProvider = new SimpleValueProvider();
  444. ParameterInfo paramWithDefaultValueAttribute = typeof(CustomConverterController).GetMethod("ParameterHasDefaultValueAttribute").GetParameters()[0];
  445. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(paramWithDefaultValueAttribute, new Mock<ActionDescriptor>().Object);
  446. // Act
  447. object valueWithDefaultValueAttribute = helper.PublicGetParameterValue(controllerContext, pd);
  448. // Assert
  449. Assert.Equal(42, valueWithDefaultValueAttribute);
  450. }
  451. [Fact]
  452. public void GetParameterValueReturnsNullIfCannotConvertNonRequiredParameter()
  453. {
  454. // Arrange
  455. Dictionary<string, object> dict = new Dictionary<string, object>()
  456. {
  457. { "id", DateTime.Now } // cannot convert DateTime to Nullable<int>
  458. };
  459. var controller = new ParameterTestingController();
  460. ControllerContext context = GetControllerContext(controller, dict);
  461. controller.ControllerContext = context;
  462. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  463. MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesNullableInt");
  464. ParameterInfo[] pis = mi.GetParameters();
  465. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(pis[0], new Mock<ActionDescriptor>().Object);
  466. // Act
  467. object oValue = helper.PublicGetParameterValue(context, pd);
  468. // Assert
  469. Assert.Null(oValue);
  470. }
  471. [Fact]
  472. public void GetParameterValueReturnsNullIfNullableTypeValueNotFound()
  473. {
  474. // Arrange
  475. var controller = new ParameterTestingController();
  476. ControllerContext context = GetControllerContext(controller);
  477. controller.ControllerContext = context;
  478. controller.ValueProvider = new SimpleValueProvider();
  479. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  480. MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesNullableInt");
  481. ParameterInfo[] pis = mi.GetParameters();
  482. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(pis[0], new Mock<ActionDescriptor>().Object);
  483. // Act
  484. object oValue = helper.PublicGetParameterValue(context, pd);
  485. // Assert
  486. Assert.Null(oValue);
  487. }
  488. [Fact]
  489. public void GetParameterValueReturnsNullIfReferenceTypeValueNotFound()
  490. {
  491. // Arrange
  492. var controller = new ParameterTestingController();
  493. ControllerContext context = GetControllerContext(controller);
  494. controller.ControllerContext = context;
  495. controller.ValueProvider = new SimpleValueProvider();
  496. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  497. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo");
  498. ParameterInfo[] pis = mi.GetParameters();
  499. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(pis[0], new Mock<ActionDescriptor>().Object);
  500. // Act
  501. object oValue = helper.PublicGetParameterValue(context, pd);
  502. // Assert
  503. Assert.Null(oValue);
  504. }
  505. [Fact]
  506. public void GetParameterValuesCallsGetParameterValue()
  507. {
  508. // Arrange
  509. ControllerBase controller = new ParameterTestingController();
  510. IDictionary<string, object> dict = new Dictionary<string, object>();
  511. ControllerContext context = GetControllerContext(controller);
  512. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo");
  513. ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "Foo", new Mock<ControllerDescriptor>().Object);
  514. ParameterDescriptor[] pds = ad.GetParameters();
  515. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>() { CallBase = true };
  516. mockHelper.Setup(h => h.PublicGetParameterValue(context, pds[0])).Returns("Myfoo").Verifiable();
  517. mockHelper.Setup(h => h.PublicGetParameterValue(context, pds[1])).Returns("Mybar").Verifiable();
  518. mockHelper.Setup(h => h.PublicGetParameterValue(context, pds[2])).Returns("Mybaz").Verifiable();
  519. ControllerActionInvokerHelper helper = mockHelper.Object;
  520. // Act
  521. IDictionary<string, object> parameters = helper.PublicGetParameterValues(context, ad);
  522. // Assert
  523. Assert.Equal(3, parameters.Count);
  524. Assert.Equal("Myfoo", parameters["foo"]);
  525. Assert.Equal("Mybar", parameters["bar"]);
  526. Assert.Equal("Mybaz", parameters["baz"]);
  527. mockHelper.Verify();
  528. }
  529. [Fact]
  530. public void GetParameterValuesReturnsEmptyDictionaryForParameterlessMethod()
  531. {
  532. // Arrange
  533. var controller = new ParameterTestingController();
  534. ControllerContext context = GetControllerContext(controller);
  535. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  536. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Parameterless");
  537. ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "Parameterless", new Mock<ControllerDescriptor>().Object);
  538. // Act
  539. IDictionary<string, object> parameters = helper.PublicGetParameterValues(context, ad);
  540. // Assert
  541. Assert.Empty(parameters);
  542. }
  543. [Fact]
  544. public void GetParameterValuesReturnsValuesForParametersInOrder()
  545. {
  546. // We need to hook into GetParameterValue() to make sure that GetParameterValues() is calling it.
  547. // Arrange
  548. var controller = new ParameterTestingController();
  549. Dictionary<string, object> dict = new Dictionary<string, object>()
  550. {
  551. { "foo", "MyFoo" },
  552. { "bar", "MyBar" },
  553. { "baz", "MyBaz" }
  554. };
  555. ControllerContext context = GetControllerContext(controller, dict);
  556. controller.ControllerContext = context;
  557. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  558. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo");
  559. ReflectedActionDescriptor ad = new ReflectedActionDescriptor(mi, "Foo", new Mock<ControllerDescriptor>().Object);
  560. // Act
  561. IDictionary<string, object> parameters = helper.PublicGetParameterValues(context, ad);
  562. // Assert
  563. Assert.Equal(3, parameters.Count);
  564. Assert.Equal("MyFoo", parameters["foo"]);
  565. Assert.Equal("MyBar", parameters["bar"]);
  566. Assert.Equal("MyBaz", parameters["baz"]);
  567. }
  568. [Fact]
  569. public void GetParameterValueUsesControllerValueProviderAsValueProvider()
  570. {
  571. // Arrange
  572. Dictionary<string, object> values = new Dictionary<string, object>()
  573. {
  574. { "foo", "fooValue" }
  575. };
  576. CustomConverterController controller = new CustomConverterController();
  577. ControllerContext controllerContext = GetControllerContext(controller, values);
  578. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper();
  579. ParameterInfo parameter = typeof(CustomConverterController).GetMethod("ParameterHasNoConverters").GetParameters()[0];
  580. ReflectedParameterDescriptor pd = new ReflectedParameterDescriptor(parameter, new Mock<ActionDescriptor>().Object);
  581. // Act
  582. object parameterValue = helper.PublicGetParameterValue(controllerContext, pd);
  583. // Assert
  584. Assert.Equal("fooValue", parameterValue);
  585. }
  586. [Fact]
  587. public void InvokeAction()
  588. {
  589. // Arrange
  590. ControllerBase controller = new Mock<ControllerBase>().Object;
  591. ControllerContext context = GetControllerContext(controller);
  592. ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
  593. ActionDescriptor ad = new Mock<ActionDescriptor>().Object;
  594. FilterInfo filterInfo = new FilterInfo();
  595. IDictionary<string, object> parameters = new Dictionary<string, object>();
  596. MethodInfo methodInfo = typeof(object).GetMethod("ToString");
  597. ActionResult actionResult = new EmptyResult();
  598. ActionExecutedContext postContext = new ActionExecutedContext(context, ad, false /* canceled */, null /* exception */)
  599. {
  600. Result = actionResult
  601. };
  602. AuthorizationContext authContext = new AuthorizationContext();
  603. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>() { CallBase = true };
  604. mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable();
  605. mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable();
  606. mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable();
  607. mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable();
  608. mockHelper.Setup(h => h.PublicGetParameterValues(context, ad)).Returns(parameters).Verifiable();
  609. mockHelper.Setup(h => h.PublicInvokeActionMethodWithFilters(context, filterInfo.ActionFilters, ad, parameters)).Returns(postContext).Verifiable();
  610. mockHelper.Setup(h => h.PublicInvokeActionResultWithFilters(context, filterInfo.ResultFilters, actionResult)).Returns((ResultExecutedContext)null).Verifiable();
  611. ControllerActionInvokerHelper helper = mockHelper.Object;
  612. // Act
  613. bool retVal = helper.InvokeAction(context, "SomeMethod");
  614. Assert.True(retVal);
  615. mockHelper.Verify();
  616. }
  617. [Fact]
  618. public void InvokeActionCallsValidateRequestIfAsked()
  619. {
  620. // Arrange
  621. ControllerBase controller = new Mock<ControllerBase>().Object;
  622. controller.ValidateRequest = true;
  623. bool validateInputWasCalled = false;
  624. ControllerContext context = GetControllerContext(controller, null, validateInputCallback: () => { validateInputWasCalled = true; });
  625. ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
  626. ActionDescriptor ad = new Mock<ActionDescriptor>().Object;
  627. FilterInfo filterInfo = new FilterInfo();
  628. AuthorizationContext authContext = new AuthorizationContext();
  629. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>();
  630. mockHelper.CallBase = true;
  631. mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable();
  632. mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable();
  633. mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable();
  634. mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable();
  635. ControllerActionInvokerHelper helper = mockHelper.Object;
  636. // Act
  637. helper.InvokeAction(context, "SomeMethod");
  638. // Assert
  639. Assert.True(validateInputWasCalled);
  640. mockHelper.Verify();
  641. }
  642. [Fact]
  643. public void InvokeActionDoesNotCallValidateRequestForChildActions()
  644. {
  645. // Arrange
  646. ControllerBase controller = new Mock<ControllerBase>().Object;
  647. controller.ValidateRequest = true;
  648. ControllerContext context = GetControllerContext(controller, null);
  649. Mock.Get<ControllerContext>(context).SetupGet(c => c.IsChildAction).Returns(true);
  650. ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
  651. ActionDescriptor ad = new Mock<ActionDescriptor>().Object;
  652. FilterInfo filterInfo = new FilterInfo();
  653. AuthorizationContext authContext = new AuthorizationContext();
  654. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>();
  655. mockHelper.CallBase = true;
  656. mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable();
  657. mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable();
  658. mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable();
  659. mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Returns(authContext).Verifiable();
  660. ControllerActionInvokerHelper helper = mockHelper.Object;
  661. // Act
  662. helper.InvokeAction(context, "SomeMethod"); // No exception thrown
  663. // Assert
  664. mockHelper.Verify();
  665. }
  666. [Fact]
  667. public void InvokeActionMethodFilterWhereContinuationThrowsExceptionAndIsHandled()
  668. {
  669. // Arrange
  670. List<string> actions = new List<string>();
  671. MethodInfo mi = typeof(object).GetMethod("ToString");
  672. Dictionary<string, object> parameters = new Dictionary<string, object>();
  673. Exception exception = new Exception();
  674. ActionDescriptor action = new Mock<ActionDescriptor>().Object;
  675. ActionFilterImpl filter = new ActionFilterImpl()
  676. {
  677. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting"); },
  678. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext)
  679. {
  680. actions.Add("OnActionExecuted");
  681. Assert.Same(exception, filterContext.Exception);
  682. Assert.Same(action, filterContext.ActionDescriptor);
  683. Assert.False(filterContext.ExceptionHandled);
  684. filterContext.ExceptionHandled = true;
  685. }
  686. };
  687. Func<ActionExecutedContext> continuation = delegate
  688. {
  689. actions.Add("Continuation");
  690. throw exception;
  691. };
  692. ActionExecutingContext context = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters);
  693. // Act
  694. ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation);
  695. // Assert
  696. Assert.Equal(3, actions.Count);
  697. Assert.Equal("OnActionExecuting", actions[0]);
  698. Assert.Equal("Continuation", actions[1]);
  699. Assert.Equal("OnActionExecuted", actions[2]);
  700. Assert.Same(exception, result.Exception);
  701. Assert.Same(action, result.ActionDescriptor);
  702. Assert.True(result.ExceptionHandled);
  703. }
  704. [Fact]
  705. public void InvokeActionMethodFilterWhereContinuationThrowsExceptionAndIsNotHandled()
  706. {
  707. // Arrange
  708. List<string> actions = new List<string>();
  709. Dictionary<string, object> parameters = new Dictionary<string, object>();
  710. ActionDescriptor action = new Mock<ActionDescriptor>().Object;
  711. ActionFilterImpl filter = new ActionFilterImpl()
  712. {
  713. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting"); },
  714. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext)
  715. {
  716. Assert.NotNull(filterContext.Exception);
  717. Assert.Equal("Some exception message.", filterContext.Exception.Message);
  718. Assert.Same(action, filterContext.ActionDescriptor);
  719. actions.Add("OnActionExecuted");
  720. }
  721. };
  722. Func<ActionExecutedContext> continuation = delegate
  723. {
  724. actions.Add("Continuation");
  725. throw new Exception("Some exception message.");
  726. };
  727. ActionExecutingContext context = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters);
  728. // Act & Assert
  729. Assert.Throws<Exception>(
  730. delegate { ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation); },
  731. "Some exception message.");
  732. Assert.Equal(3, actions.Count);
  733. Assert.Equal("OnActionExecuting", actions[0]);
  734. Assert.Equal("Continuation", actions[1]);
  735. Assert.Equal("OnActionExecuted", actions[2]);
  736. }
  737. [Fact]
  738. public void InvokeActionMethodFilterWhereContinuationThrowsThreadAbortException()
  739. {
  740. // Arrange
  741. List<string> actions = new List<string>();
  742. ActionResult actionResult = new EmptyResult();
  743. ActionDescriptor action = new Mock<ActionDescriptor>().Object;
  744. ActionFilterImpl filter = new ActionFilterImpl()
  745. {
  746. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) { actions.Add("OnActionExecuting"); },
  747. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext)
  748. {
  749. Thread.ResetAbort();
  750. actions.Add("OnActionExecuted");
  751. Assert.Null(filterContext.Exception);
  752. Assert.False(filterContext.ExceptionHandled);
  753. Assert.Same(action, filterContext.ActionDescriptor);
  754. }
  755. };
  756. Func<ActionExecutedContext> continuation = delegate
  757. {
  758. actions.Add("Continuation");
  759. Thread.CurrentThread.Abort();
  760. return null;
  761. };
  762. ActionExecutingContext context = new ActionExecutingContext(new Mock<ControllerContext>().Object, action, new Dictionary<string, object>());
  763. // Act & Assert
  764. Assert.Throws<ThreadAbortException>(
  765. delegate { ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation); },
  766. "Thread was being aborted.");
  767. Assert.Equal(3, actions.Count);
  768. Assert.Equal("OnActionExecuting", actions[0]);
  769. Assert.Equal("Continuation", actions[1]);
  770. Assert.Equal("OnActionExecuted", actions[2]);
  771. }
  772. [Fact]
  773. public void InvokeActionMethodFilterWhereOnActionExecutingCancels()
  774. {
  775. // Arrange
  776. bool wasCalled = false;
  777. ActionDescriptor ad = new Mock<ActionDescriptor>().Object;
  778. Dictionary<string, object> parameters = new Dictionary<string, object>();
  779. ActionResult actionResult = new EmptyResult();
  780. ActionDescriptor action = new Mock<ActionDescriptor>().Object;
  781. ActionFilterImpl filter = new ActionFilterImpl()
  782. {
  783. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext)
  784. {
  785. Assert.False(wasCalled);
  786. wasCalled = true;
  787. filterContext.Result = actionResult;
  788. },
  789. };
  790. Func<ActionExecutedContext> continuation = delegate
  791. {
  792. Assert.True(false, "The continuation should not be called.");
  793. return null;
  794. };
  795. ActionExecutingContext context = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters);
  796. // Act
  797. ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, context, continuation);
  798. // Assert
  799. Assert.True(wasCalled);
  800. Assert.Null(result.Exception);
  801. Assert.True(result.Canceled);
  802. Assert.Same(actionResult, result.Result);
  803. Assert.Same(action, result.ActionDescriptor);
  804. }
  805. [Fact]
  806. public void InvokeActionMethodFilterWithNormalControlFlow()
  807. {
  808. // Arrange
  809. List<string> actions = new List<string>();
  810. Dictionary<string, object> parameters = new Dictionary<string, object>();
  811. ActionDescriptor action = new Mock<ActionDescriptor>().Object;
  812. ActionExecutingContext preContext = new ActionExecutingContext(GetControllerContext(new EmptyController()), action, parameters);
  813. Mock<ActionExecutedContext> mockPostContext = new Mock<ActionExecutedContext>();
  814. ActionFilterImpl filter = new ActionFilterImpl()
  815. {
  816. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext)
  817. {
  818. Assert.Same(parameters, filterContext.ActionParameters);
  819. Assert.Null(filterContext.Result);
  820. actions.Add("OnActionExecuting");
  821. },
  822. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext)
  823. {
  824. Assert.Equal(mockPostContext.Object, filterContext);
  825. actions.Add("OnActionExecuted");
  826. }
  827. };
  828. Func<ActionExecutedContext> continuation = delegate
  829. {
  830. actions.Add("Continuation");
  831. return mockPostContext.Object;
  832. };
  833. // Act
  834. ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, preContext, continuation);
  835. // Assert
  836. Assert.Equal(3, actions.Count);
  837. Assert.Equal("OnActionExecuting", actions[0]);
  838. Assert.Equal("Continuation", actions[1]);
  839. Assert.Equal("OnActionExecuted", actions[2]);
  840. Assert.Same(result, mockPostContext.Object);
  841. }
  842. [Fact]
  843. public void InvokeActionInvokesExceptionFiltersAndExecutesResultIfExceptionHandled()
  844. {
  845. // Arrange
  846. ControllerBase controller = new Mock<ControllerBase>().Object;
  847. ControllerContext context = GetControllerContext(controller);
  848. ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
  849. ActionDescriptor ad = new Mock<ActionDescriptor>().Object;
  850. FilterInfo filterInfo = new FilterInfo();
  851. Exception exception = new Exception();
  852. ActionResult actionResult = new EmptyResult();
  853. ExceptionContext exContext = new ExceptionContext(context, exception)
  854. {
  855. ExceptionHandled = true,
  856. Result = actionResult
  857. };
  858. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>() { CallBase = true };
  859. mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable();
  860. mockHelper.Setup(h => h.PublicFindAction(context, cd, "SomeMethod")).Returns(ad).Verifiable();
  861. mockHelper.Setup(h => h.PublicGetFilters(context, ad)).Returns(filterInfo).Verifiable();
  862. mockHelper.Setup(h => h.PublicInvokeAuthorizationFilters(context, filterInfo.AuthorizationFilters, ad)).Throws(exception).Verifiable();
  863. mockHelper.Setup(h => h.PublicInvokeExceptionFilters(context, filterInfo.ExceptionFilters, exception)).Returns(exContext).Verifiable();
  864. mockHelper.Setup(h => h.PublicInvokeActionResult(context, actionResult)).Verifiable();
  865. ControllerActionInvokerHelper helper = mockHelper.Object;
  866. // Act
  867. bool retVal = helper.InvokeAction(context, "SomeMethod");
  868. Assert.True(retVal);
  869. mockHelper.Verify();
  870. }
  871. [Fact]
  872. public void InvokeActionInvokesExceptionFiltersAndRethrowsExceptionIfNotHandled()
  873. {
  874. // Arrange
  875. ControllerBase controller = new Mock<ControllerBase>().Object;
  876. ControllerContext context = GetControllerContext(controller);
  877. ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
  878. ActionDescriptor ad = new Mock<ActionDescriptor>().Object;
  879. FilterInfo filterInfo = new FilterInfo();
  880. Exception exception = new Exception();
  881. ExceptionContext exContext = new ExceptionContext(context, exception);
  882. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>() { CallBase = true };
  883. mockHelper.Setup(h => h.PublicGetControllerDescriptor(context)).Returns(cd).Verifiable();
  884. mockHelper.Setup(h => h.PublicFindAction(context, cd, "