ServerTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.3.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Http;
  17. use Cake\Core\HttpApplicationInterface;
  18. use Cake\Event\EventInterface;
  19. use Cake\Event\EventManager;
  20. use Cake\Http\BaseApplication;
  21. use Cake\Http\CallbackStream;
  22. use Cake\Http\MiddlewareQueue;
  23. use Cake\Http\Server;
  24. use Cake\Http\ServerRequest;
  25. use Cake\Http\Session;
  26. use Cake\TestSuite\TestCase;
  27. use InvalidArgumentException;
  28. use TestApp\Http\MiddlewareApplication;
  29. use Zend\Diactoros\Response;
  30. require_once __DIR__ . '/server_mocks.php';
  31. /**
  32. * Server test case
  33. */
  34. class ServerTest extends TestCase
  35. {
  36. /**
  37. * @var string
  38. */
  39. protected $config;
  40. /**
  41. * @var array
  42. */
  43. private $server;
  44. /**
  45. * Setup
  46. *
  47. * @return void
  48. */
  49. public function setUp(): void
  50. {
  51. parent::setUp();
  52. $this->server = $_SERVER;
  53. $this->config = dirname(dirname(__DIR__)) . '/test_app/config';
  54. $GLOBALS['mockedHeaders'] = [];
  55. $GLOBALS['mockedHeadersSent'] = true;
  56. }
  57. /**
  58. * Teardown
  59. *
  60. * @return void
  61. */
  62. public function tearDown(): void
  63. {
  64. parent::tearDown();
  65. $_SERVER = $this->server;
  66. unset($GLOBALS['mockedHeadersSent']);
  67. }
  68. /**
  69. * test get/set on the app
  70. *
  71. * @return void
  72. */
  73. public function testAppGetSet()
  74. {
  75. /** @var \Cake\Http\BaseApplication|\PHPUnit\Framework\MockObject\MockObject $app */
  76. $app = $this->getMockBuilder(BaseApplication::class)
  77. ->setConstructorArgs([$this->config])
  78. ->getMock();
  79. $manager = new EventManager();
  80. $app->method('getEventManager')
  81. ->willReturn($manager);
  82. $server = new Server($app);
  83. $this->assertSame($app, $server->getApp());
  84. $this->assertSame($app->getEventManager(), $server->getEventManager());
  85. }
  86. /**
  87. * test run building a response
  88. *
  89. * @return void
  90. */
  91. public function testRunWithRequest()
  92. {
  93. $request = new ServerRequest();
  94. $request = $request->withHeader('X-pass', 'request header');
  95. $app = new MiddlewareApplication($this->config);
  96. $server = new Server($app);
  97. $res = $server->run($request);
  98. $this->assertEquals(
  99. 'source header',
  100. $res->getHeaderLine('X-testing'),
  101. 'Input response is carried through out middleware'
  102. );
  103. $this->assertEquals(
  104. 'request header',
  105. $res->getHeaderLine('X-pass'),
  106. 'Request is used in middleware'
  107. );
  108. }
  109. /**
  110. * test run calling plugin hooks
  111. *
  112. * @return void
  113. */
  114. public function testRunCallingPluginHooks()
  115. {
  116. $request = new ServerRequest();
  117. $request = $request->withHeader('X-pass', 'request header');
  118. /** @var \TestApp\Http\MiddlewareApplication|\PHPUnit\Framework\MockObject\MockObject $app */
  119. $app = $this->getMockBuilder(MiddlewareApplication::class)
  120. ->setMethods(['pluginBootstrap', 'pluginEvents', 'pluginMiddleware'])
  121. ->setConstructorArgs([$this->config])
  122. ->getMock();
  123. $app->expects($this->at(0))
  124. ->method('pluginBootstrap');
  125. $app->expects($this->at(1))
  126. ->method('pluginMiddleware')
  127. ->with($this->isInstanceOf(MiddlewareQueue::class))
  128. ->will($this->returnCallback(function ($middleware) {
  129. return $middleware;
  130. }));
  131. $server = new Server($app);
  132. $res = $server->run($request);
  133. $this->assertEquals(
  134. 'source header',
  135. $res->getHeaderLine('X-testing'),
  136. 'Input response is carried through out middleware'
  137. );
  138. $this->assertEquals(
  139. 'request header',
  140. $res->getHeaderLine('X-pass'),
  141. 'Request is used in middleware'
  142. );
  143. }
  144. /**
  145. * test run building a request from globals.
  146. *
  147. * @return void
  148. */
  149. public function testRunWithGlobals()
  150. {
  151. $_SERVER['HTTP_X_PASS'] = 'globalvalue';
  152. $app = new MiddlewareApplication($this->config);
  153. $server = new Server($app);
  154. $res = $server->run();
  155. $this->assertEquals(
  156. 'globalvalue',
  157. $res->getHeaderLine('X-pass'),
  158. 'Default request is made from server'
  159. );
  160. }
  161. /**
  162. * Test middleware being invoked.
  163. *
  164. * @return void
  165. */
  166. public function testRunMultipleMiddlewareSuccess()
  167. {
  168. $app = new MiddlewareApplication($this->config);
  169. $server = new Server($app);
  170. $res = $server->run();
  171. $this->assertSame('first', $res->getHeaderLine('X-First'));
  172. $this->assertSame('second', $res->getHeaderLine('X-Second'));
  173. }
  174. /**
  175. * Test that run closes session after invoking the application (if CakePHP ServerRequest is used).
  176. */
  177. public function testRunClosesSessionIfServerRequestUsed()
  178. {
  179. $sessionMock = $this->createMock(Session::class);
  180. $sessionMock->expects($this->once())
  181. ->method('close');
  182. $app = new MiddlewareApplication($this->config);
  183. $server = new Server($app);
  184. $request = new ServerRequest(['session' => $sessionMock]);
  185. $res = $server->run($request);
  186. // assert that app was executed correctly
  187. $this->assertSame(
  188. 200,
  189. $res->getStatusCode(),
  190. "Application was expected to be executed"
  191. );
  192. $this->assertSame(
  193. 'source header',
  194. $res->getHeaderLine('X-testing'),
  195. "Application was expected to be executed"
  196. );
  197. }
  198. /**
  199. * Test that run does not close the session if CakePHP ServerRequest is not used.
  200. */
  201. public function testRunDoesNotCloseSessionIfServerRequestNotUsed()
  202. {
  203. $request = new \Zend\Diactoros\ServerRequest();
  204. $app = new MiddlewareApplication($this->config);
  205. $server = new Server($app);
  206. $res = $server->run($request);
  207. // assert that app was executed correctly
  208. $this->assertSame(
  209. 200,
  210. $res->getStatusCode(),
  211. "Application was expected to be executed"
  212. );
  213. $this->assertSame(
  214. 'source header',
  215. $res->getHeaderLine('X-testing'),
  216. "Application was expected to be executed"
  217. );
  218. }
  219. /**
  220. * Test that emit invokes the appropriate methods on the emitter.
  221. *
  222. * @return void
  223. */
  224. public function testEmit()
  225. {
  226. $app = new MiddlewareApplication($this->config);
  227. $server = new Server($app);
  228. $response = $server->run();
  229. $final = $response
  230. ->withHeader('X-First', 'first')
  231. ->withHeader('X-Second', 'second');
  232. $emitter = $this->getMockBuilder('Zend\HttpHandlerRunner\Emitter\EmitterInterface')->getMock();
  233. $emitter->expects($this->once())
  234. ->method('emit')
  235. ->with($final);
  236. $server->emit($final, $emitter);
  237. }
  238. /**
  239. * Test that emit invokes the appropriate methods on the emitter.
  240. *
  241. * @return void
  242. */
  243. public function testEmitCallbackStream()
  244. {
  245. $GLOBALS['mockedHeadersSent'] = false;
  246. $response = new Response('php://memory', 200, ['x-testing' => 'source header']);
  247. $response = $response->withBody(new CallbackStream(function () {
  248. echo 'body content';
  249. }));
  250. $app = new MiddlewareApplication($this->config);
  251. $server = new Server($app);
  252. ob_start();
  253. $server->emit($response);
  254. $result = ob_get_clean();
  255. $this->assertSame('body content', $result);
  256. }
  257. /**
  258. * Ensure that the Server.buildMiddleware event is fired.
  259. *
  260. * @return void
  261. */
  262. public function testBuildMiddlewareEvent()
  263. {
  264. $app = new MiddlewareApplication($this->config);
  265. $server = new Server($app);
  266. $this->called = false;
  267. $server->getEventManager()->on('Server.buildMiddleware', function (EventInterface $event, MiddlewareQueue $middleware) {
  268. $middleware->add(function ($req, $res, $next) {
  269. $this->called = true;
  270. return $next($req, $res);
  271. });
  272. $this->middleware = $middleware;
  273. });
  274. $server->run();
  275. $this->assertTrue($this->called, 'Middleware added in the event was not triggered.');
  276. $this->middleware->seek(3);
  277. $this->assertInstanceOf('Closure', $this->middleware->current()->getCallable(), '2nd last middleware is a closure');
  278. }
  279. /**
  280. * test event manager proxies to the application.
  281. *
  282. * @return void
  283. */
  284. public function testEventManagerProxies()
  285. {
  286. /** @var \Cake\Http\BaseApplication|\PHPUnit\Framework\MockObject\MockObject $app */
  287. $app = $this->getMockForAbstractClass(
  288. BaseApplication::class,
  289. [$this->config]
  290. );
  291. $server = new Server($app);
  292. $this->assertSame($app->getEventManager(), $server->getEventManager());
  293. }
  294. /**
  295. * test event manager cannot be set on applications without events.
  296. *
  297. * @return void
  298. */
  299. public function testGetEventManagerNonEventedApplication()
  300. {
  301. /** @var \Cake\Core\HttpApplicationInterface|\PHPUnit\Framework\MockObject\MockObject $app */
  302. $app = $this->createMock(HttpApplicationInterface::class);
  303. $server = new Server($app);
  304. $this->assertSame(EventManager::instance(), $server->getEventManager());
  305. }
  306. /**
  307. * test event manager cannot be set on applications without events.
  308. *
  309. * @return void
  310. */
  311. public function testSetEventManagerNonEventedApplication()
  312. {
  313. /** @var \Cake\Core\HttpApplicationInterface|\PHPUnit\Framework\MockObject\MockObject $app */
  314. $app = $this->createMock(HttpApplicationInterface::class);
  315. $events = new EventManager();
  316. $server = new Server($app);
  317. $this->expectException(InvalidArgumentException::class);
  318. $server->setEventManager($events);
  319. }
  320. }