ServerTest.php 11 KB

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