PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/ZendTest/Mvc/Controller/RestfulControllerTest.php

https://github.com/praveenuniyal/zf2
PHP | 488 lines | 420 code | 51 blank | 17 comment | 1 complexity | 67f34dee05e353447382824c936adeb5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_Mvc
  9. */
  10. namespace ZendTest\Mvc\Controller;
  11. use PHPUnit_Framework_TestCase as TestCase;
  12. use ReflectionObject;
  13. use stdClass;
  14. use Zend\EventManager\SharedEventManager;
  15. use Zend\Http\Response;
  16. use Zend\Mvc\MvcEvent;
  17. use Zend\Mvc\Router\RouteMatch;
  18. class RestfulControllerTest extends TestCase
  19. {
  20. public $controller;
  21. public $emptyController;
  22. public $request;
  23. public $response;
  24. public $routeMatch;
  25. public $event;
  26. public function setUp()
  27. {
  28. $this->controller = new TestAsset\RestfulTestController();
  29. $this->emptyController = new TestAsset\RestfulMethodNotAllowedTestController();
  30. $this->request = new TestAsset\Request();
  31. $this->response = new Response();
  32. $this->routeMatch = new RouteMatch(array('controller' => 'controller-restful'));
  33. $this->event = new MvcEvent;
  34. $this->event->setRouteMatch($this->routeMatch);
  35. $this->controller->setEvent($this->event);
  36. $this->emptyController->setEvent($this->event);
  37. }
  38. public function testDispatchInvokesListWhenNoActionPresentAndNoIdentifierOnGet()
  39. {
  40. $entities = array(
  41. new stdClass,
  42. new stdClass,
  43. new stdClass,
  44. );
  45. $this->controller->entities = $entities;
  46. $result = $this->controller->dispatch($this->request, $this->response);
  47. $this->assertArrayHasKey('entities', $result);
  48. $this->assertEquals($entities, $result['entities']);
  49. $this->assertEquals('getList', $this->routeMatch->getParam('action'));
  50. }
  51. public function testDispatchInvokesGetMethodWhenNoActionPresentAndIdentifierPresentOnGet()
  52. {
  53. $entity = new stdClass;
  54. $this->controller->entity = $entity;
  55. $this->routeMatch->setParam('id', 1);
  56. $result = $this->controller->dispatch($this->request, $this->response);
  57. $this->assertArrayHasKey('entity', $result);
  58. $this->assertEquals($entity, $result['entity']);
  59. $this->assertEquals('get', $this->routeMatch->getParam('action'));
  60. }
  61. public function testDispatchInvokesCreateMethodWhenNoActionPresentAndPostInvoked()
  62. {
  63. $entity = array('id' => 1, 'name' => __FUNCTION__);
  64. $this->request->setMethod('POST');
  65. $post = $this->request->getPost();
  66. $post->fromArray($entity);
  67. $result = $this->controller->dispatch($this->request, $this->response);
  68. $this->assertArrayHasKey('entity', $result);
  69. $this->assertEquals($entity, $result['entity']);
  70. $this->assertEquals('create', $this->routeMatch->getParam('action'));
  71. }
  72. public function testDispatchInvokesUpdateMethodWhenNoActionPresentAndPutInvokedWithIdentifier()
  73. {
  74. $entity = array('name' => __FUNCTION__);
  75. $string = http_build_query($entity);
  76. $this->request->setMethod('PUT')
  77. ->setContent($string);
  78. $this->routeMatch->setParam('id', 1);
  79. $result = $this->controller->dispatch($this->request, $this->response);
  80. $this->assertArrayHasKey('entity', $result);
  81. $test = $result['entity'];
  82. $this->assertArrayHasKey('id', $test);
  83. $this->assertEquals(1, $test['id']);
  84. $this->assertArrayHasKey('name', $test);
  85. $this->assertEquals(__FUNCTION__, $test['name']);
  86. $this->assertEquals('update', $this->routeMatch->getParam('action'));
  87. }
  88. public function testDispatchInvokesReplaceListMethodWhenNoActionPresentAndPutInvokedWithoutIdentifier()
  89. {
  90. $entities = array(
  91. array('id' => uniqid(), 'name' => __FUNCTION__),
  92. array('id' => uniqid(), 'name' => __FUNCTION__),
  93. array('id' => uniqid(), 'name' => __FUNCTION__),
  94. );
  95. $string = http_build_query($entities);
  96. $this->request->setMethod('PUT')
  97. ->setContent($string);
  98. $result = $this->controller->dispatch($this->request, $this->response);
  99. $this->assertEquals($entities, $result);
  100. $this->assertEquals('replaceList', $this->routeMatch->getParam('action'));
  101. }
  102. public function testDispatchInvokesPatchListMethodWhenNoActionPresentAndPatchInvokedWithoutIdentifier()
  103. {
  104. $entities = array(
  105. array('id' => uniqid(), 'name' => __FUNCTION__),
  106. array('id' => uniqid(), 'name' => __FUNCTION__),
  107. array('id' => uniqid(), 'name' => __FUNCTION__),
  108. );
  109. $string = http_build_query($entities);
  110. $this->request->setMethod('PATCH')
  111. ->setContent($string);
  112. $result = $this->controller->dispatch($this->request, $this->response);
  113. $this->assertEquals($entities, $result);
  114. $this->assertEquals('patchList', $this->routeMatch->getParam('action'));
  115. }
  116. public function testDispatchInvokesDeleteMethodWhenNoActionPresentAndDeleteInvokedWithIdentifier()
  117. {
  118. $entity = array('id' => 1, 'name' => __FUNCTION__);
  119. $this->controller->entity = $entity;
  120. $this->request->setMethod('DELETE');
  121. $this->routeMatch->setParam('id', 1);
  122. $result = $this->controller->dispatch($this->request, $this->response);
  123. $this->assertEquals(array(), $result);
  124. $this->assertEquals(array(), $this->controller->entity);
  125. $this->assertEquals('delete', $this->routeMatch->getParam('action'));
  126. }
  127. public function testDispatchInvokesDeleteListMethodWhenNoActionPresentAndDeleteInvokedWithoutIdentifier()
  128. {
  129. $this->request->setMethod('DELETE');
  130. $result = $this->controller->dispatch($this->request, $this->response);
  131. $this->assertSame($this->response, $result);
  132. $this->assertEquals(204, $result->getStatusCode());
  133. $this->assertTrue($result->getHeaders()->has('X-Deleted'));
  134. $this->assertEquals('deleteList', $this->routeMatch->getParam('action'));
  135. }
  136. public function testDispatchInvokesOptionsMethodWhenNoActionPresentAndOptionsInvoked()
  137. {
  138. $this->request->setMethod('OPTIONS');
  139. $result = $this->controller->dispatch($this->request, $this->response);
  140. $this->assertSame($this->response, $result);
  141. $this->assertEquals('options', $this->routeMatch->getParam('action'));
  142. $headers = $result->getHeaders();
  143. $this->assertTrue($headers->has('Allow'));
  144. $allow = $headers->get('Allow');
  145. $expected = explode(', ', 'GET, POST, PUT, DELETE, PATCH, HEAD, TRACE');
  146. sort($expected);
  147. $test = explode(', ', $allow->getFieldValue());
  148. sort($test);
  149. $this->assertEquals($expected, $test);
  150. }
  151. public function testDispatchInvokesPatchMethodWhenNoActionPresentAndPatchInvokedWithIdentifier()
  152. {
  153. $entity = new stdClass;
  154. $entity->name = 'foo';
  155. $entity->type = 'standard';
  156. $this->controller->entity = $entity;
  157. $entity = array('name' => __FUNCTION__);
  158. $string = http_build_query($entity);
  159. $this->request->setMethod('PATCH')
  160. ->setContent($string);
  161. $this->routeMatch->setParam('id', 1);
  162. $result = $this->controller->dispatch($this->request, $this->response);
  163. $this->assertArrayHasKey('entity', $result);
  164. $test = $result['entity'];
  165. $this->assertArrayHasKey('id', $test);
  166. $this->assertEquals(1, $test['id']);
  167. $this->assertArrayHasKey('name', $test);
  168. $this->assertEquals(__FUNCTION__, $test['name']);
  169. $this->assertArrayHasKey('type', $test);
  170. $this->assertEquals('standard', $test['type']);
  171. $this->assertEquals('patch', $this->routeMatch->getParam('action'));
  172. }
  173. public function testDispatchInvokesHeadMethodWhenNoActionPresentAndHeadInvokedWithoutIdentifier()
  174. {
  175. $entities = array(
  176. new stdClass,
  177. new stdClass,
  178. new stdClass,
  179. );
  180. $this->controller->entities = $entities;
  181. $this->request->setMethod('HEAD');
  182. $result = $this->controller->dispatch($this->request, $this->response);
  183. $this->assertSame($this->response, $result);
  184. $content = $result->getContent();
  185. $this->assertEquals('', $content);
  186. $this->assertEquals('head', $this->routeMatch->getParam('action'));
  187. }
  188. public function testDispatchInvokesHeadMethodWhenNoActionPresentAndHeadInvokedWithIdentifier()
  189. {
  190. $entity = new stdClass;
  191. $this->controller->entity = $entity;
  192. $this->routeMatch->setParam('id', 1);
  193. $this->request->setMethod('HEAD');
  194. $result = $this->controller->dispatch($this->request, $this->response);
  195. $this->assertSame($this->response, $result);
  196. $content = $result->getContent();
  197. $this->assertEquals('', $content);
  198. $this->assertEquals('head', $this->routeMatch->getParam('action'));
  199. $headers = $this->controller->getResponse()->getHeaders();
  200. $this->assertTrue($headers->has('X-ZF2-Id'));
  201. $header = $headers->get('X-ZF2-Id');
  202. $this->assertEquals(1, $header->getFieldValue());
  203. }
  204. public function testAllowsRegisteringCustomHttpMethodsWithHandlers()
  205. {
  206. $this->controller->addHttpMethodHandler('DESCRIBE', array($this->controller, 'describe'));
  207. $this->request->setMethod('DESCRIBE');
  208. $result = $this->controller->dispatch($this->request, $this->response);
  209. $this->assertArrayHasKey('description', $result);
  210. $this->assertContains('::describe', $result['description']);
  211. }
  212. public function testDispatchCallsActionMethodBasedOnNormalizingAction()
  213. {
  214. $this->routeMatch->setParam('action', 'test.some-strangely_separated.words');
  215. $result = $this->controller->dispatch($this->request, $this->response);
  216. $this->assertArrayHasKey('content', $result);
  217. $this->assertContains('Test Some Strangely Separated Words', $result['content']);
  218. }
  219. public function testDispatchCallsNotFoundActionWhenActionPassedThatCannotBeMatched()
  220. {
  221. $this->routeMatch->setParam('action', 'test-some-made-up-action');
  222. $result = $this->controller->dispatch($this->request, $this->response);
  223. $response = $this->controller->getResponse();
  224. $this->assertEquals(404, $response->getStatusCode());
  225. $this->assertArrayHasKey('content', $result);
  226. $this->assertContains('Page not found', $result['content']);
  227. }
  228. public function testShortCircuitsBeforeActionIfPreDispatchReturnsAResponse()
  229. {
  230. $response = new Response();
  231. $response->setContent('short circuited!');
  232. $this->controller->getEventManager()->attach(MvcEvent::EVENT_DISPATCH, function ($e) use ($response) {
  233. return $response;
  234. }, 10);
  235. $result = $this->controller->dispatch($this->request, $this->response);
  236. $this->assertSame($response, $result);
  237. }
  238. public function testPostDispatchEventAllowsReplacingResponse()
  239. {
  240. $response = new Response();
  241. $response->setContent('short circuited!');
  242. $this->controller->getEventManager()->attach(MvcEvent::EVENT_DISPATCH, function ($e) use ($response) {
  243. return $response;
  244. }, -10);
  245. $result = $this->controller->dispatch($this->request, $this->response);
  246. $this->assertSame($response, $result);
  247. }
  248. public function testEventManagerListensOnDispatchableInterfaceByDefault()
  249. {
  250. $response = new Response();
  251. $response->setContent('short circuited!');
  252. $events = new SharedEventManager();
  253. $events->attach('Zend\Stdlib\DispatchableInterface', MvcEvent::EVENT_DISPATCH, function ($e) use ($response) {
  254. return $response;
  255. }, 10);
  256. $this->controller->getEventManager()->setSharedManager($events);
  257. $result = $this->controller->dispatch($this->request, $this->response);
  258. $this->assertSame($response, $result);
  259. }
  260. public function testEventManagerListensOnRestfulControllerClassByDefault()
  261. {
  262. $response = new Response();
  263. $response->setContent('short circuited!');
  264. $events = new SharedEventManager();
  265. $events->attach('Zend\Mvc\Controller\AbstractRestfulController', MvcEvent::EVENT_DISPATCH, function ($e) use ($response) {
  266. return $response;
  267. }, 10);
  268. $this->controller->getEventManager()->setSharedManager($events);
  269. $result = $this->controller->dispatch($this->request, $this->response);
  270. $this->assertSame($response, $result);
  271. }
  272. public function testEventManagerListensOnClassNameByDefault()
  273. {
  274. $response = new Response();
  275. $response->setContent('short circuited!');
  276. $events = new SharedEventManager();
  277. $events->attach(get_class($this->controller), MvcEvent::EVENT_DISPATCH, function ($e) use ($response) {
  278. return $response;
  279. }, 10);
  280. $this->controller->getEventManager()->setSharedManager($events);
  281. $result = $this->controller->dispatch($this->request, $this->response);
  282. $this->assertSame($response, $result);
  283. }
  284. public function testDispatchInjectsEventIntoController()
  285. {
  286. $this->controller->dispatch($this->request, $this->response);
  287. $event = $this->controller->getEvent();
  288. $this->assertNotNull($event);
  289. $this->assertSame($this->event, $event);
  290. }
  291. public function testControllerIsLocatorAware()
  292. {
  293. $this->assertInstanceOf('Zend\ServiceManager\ServiceLocatorAwareInterface', $this->controller);
  294. }
  295. public function testControllerIsEventAware()
  296. {
  297. $this->assertInstanceOf('Zend\Mvc\InjectApplicationEventInterface', $this->controller);
  298. }
  299. public function testControllerIsPluggable()
  300. {
  301. $this->assertTrue(method_exists($this->controller, 'plugin'));
  302. }
  303. public function testMethodOverloadingShouldReturnPluginWhenFound()
  304. {
  305. $plugin = $this->controller->url();
  306. $this->assertInstanceOf('Zend\Mvc\Controller\Plugin\Url', $plugin);
  307. }
  308. public function testMethodOverloadingShouldInvokePluginAsFunctorIfPossible()
  309. {
  310. $model = $this->event->getViewModel();
  311. $this->controller->layout('alternate/layout');
  312. $this->assertEquals('alternate/layout', $model->getTemplate());
  313. }
  314. public function testParsingDataAsJsonWillReturnAsArray()
  315. {
  316. $this->request->setMethod('POST');
  317. $this->request->getHeaders()->addHeaderLine('Content-type', 'application/json');
  318. $this->request->setContent('{"foo":"bar"}');
  319. $this->controller->getEventManager()->setSharedManager(new SharedEventManager());
  320. $result = $this->controller->dispatch($this->request, $this->response);
  321. $this->assertInternalType('array', $result);
  322. $this->assertEquals(array('entity' => array('foo' => 'bar')), $result);
  323. }
  324. public function matchingContentTypes()
  325. {
  326. return array(
  327. 'exact-first' => array('application/hal+json'),
  328. 'exact-second' => array('application/json'),
  329. 'with-charset' => array('application/json; charset=utf-8'),
  330. 'with-whitespace' => array('application/json '),
  331. );
  332. }
  333. /**
  334. * @dataProvider matchingContentTypes
  335. */
  336. public function testRequestingContentTypeReturnsTrueForValidMatches($contentType)
  337. {
  338. $this->request->getHeaders()->addHeaderLine('Content-Type', $contentType);
  339. $this->assertTrue($this->controller->requestHasContentType($this->request, TestAsset\RestfulTestController::CONTENT_TYPE_JSON));
  340. }
  341. public function nonMatchingContentTypes()
  342. {
  343. return array(
  344. 'specific-type' => array('application/xml'),
  345. 'generic-type' => array('text/json'),
  346. );
  347. }
  348. /**
  349. * @dataProvider nonMatchingContentTypes
  350. */
  351. public function testRequestingContentTypeReturnsFalseForInvalidMatches($contentType)
  352. {
  353. $this->request->getHeaders()->addHeaderLine('Content-Type', $contentType);
  354. $this->assertFalse($this->controller->requestHasContentType($this->request, TestAsset\RestfulTestController::CONTENT_TYPE_JSON));
  355. }
  356. public function testDispatchWithUnrecognizedMethodReturns405Response()
  357. {
  358. $this->request->setMethod('PROPFIND');
  359. $result = $this->controller->dispatch($this->request, $this->response);
  360. $this->assertInstanceOf('Zend\Http\Response', $result);
  361. $this->assertEquals(405, $result->getStatusCode());
  362. }
  363. public function testDispatchInvokesGetMethodWhenNoActionPresentAndZeroIdentifierPresentOnGet()
  364. {
  365. $entity = new stdClass;
  366. $this->controller->entity = $entity;
  367. $this->routeMatch->setParam('id', 0);
  368. $result = $this->controller->dispatch($this->request, $this->response);
  369. $this->assertArrayHasKey('entity', $result);
  370. $this->assertEquals($entity, $result['entity']);
  371. $this->assertEquals('get', $this->routeMatch->getParam('action'));
  372. }
  373. public function testIdentifierNameDefaultsToId()
  374. {
  375. $this->assertEquals('id', $this->controller->getIdentifierName());
  376. }
  377. public function testCanSetIdentifierName()
  378. {
  379. $this->controller->setIdentifierName('name');
  380. $this->assertEquals('name', $this->controller->getIdentifierName());
  381. }
  382. public function testUsesConfiguredIdentifierNameToGetIdentifier()
  383. {
  384. $r = new ReflectionObject($this->controller);
  385. $getIdentifier = $r->getMethod('getIdentifier');
  386. $getIdentifier->setAccessible(true);
  387. $this->controller->setIdentifierName('name');
  388. $this->routeMatch->setParam('name', 'foo');
  389. $result = $getIdentifier->invoke($this->controller, $this->routeMatch, $this->request);
  390. $this->assertEquals('foo', $result);
  391. $this->routeMatch->setParam('name', false);
  392. $this->request->getQuery()->set('name', 'bar');
  393. $result = $getIdentifier->invoke($this->controller, $this->routeMatch, $this->request);
  394. $this->assertEquals('bar', $result);
  395. }
  396. /**
  397. * @dataProvider testNotImplementedMethodSets504HttpCodeProvider
  398. */
  399. public function testNotImplementedMethodSets504HttpCode($method, $content, array $routeParams)
  400. {
  401. $this->request->setMethod($method);
  402. if ($content) {
  403. $this->request->setContent($content);
  404. }
  405. foreach ($routeParams as $name => $value) {
  406. $this->routeMatch->setParam($name, $value);
  407. }
  408. $result = $this->emptyController->dispatch($this->request, $this->response);
  409. $response = $this->emptyController->getResponse();
  410. $this->assertEquals(405, $response->getStatusCode());
  411. $this->assertEquals('Method Not Allowed', $this->response->getReasonPhrase());
  412. }
  413. public function testNotImplementedMethodSets504HttpCodeProvider()
  414. {
  415. return array(
  416. array('DELETE', array(), array('id' => 1)), // AbstractRestfulController::delete()
  417. array('DELETE', array(), array()), // AbstractRestfulController::deleteList()
  418. array('GET', array(), array('id' => 1)), // AbstractRestfulController::get()
  419. array('GET', array(), array()), // AbstractRestfulController::getList()
  420. array('HEAD', array(), array('id' => 1)), // AbstractRestfulController::head()
  421. array('HEAD', array(), array()), // AbstractRestfulController::head()
  422. array('OPTIONS', array(), array()), // AbstractRestfulController::options()
  423. array('PATCH', http_build_query(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::patch()
  424. array('PATCH', json_encode(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::patch()
  425. array('PATCH', http_build_query(array('foo' => 1)), array()), // AbstractRestfulController::patchList()
  426. array('PATCH', json_encode(array('foo' => 1)), array()), // AbstractRestfulController::patchList()
  427. array('POST', http_build_query(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
  428. array('POST', json_encode(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
  429. array('POST', http_build_query(array('foo' => 1)), array()), // AbstractRestfulController::create()
  430. array('POST', json_encode(array('foo' => 1)), array()), // AbstractRestfulController::create()
  431. array('PUT', http_build_query(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
  432. array('PUT', json_encode(array('foo' => 1)), array('id' => 1)), // AbstractRestfulController::update()
  433. array('PUT', http_build_query(array('foo' => 1)), array()), // AbstractRestfulController::replaceList()
  434. array('PUT', json_encode(array('foo' => 1)), array()), // AbstractRestfulController::replaceList()
  435. );
  436. }
  437. }