PageRenderTime 69ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/Test/SystemWebMvcTest/Mvc/Test/ControllerActionInvokerTest.cs

https://bitbucket.org/markhneedham/aspnet-mvc
C# | 2456 lines | 1785 code | 407 blank | 264 comment | 5 complexity | 4374b283754583dcd0ad6fd4d315596f MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. namespace System.Web.Mvc.Test {
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using System.Threading;
  7. using System.Web.Mvc;
  8. using System.Web.Routing;
  9. using System.Web.TestUtil;
  10. using Microsoft.VisualStudio.TestTools.UnitTesting;
  11. using Moq;
  12. [TestClass]
  13. [CLSCompliant(false)]
  14. public class ControllerActionInvokerTest {
  15. [TestMethod]
  16. public void FindActionMethodDoesNotMatchConstructor() {
  17. // FindActionMethod() shouldn't match special-named methods like type constructors.
  18. // Arrange
  19. var controller = new FindMethodController();
  20. ControllerContext context = GetControllerContext(controller);
  21. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  22. // Act
  23. MethodInfo mi = helper.PublicFindActionMethod(".ctor");
  24. // Assert
  25. Assert.IsNull(mi);
  26. }
  27. [TestMethod]
  28. public void FindActionMethodDoesNotMatchEvent() {
  29. // FindActionMethod() should skip methods that aren't publicly visible.
  30. // Arrange
  31. var controller = new FindMethodController();
  32. ControllerContext context = GetControllerContext(controller);
  33. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  34. // Act
  35. MethodInfo mi = helper.PublicFindActionMethod("add_Event");
  36. // Assert
  37. Assert.IsNull(mi);
  38. }
  39. [TestMethod]
  40. public void FindActionMethodDoesNotMatchInternalMethod() {
  41. // FindActionMethod() should skip methods that aren't publicly visible.
  42. // Arrange
  43. var controller = new FindMethodController();
  44. ControllerContext context = GetControllerContext(controller);
  45. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  46. // Act
  47. MethodInfo mi = helper.PublicFindActionMethod("InternalMethod");
  48. // Assert
  49. Assert.IsNull(mi);
  50. }
  51. [TestMethod]
  52. public void FindActionMethodDoesNotMatchMethodsDefinedOnControllerType() {
  53. // FindActionMethod() shouldn't match methods originally defined on the Controller type, e.g. Dispose().
  54. // Arrange
  55. var controller = new BlankController();
  56. ControllerContext context = GetControllerContext(controller);
  57. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  58. var methods = typeof(Controller).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
  59. // Act & Assert
  60. foreach (var method in methods) {
  61. bool wasFound = true;
  62. try {
  63. MethodInfo mi = helper.PublicFindActionMethod(method.Name);
  64. wasFound = (mi != null);
  65. }
  66. finally {
  67. Assert.IsFalse(wasFound, "FindActionMethod() should return false for methods defined on the Controller class: " + method);
  68. }
  69. }
  70. }
  71. [TestMethod]
  72. public void FindActionMethodDoesNotMatchMethodsDefinedOnObjectType() {
  73. // FindActionMethod() shouldn't match methods originally defined on the Object type, e.g. ToString().
  74. // Arrange
  75. var controller = new FindMethodController();
  76. ControllerContext context = GetControllerContext(controller);
  77. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  78. // Act
  79. MethodInfo mi = helper.PublicFindActionMethod("ToString");
  80. // Assert
  81. Assert.IsNull(mi);
  82. }
  83. [TestMethod]
  84. public void FindActionMethodDoesNotMatchNonActionMethod() {
  85. // FindActionMethod() should respect the [NonAction] attribute.
  86. // Arrange
  87. var controller = new FindMethodController();
  88. ControllerContext context = GetControllerContext(controller);
  89. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  90. // Act
  91. MethodInfo mi = helper.PublicFindActionMethod("NonActionMethod");
  92. // Assert
  93. Assert.IsNull(mi);
  94. }
  95. [TestMethod]
  96. public void FindActionMethodDoesNotMatchOverriddenNonActionMethod() {
  97. // FindActionMethod() should trace the method's inheritance chain looking for the [NonAction] attribute.
  98. // Arrange
  99. var controller = new DerivedFindMethodController();
  100. ControllerContext context = GetControllerContext(controller);
  101. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  102. // Act
  103. MethodInfo mi = helper.PublicFindActionMethod("NonActionMethod");
  104. // Assert
  105. Assert.IsNull(mi);
  106. }
  107. [TestMethod]
  108. public void FindActionMethodDoesNotMatchPrivateMethod() {
  109. // FindActionMethod() should skip methods that aren't publicly visible.
  110. // Arrange
  111. var controller = new FindMethodController();
  112. ControllerContext context = GetControllerContext(controller);
  113. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  114. // Act
  115. MethodInfo mi = helper.PublicFindActionMethod("PrivateMethod");
  116. // Assert
  117. Assert.IsNull(mi);
  118. }
  119. [TestMethod]
  120. public void FindActionMethodDoesNotMatchProperty() {
  121. // FindActionMethod() shouldn't match special-named methods like property getters.
  122. // Arrange
  123. var controller = new FindMethodController();
  124. ControllerContext context = GetControllerContext(controller);
  125. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  126. // Act
  127. MethodInfo mi = helper.PublicFindActionMethod("get_Property");
  128. // Assert
  129. Assert.IsNull(mi);
  130. }
  131. [TestMethod]
  132. public void FindActionMethodDoesNotMatchProtectedMethod() {
  133. // FindActionMethod() should skip methods that aren't publicly visible.
  134. // Arrange
  135. var controller = new FindMethodController();
  136. ControllerContext context = GetControllerContext(controller);
  137. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  138. // Act
  139. MethodInfo mi = helper.PublicFindActionMethod("ProtectedMethod");
  140. // Assert
  141. Assert.IsNull(mi);
  142. }
  143. [TestMethod]
  144. public void FindActionMethodIsCaseInsensitive() {
  145. // Arrange
  146. var controller = new FindMethodController();
  147. ControllerContext context = GetControllerContext(controller);
  148. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  149. MethodInfo expected = typeof(FindMethodController).GetMethod("ValidActionMethod");
  150. // Act
  151. MethodInfo mi1 = helper.PublicFindActionMethod("validactionmethod");
  152. MethodInfo mi2 = helper.PublicFindActionMethod("VALIDACTIONMETHOD");
  153. // Assert
  154. Assert.AreEqual(expected, mi1);
  155. Assert.AreEqual(expected, mi2);
  156. }
  157. [TestMethod]
  158. public void FindActionMethodMatchesNewActionMethodsHidingNonActionMethods() {
  159. // FindActionMethod() should stop looking for [NonAction] in the method's inheritance chain when it sees
  160. // that a method in a derived class hides the a method in the base class.
  161. // Arrange
  162. var controller = new DerivedFindMethodController();
  163. ControllerContext context = GetControllerContext(controller);
  164. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  165. MethodInfo expected = typeof(DerivedFindMethodController).GetMethod("DerivedIsActionMethod");
  166. // Act
  167. MethodInfo mi = helper.PublicFindActionMethod("DerivedIsActionMethod");
  168. // Assert
  169. Assert.AreEqual(expected, mi);
  170. }
  171. [TestMethod]
  172. public void FindActionMethodWithClosedGenerics() {
  173. // FindActionMethod() should work with generic methods as long as there are no open types.
  174. // Arrange
  175. var controller = new GenericFindMethodController<int>();
  176. ControllerContext context = GetControllerContext(controller);
  177. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  178. MethodInfo expected = typeof(GenericFindMethodController<int>).GetMethod("ClosedGenericMethod");
  179. // Act
  180. MethodInfo mi = helper.PublicFindActionMethod("ClosedGenericMethod");
  181. // Assert
  182. Assert.AreEqual(expected, mi);
  183. }
  184. [TestMethod]
  185. public void FindActionMethodWithEmptyActionNameThrows() {
  186. // Arrange
  187. var controller = new FindMethodController();
  188. ControllerContext context = GetControllerContext(controller);
  189. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  190. // Act & Assert
  191. ExceptionHelper.ExpectArgumentExceptionNullOrEmpty(
  192. delegate {
  193. helper.PublicFindActionMethod(String.Empty);
  194. },
  195. "actionName");
  196. }
  197. [TestMethod]
  198. public void FindActionMethodWithNullActionNameThrows() {
  199. // Arrange
  200. var controller = new FindMethodController();
  201. ControllerContext context = GetControllerContext(controller);
  202. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  203. // Act & Assert
  204. ExceptionHelper.ExpectArgumentExceptionNullOrEmpty(
  205. delegate {
  206. helper.PublicFindActionMethod(null /* actionName */);
  207. },
  208. "actionName");
  209. }
  210. [TestMethod]
  211. public void FindActionMethodWithOpenGenericsThrows() {
  212. // FindActionMethod() should throw if matching on a generic method with open types.
  213. // Arrange
  214. var controller = new GenericFindMethodController<int>();
  215. ControllerContext context = GetControllerContext(controller);
  216. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  217. // Act & Assert
  218. ExceptionHelper.ExpectException<InvalidOperationException>(
  219. delegate {
  220. helper.PublicFindActionMethod("OpenGenericMethod");
  221. },
  222. "Cannot call action method 'System.Web.Mvc.ActionResult OpenGenericMethod[U](U)' since it is a generic method.");
  223. }
  224. [TestMethod]
  225. public void FindActionMethodWithOverloadsThrows() {
  226. // FindActionMethod() should throw if it encounters an overloaded method.
  227. // Arrange
  228. var controller = new FindMethodController();
  229. ControllerContext context = GetControllerContext(controller);
  230. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  231. // Act & Assert
  232. ExceptionHelper.ExpectException<InvalidOperationException>(
  233. delegate {
  234. helper.PublicFindActionMethod("MethodOverloaded");
  235. },
  236. @"The current request for action 'MethodOverloaded' on controller type 'FindMethodController' is ambiguous between the following action methods:
  237. System.Web.Mvc.ActionResult MethodOverloaded() on type System.Web.Mvc.Test.ControllerActionInvokerTest+FindMethodController
  238. System.Web.Mvc.ActionResult MethodOverloaded(System.String) on type System.Web.Mvc.Test.ControllerActionInvokerTest+FindMethodController");
  239. }
  240. [TestMethod]
  241. public void FindActionMethodWithValidMethod() {
  242. // Test basic functionality of FindActionMethod() by giving it a known good case.
  243. // Arrange
  244. var controller = new FindMethodController();
  245. ControllerContext context = GetControllerContext(controller);
  246. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  247. MethodInfo expected = typeof(FindMethodController).GetMethod("ValidActionMethod");
  248. // Act
  249. MethodInfo mi = helper.PublicFindActionMethod("ValidActionMethod");
  250. // Assert
  251. Assert.AreEqual(expected, mi);
  252. }
  253. [TestMethod]
  254. public void GetFiltersForActionMethod() {
  255. // Arrange
  256. ControllerBase controller = new GetMemberChainSubderivedController();
  257. ControllerContext context = GetControllerContext(controller);
  258. MethodInfo methodInfo = typeof(GetMemberChainSubderivedController).GetMethod("SomeVirtual");
  259. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  260. // Act
  261. var filters = helper.PublicGetFiltersForActionMethod(methodInfo);
  262. // Assert
  263. Assert.AreEqual(3, filters.AuthorizationFilters.Count, "Wrong number of authorization filters.");
  264. Assert.AreSame(controller, filters.AuthorizationFilters[0]);
  265. Assert.AreEqual("BaseClass", ((KeyedFilterAttribute)filters.AuthorizationFilters[1]).Key);
  266. Assert.AreEqual("BaseMethod", ((KeyedFilterAttribute)filters.AuthorizationFilters[2]).Key);
  267. Assert.AreEqual(6, filters.ActionFilters.Count, "Wrong number of action filters.");
  268. Assert.AreSame(controller, filters.ActionFilters[0]);
  269. Assert.AreEqual("BaseClass", ((KeyedFilterAttribute)filters.ActionFilters[1]).Key);
  270. Assert.AreEqual("BaseMethod", ((KeyedFilterAttribute)filters.ActionFilters[2]).Key);
  271. Assert.AreEqual("DerivedClass", ((KeyedFilterAttribute)filters.ActionFilters[3]).Key);
  272. Assert.AreEqual("SubderivedClass", ((KeyedFilterAttribute)filters.ActionFilters[4]).Key);
  273. Assert.AreEqual("SubderivedMethod", ((KeyedFilterAttribute)filters.ActionFilters[5]).Key);
  274. Assert.AreEqual(6, filters.ResultFilters.Count, "Wrong number of result filters.");
  275. Assert.AreSame(controller, filters.ResultFilters[0]);
  276. Assert.AreEqual("BaseClass", ((KeyedFilterAttribute)filters.ResultFilters[1]).Key);
  277. Assert.AreEqual("BaseMethod", ((KeyedFilterAttribute)filters.ResultFilters[2]).Key);
  278. Assert.AreEqual("DerivedClass", ((KeyedFilterAttribute)filters.ResultFilters[3]).Key);
  279. Assert.AreEqual("SubderivedClass", ((KeyedFilterAttribute)filters.ResultFilters[4]).Key);
  280. Assert.AreEqual("SubderivedMethod", ((KeyedFilterAttribute)filters.ResultFilters[5]).Key);
  281. Assert.AreEqual(1, filters.ExceptionFilters.Count, "Wrong number of exception filters.");
  282. Assert.AreSame(controller, filters.ExceptionFilters[0]);
  283. }
  284. [TestMethod]
  285. public void GetFiltersForActionMethodGetsDerivedClassFiltersForBaseClassMethod() {
  286. // DevDiv 208062: Action filters specified on derived class won't run if the action method is on a base class
  287. // Arrange
  288. ControllerBase controller = new GetMemberChainDerivedController();
  289. ControllerContext context = GetControllerContext(controller);
  290. MethodInfo methodInfo = typeof(GetMemberChainDerivedController).GetMethod("SomeVirtual");
  291. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  292. // Act
  293. var filters = helper.PublicGetFiltersForActionMethod(methodInfo);
  294. // Assert
  295. Assert.AreEqual(3, filters.AuthorizationFilters.Count, "Wrong number of authorization filters.");
  296. Assert.AreSame(controller, filters.AuthorizationFilters[0]);
  297. Assert.AreEqual("BaseClass", ((KeyedFilterAttribute)filters.AuthorizationFilters[1]).Key);
  298. Assert.AreEqual("BaseMethod", ((KeyedFilterAttribute)filters.AuthorizationFilters[2]).Key);
  299. Assert.AreEqual(4, filters.ActionFilters.Count, "Wrong number of action filters.");
  300. Assert.AreSame(controller, filters.ActionFilters[0]);
  301. Assert.AreEqual("BaseClass", ((KeyedFilterAttribute)filters.ActionFilters[1]).Key);
  302. Assert.AreEqual("BaseMethod", ((KeyedFilterAttribute)filters.ActionFilters[2]).Key);
  303. Assert.AreEqual("DerivedClass", ((KeyedFilterAttribute)filters.ActionFilters[3]).Key);
  304. Assert.AreEqual(4, filters.ResultFilters.Count, "Wrong number of result filters.");
  305. Assert.AreSame(controller, filters.ResultFilters[0]);
  306. Assert.AreEqual("BaseClass", ((KeyedFilterAttribute)filters.ResultFilters[1]).Key);
  307. Assert.AreEqual("BaseMethod", ((KeyedFilterAttribute)filters.ResultFilters[2]).Key);
  308. Assert.AreEqual("DerivedClass", ((KeyedFilterAttribute)filters.ResultFilters[3]).Key);
  309. Assert.AreEqual(1, filters.ExceptionFilters.Count, "Wrong number of exception filters.");
  310. Assert.AreSame(controller, filters.ExceptionFilters[0]);
  311. }
  312. [TestMethod]
  313. public void GetFiltersForActionMethodWithNullMethodInfoThrows() {
  314. // Arrange
  315. ControllerBase controller = new GetMemberChainSubderivedController();
  316. ControllerContext context = GetControllerContext(controller);
  317. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  318. // Act & Assert
  319. ExceptionHelper.ExpectArgumentNullException(
  320. delegate {
  321. helper.PublicGetFiltersForActionMethod(null /* methodInfo */);
  322. }, "methodInfo");
  323. }
  324. [TestMethod]
  325. public void GetParameterValueAllowsAllSubpropertiesIfBindAttributeNotSpecified() {
  326. // Arrange
  327. CustomConverterController controller = new CustomConverterController() { ValueProvider = new Mock<IValueProvider>().Object };
  328. ControllerContext controllerContext = GetControllerContext(controller);
  329. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  330. ParameterInfo paramWithoutBindAttribute = typeof(CustomConverterController).GetMethod("ParameterWithoutBindAttribute").GetParameters()[0];
  331. // Act
  332. object valueWithoutBindAttribute = helper.PublicGetParameterValue(paramWithoutBindAttribute);
  333. // Assert
  334. Assert.AreEqual("foo=True&bar=True", valueWithoutBindAttribute);
  335. }
  336. [TestMethod]
  337. public void GetParameterValueResolvesConvertersInCorrectOrderOfPrecedence() {
  338. // Order of precedence:
  339. // 1. Attributes on the parameter itself
  340. // 2. Query the global converter provider
  341. // Arrange
  342. CustomConverterController controller = new CustomConverterController();
  343. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" } };
  344. ControllerContext controllerContext = GetControllerContext(controller, values);
  345. controller.ControllerContext = controllerContext;
  346. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  347. ParameterInfo paramWithOneConverter = typeof(CustomConverterController).GetMethod("ParameterHasOneConverter").GetParameters()[0];
  348. ParameterInfo paramWithNoConverters = typeof(CustomConverterController).GetMethod("ParameterHasNoConverters").GetParameters()[0];
  349. // Act
  350. object valueWithOneConverter = helper.PublicGetParameterValue(paramWithOneConverter);
  351. object valueWithNoConverters = helper.PublicGetParameterValue(paramWithNoConverters);
  352. // Assert
  353. Assert.AreEqual("foo_String", valueWithOneConverter);
  354. Assert.AreEqual("fooValue", valueWithNoConverters);
  355. }
  356. [TestMethod]
  357. public void GetParameterValueRespectsBindAttribute() {
  358. // Arrange
  359. CustomConverterController controller = new CustomConverterController() { ValueProvider = new Mock<IValueProvider>().Object };
  360. ControllerContext controllerContext = GetControllerContext(controller);
  361. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  362. ParameterInfo paramWithBindAttribute = typeof(CustomConverterController).GetMethod("ParameterHasBindAttribute").GetParameters()[0];
  363. // Act
  364. object valueWithBindAttribute = helper.PublicGetParameterValue(paramWithBindAttribute);
  365. // Assert
  366. Assert.AreEqual("foo=True&bar=False", valueWithBindAttribute);
  367. }
  368. [TestMethod]
  369. public void GetParameterValueRespectsBindAttributePrefix() {
  370. // Arrange
  371. CustomConverterController controller = new CustomConverterController();
  372. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" }, { "bar", "barValue" } };
  373. ControllerContext controllerContext = GetControllerContext(controller, values);
  374. controller.ControllerContext = controllerContext;
  375. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  376. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasFieldPrefix").GetParameters()[0];
  377. // Act
  378. object parameterValue = helper.PublicGetParameterValue(paramWithFieldPrefix);
  379. // Assert
  380. Assert.AreEqual("barValue", parameterValue);
  381. }
  382. [TestMethod]
  383. public void GetParameterValueRespectsBindAttributeNullPrefix() {
  384. // Arrange
  385. CustomConverterController controller = new CustomConverterController();
  386. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" }, { "bar", "barValue" } };
  387. ControllerContext controllerContext = GetControllerContext(controller, values);
  388. controller.ControllerContext = controllerContext;
  389. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  390. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasNullFieldPrefix").GetParameters()[0];
  391. // Act
  392. object parameterValue = helper.PublicGetParameterValue(paramWithFieldPrefix);
  393. // Assert
  394. Assert.AreEqual("fooValue", parameterValue);
  395. }
  396. [TestMethod]
  397. public void GetParameterValueRespectsBindAttributeEmptyPrefix() {
  398. // Arrange
  399. CustomConverterController controller = new CustomConverterController();
  400. Dictionary<string, object> values = new Dictionary<string, object> { { "foo", "fooValue" }, { "bar", "barValue" }, { "intprop", "123" }, { "stringprop", "hello" } };
  401. ControllerContext controllerContext = GetControllerContext(controller, values);
  402. controller.ControllerContext = controllerContext;
  403. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  404. ParameterInfo paramWithFieldPrefix = typeof(CustomConverterController).GetMethod("ParameterHasEmptyFieldPrefix").GetParameters()[0];
  405. // Act
  406. MySimpleModel parameterValue = helper.PublicGetParameterValue(paramWithFieldPrefix) as MySimpleModel;
  407. // Assert
  408. Assert.IsNotNull(parameterValue);
  409. Assert.AreEqual<int>(123, parameterValue.IntProp);
  410. Assert.AreEqual<string>("hello", parameterValue.StringProp);
  411. }
  412. [TestMethod]
  413. public void GetParameterValueReturnsNullIfCannotConvertNonRequiredParameter() {
  414. // Arrange
  415. var controller = new ParameterTestingController();
  416. ControllerContext context = GetControllerContext(controller);
  417. controller.ControllerContext = context;
  418. Dictionary<string, object> dict = new Dictionary<string, object>() {
  419. { "id", DateTime.Now } // cannot convert DateTime to Nullable<int>
  420. };
  421. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  422. MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesNullableInt");
  423. ParameterInfo[] pis = mi.GetParameters();
  424. // Act
  425. object oValue = helper.PublicGetParameterValue(pis[0]);
  426. // Assert
  427. Assert.IsNull(oValue);
  428. }
  429. [TestMethod]
  430. public void GetParameterValueReturnsNullIfNullableTypeValueNotFound() {
  431. // Arrange
  432. var controller = new ParameterTestingController();
  433. ControllerContext context = GetControllerContext(controller);
  434. controller.ControllerContext = context;
  435. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  436. MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesNullableInt");
  437. ParameterInfo[] pis = mi.GetParameters();
  438. // Act
  439. object oValue = helper.PublicGetParameterValue(pis[0]);
  440. // Assert
  441. Assert.IsNull(oValue);
  442. }
  443. [TestMethod]
  444. public void GetParameterValueReturnsNullIfReferenceTypeValueNotFound() {
  445. // Arrange
  446. var controller = new ParameterTestingController();
  447. ControllerContext context = GetControllerContext(controller);
  448. controller.ControllerContext = context;
  449. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  450. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo");
  451. ParameterInfo[] pis = mi.GetParameters();
  452. // Act
  453. object oValue = helper.PublicGetParameterValue(pis[0]);
  454. // Assert
  455. Assert.IsNull(oValue);
  456. }
  457. [TestMethod]
  458. public void GetParameterValuesCallsGetParameterValue() {
  459. // Arrange
  460. ControllerBase controller = new ParameterTestingController();
  461. IDictionary<string, object> dict = new Dictionary<string, object>();
  462. ControllerContext context = GetControllerContext(controller);
  463. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo");
  464. ParameterInfo[] pis = mi.GetParameters();
  465. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>(context);
  466. mockHelper.CallBase = true;
  467. mockHelper.Expect(h => h.PublicGetParameterValue(pis[0])).Returns("Myfoo").Verifiable();
  468. mockHelper.Expect(h => h.PublicGetParameterValue(pis[1])).Returns("Mybar").Verifiable();
  469. mockHelper.Expect(h => h.PublicGetParameterValue(pis[2])).Returns("Mybaz").Verifiable();
  470. ControllerActionInvokerHelper helper = mockHelper.Object;
  471. // Act
  472. IDictionary<string, object> parameters = helper.PublicGetParameterValues(mi);
  473. // Assert
  474. Assert.AreEqual(3, parameters.Count);
  475. Assert.AreEqual("Myfoo", parameters["foo"]);
  476. Assert.AreEqual("Mybar", parameters["bar"]);
  477. Assert.AreEqual("Mybaz", parameters["baz"]);
  478. mockHelper.Verify();
  479. }
  480. [TestMethod]
  481. public void GetParameterValuesReturnsEmptyArrayForParameterlessMethod() {
  482. // Arrange
  483. var controller = new ParameterTestingController();
  484. ControllerContext context = GetControllerContext(controller);
  485. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  486. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Parameterless");
  487. // Act
  488. IDictionary<string, object> parameters = helper.PublicGetParameterValues(mi);
  489. // Assert
  490. Assert.AreEqual(0, parameters.Count);
  491. }
  492. [TestMethod]
  493. public void GetParameterValuesReturnsValuesForParametersInOrder() {
  494. // We need to hook into GetParameterValue() to make sure that GetParameterValues() is calling it.
  495. // Arrange
  496. var controller = new ParameterTestingController();
  497. Dictionary<string, object> dict = new Dictionary<string, object>() {
  498. { "foo", "MyFoo" },
  499. { "bar", "MyBar" },
  500. { "baz", "MyBaz" }
  501. };
  502. ControllerContext context = GetControllerContext(controller, dict);
  503. controller.ControllerContext = context;
  504. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  505. MethodInfo mi = typeof(ParameterTestingController).GetMethod("Foo");
  506. // Act
  507. IDictionary<string, object> parameters = helper.PublicGetParameterValues(mi);
  508. // Assert
  509. Assert.AreEqual(3, parameters.Count);
  510. Assert.AreEqual("MyFoo", parameters["foo"]);
  511. Assert.AreEqual("MyBar", parameters["bar"]);
  512. Assert.AreEqual("MyBaz", parameters["baz"]);
  513. }
  514. [TestMethod]
  515. public void GetParameterValuesWithNullMethodInfoThrows() {
  516. // Arrange
  517. var controller = new ParameterTestingController();
  518. ControllerContext context = GetControllerContext(controller);
  519. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  520. // Act & Assert
  521. ExceptionHelper.ExpectArgumentNullException(
  522. delegate {
  523. helper.PublicGetParameterValues(null /* methodInfo */);
  524. },
  525. "methodInfo");
  526. }
  527. [TestMethod]
  528. public void GetParameterValuesWithOutParamThrows() {
  529. // Arrange
  530. var controller = new ParameterTestingController();
  531. ControllerContext context = GetControllerContext(controller);
  532. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  533. MethodInfo mi = typeof(ParameterTestingController).GetMethod("HasOutParam");
  534. // Act & Assert
  535. ExceptionHelper.ExpectException<InvalidOperationException>(
  536. delegate {
  537. helper.PublicGetParameterValues(mi);
  538. },
  539. "Cannot set value for parameter 'foo' in action 'HasOutParam'. Parameters passed by reference are not supported in action methods.");
  540. }
  541. [TestMethod]
  542. public void GetParameterValuesWithRefParamThrows() {
  543. // Arrange
  544. var controller = new ParameterTestingController();
  545. ControllerContext context = GetControllerContext(controller);
  546. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  547. MethodInfo mi = typeof(ParameterTestingController).GetMethod("HasRefParam");
  548. // Act & Assert
  549. ExceptionHelper.ExpectException<InvalidOperationException>(
  550. delegate {
  551. helper.PublicGetParameterValues(mi);
  552. },
  553. "Cannot set value for parameter 'foo' in action 'HasRefParam'. Parameters passed by reference are not supported in action methods.");
  554. }
  555. // TODO: This test is temporarily disabled now that converters can update ModelState. We should consider reenabling
  556. // this if invalid inputs to action methods become problematic.
  557. public void GetParameterValueThrowsIfCannotConvertRequiredParameter() {
  558. // Arrange
  559. var controller = new ParameterTestingController();
  560. ControllerContext context = GetControllerContext(controller);
  561. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  562. MethodInfo mi = typeof(ParameterTestingController).GetMethod("TakesInt");
  563. ParameterInfo[] pis = mi.GetParameters();
  564. // Act & Assert
  565. ExceptionHelper.ExpectInvalidOperationException(
  566. delegate {
  567. helper.PublicGetParameterValue(pis[0]);
  568. },
  569. "A value is required for parameter 'id' in action 'TakesInt'. The parameter either has no value or its value could"
  570. + " not be converted. To make a parameter optional its type should either be a reference type or a Nullable type.");
  571. }
  572. [TestMethod]
  573. public void GetParameterValueThrowsIfParameterHasMultipleConverters() {
  574. // Arrange
  575. CustomConverterController controller = new CustomConverterController();
  576. ControllerContext controllerContext = GetControllerContext(controller);
  577. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  578. ParameterInfo paramWithTwoConverters = typeof(CustomConverterController).GetMethod("ParameterHasTwoConverters").GetParameters()[0];
  579. // Act & Assert
  580. ExceptionHelper.ExpectInvalidOperationException(
  581. delegate {
  582. helper.PublicGetParameterValue(paramWithTwoConverters);
  583. },
  584. "The parameter 'foo' on method 'ParameterHasTwoConverters' contains multiple attributes inheriting from CustomModelBinderAttribute.");
  585. }
  586. [TestMethod]
  587. public void GetParameterValueUsesControllerValueProviderAsValueProvider() {
  588. // Arrange
  589. Mock<IValueProvider> mockValueProvider = new Mock<IValueProvider>();
  590. mockValueProvider.Expect(p => p.GetValue("foo")).Returns(new ValueProviderResult("fooValue", "fooValue", null));
  591. CustomConverterController controller = new CustomConverterController() { ValueProvider = mockValueProvider.Object };
  592. ControllerContext controllerContext = GetControllerContext(controller);
  593. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(controllerContext);
  594. ParameterInfo parameter = typeof(CustomConverterController).GetMethod("ParameterHasNoConverters").GetParameters()[0];
  595. // Act
  596. object parameterValue = helper.PublicGetParameterValue(parameter);
  597. // Assert
  598. Assert.AreEqual("fooValue", parameterValue);
  599. }
  600. [TestMethod]
  601. public void GetParameterValueWithNullParameterInfoThrows() {
  602. // Arrange
  603. var controller = new ParameterTestingController();
  604. ControllerContext context = GetControllerContext(controller);
  605. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  606. // Act & Assert
  607. ExceptionHelper.ExpectArgumentNullException(
  608. delegate {
  609. helper.PublicGetParameterValue(null /* parameterInfo */);
  610. },
  611. "parameterInfo");
  612. }
  613. [TestMethod]
  614. public void InvokeAction() {
  615. // Arrange
  616. ControllerBase controller = new Mock<ControllerBase>().Object;
  617. ControllerContext context = GetControllerContext(controller);
  618. IDictionary<string, object> paramValues = new Dictionary<string, object>();
  619. MethodInfo methodInfo = typeof(object).GetMethod("ToString");
  620. var filterInfo = new FilterInfo();
  621. ActionResult actionResult = new EmptyResult();
  622. ActionExecutedContext postContext = new ActionExecutedContext(context, false /* canceled */, null /* exception */) {
  623. Result = actionResult
  624. };
  625. AuthorizationContext authContext = new AuthorizationContext(context);
  626. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>();
  627. mockHelper.CallBase = true;
  628. mockHelper.Expect(h => h.PublicFindActionMethod("SomeMethod")).Returns(methodInfo).Verifiable();
  629. mockHelper.Expect(h => h.PublicGetParameterValues(methodInfo)).Returns(paramValues).Verifiable();
  630. mockHelper.Expect(h => h.PublicGetFiltersForActionMethod(methodInfo)).Returns(filterInfo).Verifiable();
  631. mockHelper.Expect(h => h.PublicInvokeAuthorizationFilters(methodInfo, filterInfo.AuthorizationFilters)).Returns(authContext).Verifiable();
  632. mockHelper.Expect(h => h.PublicInvokeActionMethodWithFilters(methodInfo, paramValues, filterInfo.ActionFilters)).Returns(postContext).Verifiable();
  633. mockHelper.Expect(h => h.PublicInvokeActionResultWithFilters(actionResult, filterInfo.ResultFilters)).Returns((ResultExecutedContext)null).Verifiable();
  634. ControllerActionInvokerHelper helper = mockHelper.Object;
  635. // Act
  636. bool retVal = helper.InvokeAction(context, "SomeMethod");
  637. Assert.IsTrue(retVal, "InvokeAction() should return True on success.");
  638. Assert.AreSame(context, helper.ControllerContext, "ControllerContext property should have been set by InvokeAction");
  639. mockHelper.Verify();
  640. }
  641. [TestMethod]
  642. public void InvokeActionFiltersOrdersFiltersCorrectly() {
  643. // Arrange
  644. List<string> actions = new List<string>();
  645. Dictionary<string, object> parameters = new Dictionary<string, object>();
  646. ActionResult actionResult = new EmptyResult();
  647. ActionFilterImpl filter1 = new ActionFilterImpl() {
  648. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  649. actions.Add("OnActionExecuting1");
  650. },
  651. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) {
  652. actions.Add("OnActionExecuted1");
  653. }
  654. };
  655. ActionFilterImpl filter2 = new ActionFilterImpl() {
  656. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  657. actions.Add("OnActionExecuting2");
  658. },
  659. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) {
  660. actions.Add("OnActionExecuted2");
  661. }
  662. };
  663. Func<ActionResult> continuation = delegate {
  664. actions.Add("Continuation");
  665. return new EmptyResult();
  666. };
  667. ControllerBase controller = new ContinuationController(continuation);
  668. ControllerContext context = GetControllerContext(controller);
  669. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  670. // Act
  671. helper.PublicInvokeActionMethodWithFilters(ContinuationController.GoMethod, parameters,
  672. new List<IActionFilter>() { filter1, filter2 });
  673. // Assert
  674. Assert.AreEqual(5, actions.Count);
  675. Assert.AreEqual("OnActionExecuting1", actions[0]);
  676. Assert.AreEqual("OnActionExecuting2", actions[1]);
  677. Assert.AreEqual("Continuation", actions[2]);
  678. Assert.AreEqual("OnActionExecuted2", actions[3]);
  679. Assert.AreEqual("OnActionExecuted1", actions[4]);
  680. }
  681. [TestMethod]
  682. public void InvokeActionFiltersPassesArgumentsCorrectly() {
  683. // Arrange
  684. bool wasCalled = false;
  685. MethodInfo mi = ContinuationController.GoMethod;
  686. Dictionary<string, object> parameters = new Dictionary<string, object>();
  687. ActionResult actionResult = new EmptyResult();
  688. ActionFilterImpl filter = new ActionFilterImpl() {
  689. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  690. Assert.AreSame(parameters, filterContext.ActionParameters);
  691. Assert.IsFalse(wasCalled);
  692. wasCalled = true;
  693. filterContext.Result = actionResult;
  694. }
  695. };
  696. Func<ActionResult> continuation = delegate {
  697. Assert.Fail("Continuation should not be called.");
  698. return null;
  699. };
  700. ControllerBase controller = new ContinuationController(continuation);
  701. ControllerContext context = GetControllerContext(controller);
  702. ControllerActionInvokerHelper helper = new ControllerActionInvokerHelper(context);
  703. // Act
  704. ActionExecutedContext result = helper.PublicInvokeActionMethodWithFilters(mi, parameters,
  705. new List<IActionFilter>() { filter });
  706. // Assert
  707. Assert.IsTrue(wasCalled);
  708. Assert.IsNull(result.Exception);
  709. Assert.IsFalse(result.ExceptionHandled);
  710. Assert.AreSame(actionResult, result.Result);
  711. }
  712. [TestMethod]
  713. public void InvokeActionFilterWhereContinuationThrowsExceptionAndIsHandled() {
  714. // Arrange
  715. List<string> actions = new List<string>();
  716. MethodInfo mi = typeof(object).GetMethod("ToString");
  717. Dictionary<string, object> parameters = new Dictionary<string, object>();
  718. Exception exception = new Exception();
  719. ActionFilterImpl filter = new ActionFilterImpl() {
  720. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  721. actions.Add("OnActionExecuting");
  722. },
  723. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) {
  724. actions.Add("OnActionExecuted");
  725. Assert.AreSame(exception, filterContext.Exception);
  726. Assert.IsFalse(filterContext.ExceptionHandled);
  727. filterContext.ExceptionHandled = true;
  728. }
  729. };
  730. Func<ActionExecutedContext> continuation = delegate {
  731. actions.Add("Continuation");
  732. throw exception;
  733. };
  734. // Act
  735. ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, new ActionExecutingContext(ControllerContextTest.GetControllerContext(), parameters), continuation);
  736. // Assert
  737. Assert.AreEqual(3, actions.Count);
  738. Assert.AreEqual("OnActionExecuting", actions[0]);
  739. Assert.AreEqual("Continuation", actions[1]);
  740. Assert.AreEqual("OnActionExecuted", actions[2]);
  741. Assert.AreSame(exception, result.Exception);
  742. Assert.IsTrue(result.ExceptionHandled);
  743. }
  744. [TestMethod]
  745. public void InvokeActionFilterWhereContinuationThrowsExceptionAndIsNotHandled() {
  746. // Arrange
  747. List<string> actions = new List<string>();
  748. Dictionary<string, object> parameters = new Dictionary<string, object>();
  749. ActionFilterImpl filter = new ActionFilterImpl() {
  750. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  751. actions.Add("OnActionExecuting");
  752. },
  753. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) {
  754. actions.Add("OnActionExecuted");
  755. }
  756. };
  757. Func<ActionExecutedContext> continuation = delegate {
  758. actions.Add("Continuation");
  759. throw new Exception("Some exception message.");
  760. };
  761. // Act & Assert
  762. ExceptionHelper.ExpectException<Exception>(
  763. delegate {
  764. ControllerActionInvoker.InvokeActionMethodFilter(filter, new ActionExecutingContext(ControllerContextTest.GetControllerContext(), parameters), continuation);
  765. },
  766. "Some exception message.");
  767. Assert.AreEqual(3, actions.Count);
  768. Assert.AreEqual("OnActionExecuting", actions[0]);
  769. Assert.AreEqual("Continuation", actions[1]);
  770. Assert.AreEqual("OnActionExecuted", actions[2]);
  771. }
  772. [TestMethod]
  773. public void InvokeActionFilterWhereOnActionExecutingCancels() {
  774. // Arrange
  775. bool wasCalled = false;
  776. Dictionary<string, object> parameters = new Dictionary<string, object>();
  777. ActionExecutedContext postContext = new ActionExecutedContext(ControllerContextTest.GetControllerContext(), false /* canceled */, null /* exception */);
  778. ActionResult actionResult = new EmptyResult();
  779. ActionFilterImpl filter = new ActionFilterImpl() {
  780. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  781. Assert.IsFalse(wasCalled);
  782. wasCalled = true;
  783. filterContext.Result = actionResult;
  784. },
  785. };
  786. Func<ActionExecutedContext> continuation = delegate {
  787. Assert.Fail("The continuation should not be called.");
  788. return null;
  789. };
  790. // Act
  791. ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, new ActionExecutingContext(ControllerContextTest.GetControllerContext(), parameters), continuation);
  792. // Assert
  793. Assert.IsTrue(wasCalled);
  794. Assert.IsNull(result.Exception);
  795. Assert.IsTrue(result.Canceled);
  796. Assert.AreSame(actionResult, result.Result);
  797. }
  798. [TestMethod]
  799. public void InvokeActionFilterWithNormalControlFlow() {
  800. // Arrange
  801. List<string> actions = new List<string>();
  802. Dictionary<string, object> parameters = new Dictionary<string, object>();
  803. ActionExecutedContext postContext = new ActionExecutedContext(ControllerContextTest.GetControllerContext(), false /* canceled */, null /* exception */);
  804. ActionFilterImpl filter = new ActionFilterImpl() {
  805. OnActionExecutingImpl = delegate(ActionExecutingContext filterContext) {
  806. Assert.AreSame(parameters, filterContext.ActionParameters);
  807. Assert.IsNull(filterContext.Result);
  808. actions.Add("OnActionExecuting");
  809. },
  810. OnActionExecutedImpl = delegate(ActionExecutedContext filterContext) {
  811. Assert.AreEqual(postContext, filterContext);
  812. actions.Add("OnActionExecuted");
  813. }
  814. };
  815. Func<ActionExecutedContext> continuation = delegate {
  816. actions.Add("Continuation");
  817. return postContext;
  818. };
  819. // Act
  820. ActionExecutedContext result = ControllerActionInvoker.InvokeActionMethodFilter(filter, new ActionExecutingContext(ControllerContextTest.GetControllerContext(), parameters), continuation);
  821. // Assert
  822. Assert.AreEqual(3, actions.Count);
  823. Assert.AreEqual("OnActionExecuting", actions[0]);
  824. Assert.AreEqual("Continuation", actions[1]);
  825. Assert.AreEqual("OnActionExecuted", actions[2]);
  826. Assert.AreSame(result, postContext);
  827. }
  828. [TestMethod]
  829. public void InvokeActionInvokesEmptyResultIfAuthorizationFailsAndNoResultSpecified() {
  830. // Arrange
  831. ControllerBase controller = new Mock<ControllerBase>().Object;
  832. ControllerContext context = GetControllerContext(controller);
  833. IDictionary<string, object> values = new Dictionary<string, object>();
  834. IDictionary<string, object> paramValues = new Dictionary<string, object>();
  835. MethodInfo methodInfo = typeof(object).GetMethod("ToString");
  836. var filterInfo = new FilterInfo();
  837. ActionResult actionResult = new EmptyResult();
  838. ActionExecutedContext postContext = new ActionExecutedContext(context, false /* canceled */, null /* exception */) {
  839. Result = actionResult
  840. };
  841. AuthorizationContext authContext = new AuthorizationContext(context) { Cancel = true };
  842. Mock<ControllerActionInvokerHelper> mockHelper = new Mock<ControllerActionInvokerHelper>();
  843. mockHelper.CallBase = true;
  844. mockHelper.Expect(h => h.PublicFindActionMethod("SomeMethod")).Returns(methodInfo).Verifiable();
  845. mockHelper.Expect(h => h.PublicGetParameterValues(methodInfo)).Returns(paramValues).Verifiable()

Large files files are truncated, but you can click here to view the full file