getMockBuilder(Controller::class)->disableOriginalConstructor(); if ($methods) { $builder->setMethods($methods); } return $builder->getMockForAbstractClass(); } /** * Test running controller */ public function testInvoke() { $test = $this; $controller = $this->getController(); $request = $this->createMock(ServerRequestInterface::class); $response = $this->createMock(ResponseInterface::class); $finalResponse = $this->createMock(ResponseInterface::class); $controller->expects($this->once())->method('run') ->willReturnCallback(Closure::bind(function() use ($test, $request, $response, $finalResponse) { $test->assertSame($request, $this->getRequest()); $test->assertSame($response, $this->getResponse()); return $finalResponse; }, $controller, Controller::class)); $result = $controller($request, $response); $this->assertEquals($finalResponse, $result); } /** * Test response status functions if response object is not set */ public function testResponseStatusEmptyResponse() { $controller = $this->getController(); $data = $this->getStatusCodesMap(null); foreach ($data as $func => $value) { $this->assertEquals($value, $controller->$func(), "Method '$func' returns incorrect value"); } } /** * Test functions that check response status code * * @dataProvider responseStatusProvider * @param int status code */ public function testResponseStatus($code) { $controller = $this->getController(); list($request, $response) = $this->getRequests(); $response->method('getStatusCode')->will($this->returnValue($code)); $controller($request, $response); $data = $this->getStatusCodesMap($code); foreach ($data as $func => $value) { $this->assertEquals($value, $controller->$func(), "Method '$func' returns incorrect value"); } $this->assertEquals($data['isClientError'] || $data['isServerError'], $controller->isError() , "Method 'isError' returns incorrect value"); } /** * Provide data for testing status methods * * @return array */ public function responseStatusProvider() { return [ [null], [199], [200], [201], [299], [300], [304], [399], [400], [403], [499], [500], [503] ]; } /** * Test functions that check request method * * @dataProvider requestMethodProvider * @param string $method */ public function testRequestMethod($method) { $controller = $this->getController(); list($request, $response) = $this->getRequests(); $request->method('getMethod')->will($this->returnValue($method)); $controller($request, $response); $data = $this->getMethodsMap($method); foreach ($data as $func => $value) { $this->assertEquals($value, $controller->$func(), "Method '$func' returns incorrect value"); } } /** * Provide data for testing functions that determine request method * * @return array */ public function requestMethodProvider() { return [ ['GET'], ['POST'], ['PUT'], ['DELETE'], ['HEAD'] ]; } /** * Test encodeData method, positive tests * * @dataProvider encodeDataPositiveProvider * @param mixed $data * @param string $format * @param string $callback Callback name for testing jsonp request */ public function testEncodeDataPositive($data, $format, $callback = null) { $this->markTestSkipped(); $controller = $this->getController(['getRequest']); list($request) = $this->getRequests(); if ($callback) { $request->method('getQueryParams')->will($this->returnValue(['callback' => $callback])); } $controller->method('getRequest')->will($this->returnValue($request)); $result = $controller->encodeData($data, $format); $expect = null; if ($format === 'json') { $expect = json_encode($data); if ($callback) $expect = "$callback($expect)"; } else { $expect = $data->asXML(); } $this->assertNotEmpty($result, "Result should not be empty"); $this->assertEquals($expect, $result, "Data was not encoded correctly"); } /** * Provide data for testing encodeData method * * @return array */ public function encodeDataPositiveProvider() { $xml = simplexml_load_string( " Test tag Test " ); return [ ['test_string', 'json'], [['testKey' => 'testValue'], 'json'], [['testKey' => 'testValue'], 'json', 'test_callback'], ['', 'json'], ['', 'json', 'test_callback'], [$xml, 'xml'] ]; } /** * Test encodeData method, negative tests * * @dataProvider encodeDataNegativeProvider * @param mixed $data * @param string $format */ public function testEncodeDataNegative($data, $format) { $this->markTestSkipped(); $controller = $this->getController(['getRequest']); list($request) = $this->getRequests(); $controller->method('getRequest')->will($this->returnValue($request)); $this->expectException(\InvalidArgumentException::class); $result = $controller->encodeData($data, $format); } /** * Provide data for testing encodeData method * * @return array */ public function encodeDataNegativeProvider() { return [ ['test_string', 'html'], ['test_string', 'jpg'] ]; } /** * Test output * * @dataProvider outputProvider * @param mixed $data * @param string $format * @param string $contentType * @param string $callback Callback name for testing jsonp request */ public function testOutput($data, $format, $contentType, $callback = '') { $controller = $this->getController(['getRequest', 'getResponse']); list($request, $response) = $this->getRequests(); if (is_scalar($data)) { $content = $data; } elseif ($format === 'json') { $content = json_encode($data); if ($callback) $content = "$callback($content)"; } elseif ($format === 'xml') { $content = $data->asXML(); } $this->expectOutput($response, $content, $contentType); if ($callback) { $request->method('getQueryParams')->will($this->returnValue(['callback' => $callback])); } $controller->method('getRequest')->will($this->returnValue($request)); $controller->method('getResponse')->will($this->returnValue($response)); $result = $controller->output($data, $format); $this->assertEquals($result, $response, "Output should return response instance"); } /** * Provide data for testing output * * @return array */ public function outputProvider() { $xml = simplexml_load_string( " Test tag Test " ); return [ ['test_string', 'text', 'text/plain'], ['javascript:test_call();', 'js', 'application/javascript'], ['test {}', 'css', 'text/css'], ['test_string', 'json', 'application/json'], [['testKey' => 'testValue'], 'json', 'application/json'], [['testKey' => 'testValue'], 'json', 'application/json', 'test_callback'], ['', 'json', 'application/json'], ['', 'json', 'application/json', 'test_callback'], [$xml, 'xml', 'application/xml'] ]; } /** * Test functions that deal with error messages * * @dataProvider errorMessagesProvider * @param string $function * @param int $code * @param boolean $default Is code default for this function */ public function testErrorMessages($function, $code, $default) { $message = 'Test message'; $controller = $this->getController(['getResponse']); list(, $response) = $this->getRequests(); $this->expectErrorMessage($response, $message, $code); $controller->method('getResponse')->will($this->returnValue($response)); $result = $default ? $controller->{$function}($message) : $controller->{$function}($message, $code); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Provide data for testing error messages functions * * @return array */ public function errorMessagesProvider() { return [ ['error', 400, true], ['error', 403, false], ['tooManyRequests', 429, true], ['tooManyRequests', 400, false], ['conflict', 409, true], ['conflict', 403, false], ['notFound', 404, true], ['notFound', 400, false], ['forbidden', 403, true], ['forbidden', 409, false], ['badRequest', 400, true], ['badRequest', 403, false] ]; } /** * Test setting flash * * @dataProvider flashProvider * @param object $data */ public function testFlash($data) { $controller = $this->getMockBuilder(Controller::class)->disableOriginalConstructor()->getMockForAbstractClass(); $flash = $controller->flash(); $this->assertInstanceOf(Flash::class, $flash, "Flash is not set"); $this->assertEmpty($flash->get(), "Flash data should be empty"); $flash = $controller->flash($data->type, $data->message); $this->assertInstanceOf(Flash::class, $flash, "Flash is not set"); $this->assertEquals($data, $flash->get(), "Flash data is incorrect"); $flash = $controller->flash(); $this->assertInstanceOf(Flash::class, $flash, "Flash is not set"); $this->assertEquals($data, $flash->get(), "Flash data is incorrect"); $flash->clear(); } /** * Test setting flash * * @return array */ public function flashProvider() { return [ [(object)['type' => 'test_type', 'message' => 'Test message']] ]; } /** * Test respondWith function * * @dataProvider respondWithProvider * @param int|string $code * @param string $format * @param int $setCode Actual code that will be set in response * @param string $contentType */ public function testRespondWith($code, $format, $setCode, $contentType) { $controller = $this->getController(['getResponse']); list(, $response) = $this->getRequests(); $this->expectResponseWith($response, $setCode, $contentType); $controller->method('getResponse')->will($this->returnValue($response)); $result = $controller->respondWith($code, $format); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Test function respondWith * * @return array */ public function respondWithProvider() { return [ [200, 'json', 200, 'application/json'], [200, 'application/json', 200, 'application/json'], [204, null, 204, null], ['204 Created', null, 204, null], ['json', null, null, 'application/json'] ]; } /** * Test functions that are simple wrappers around respondWith function * * @dataProvider respondWithWrappersProvider * @param string $function * @param int $code */ public function testResponseWithWrappers($function, $code) { $controller = $this->getController(['getResponse']); list(, $response) = $this->getRequests(); $this->expectResponseWith($response, $code); $controller->method('getResponse')->will($this->returnValue($response)); $result = $controller->{$function}(); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Provide data for testing respondWith wrappers * * @return array */ public function respondWithWrappersProvider() { return [ ['ok', 200], ['noContent', 204] ]; } /** * Test 'created' function * * @dataProvider createdProvider * @param string $location */ public function testCreated($location) { $controller = $this->getController(['getResponse']); list(, $response) = $this->getRequests(); $response->expects($this->once())->method('withStatus')->with($this->equalTo(201))->will($this->returnSelf()); if ($location) { $response->expects($this->once())->method('withHeader')->with($this->equalTo('Location'), $this->equalTo($location))->will($this->returnSelf()); } $controller->method('getResponse')->will($this->returnValue($response)); $result = $controller->created($location); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Provide data for testing 'created' function * * @return array */ public function createdProvider() { return [ [''], ['/some-path/test'] ]; } /** * Test 'redirect' function * * @dataProvider redirectProvider * @param string $url * @param int $code * @param boolean $default */ public function testRedirect($url, $code, $default) { $controller = $this->getController(['getResponse']); list(, $response) = $this->getRequests(); $this->expectRedirect($response, $url, $code); $controller->method('getResponse')->will($this->returnValue($response)); $result = $default ? $controller->redirect($url) : $controller->redirect($url, $code); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Provide data for testing 'redirect' function * * @return array */ public function redirectProvider() { return [ ['/test-url', 303, true], ['/test-url', 301, false] ]; } /** * Test 'requireLogin' function * * @dataProvider requireLoginProvider * @param string $function */ public function testRequireLogin($function) { $controller = $this->getController(['getResponse']); list(, $response) = $this->getRequests(); $this->expectRedirect($response, '/401', 303); $controller->method('getResponse')->will($this->returnValue($response)); $result = $controller->{$function}(); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Provide data for testing 'requireLogon' function * * @return array */ public function requireLoginProvider() { return [ ['requireLogin'], ['requireAuth'] ]; } /** * Test 'getLocalReferer' funtion * * @dataProvider localRefererProvider * @param string $referer * @param string $host * @param boolean $local */ public function testLocalReferer($referer, $host, $local) { $controller = $this->getController(['getRequest']); list($request) = $this->getRequests(); $this->expectLocalReferer($request, $referer, $host); $controller->method('getRequest')->will($this->returnValue($request)); $result = $controller->getLocalReferer(); $local ? $this->assertEquals($referer, $result, "Local referer should be returned") : $this->assertEquals('', $result, "Local referer should not be returned"); } /** * Test 'back' function * * @dataProvider localRefererProvider * @param string $referer * @param string $host * @param boolean $local */ public function testBack($referer, $host, $local) { $controller = $this->getController(['getRequest', 'getResponse']); list($request, $response) = $this->getRequests(); $this->expectLocalReferer($request, $referer, $host); $this->expectRedirect($response, $local ? $referer : '/', 303); $controller->method('getRequest')->will($this->returnValue($request)); $controller->method('getResponse')->will($this->returnValue($response)); $result = $controller->back(); $this->assertEquals($result, $response, "Response object should be returned"); } /** * Provide data fot testing 'getLocalReferer' function * * @return array */ public function localRefererProvider() { return [ ['http://not-local-host.com/path', 'local-host.com', false], ['http://local-host.com/path', 'local-host.com', true] ]; } /** * Expect for 'getLocalReferer' function to work correctly * * @param ServerRequestInterface $request * @param string $referer * @param string $host */ public function expectLocalReferer($request, $referer, $host) { $request->expects($this->exactly(2))->method('getHeaderLine')->withConsecutive( [$this->equalTo('HTTP_REFERER')], [$this->equalTo('HTTP_HOST')] )->will($this->returnCallback(function($header) use ($referer, $host) { return $header === 'HTTP_REFERER' ? $referer : $host; })); } /** * Expect for redirect * * @param ResponseInterface $response * @param string $url * @param int $code */ public function expectRedirect($response, $url, $code) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($this->equalTo('You are being redirected to ' . $url . '')); $response->expects($this->once())->method('getBody')->will($this->returnValue($stream)); $response->expects($this->once())->method('withStatus')->with($this->equalTo($code))->will($this->returnSelf()); $response->expects($this->exactly(2))->method('withHeader')->withConsecutive( [$this->equalTo('Content-Type'), $this->equalTo('text/html')], [$this->equalTo('Location'), $this->equalTo($url)] )->will($this->returnSelf()); } /** * Expect correct work of respondWith function * * @param ResponseInterface $response * @param int $code * @param string $contentType */ public function expectResponseWith($response, $code, $contentType = null) { $code ? $response->expects($this->once())->method('withStatus')->with($this->equalTo($code))->will($this->returnSelf()) : $response->expects($this->never())->method('withStatus')->with($this->equalTo($code)); $contentType ? $response->expects($this->once())->method('withHeader')->with($this->equalTo('Content-Type'), $this->equalTo($contentType))->will($this->returnSelf()) : $response->expects($this->never())->method('withHeader')->with($this->equalTo('Content-Type'), $this->equalTo($contentType)); } /** * Expect for correct work of error message functions * * @param ResponseInterface $response * @param string $message * @param int $code */ public function expectErrorMessage($response, $message, $code) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($this->equalTo($message)); $response->expects($this->once())->method('withStatus')->with($this->equalTo($code))->will($this->returnSelf()); $response->expects($this->once())->method('getBody')->will($this->returnValue($stream)); } /** * Expects that output will be set to content * * @param ResponseInterface $response * @param string $content * @param string $contentType */ public function expectOutput($response, $content, $contentType) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($this->equalTo($content)); $response->expects($this->once())->method('withHeader')->with($this->equalTo('Content-Type'), $this->equalTo($contentType))->will($this->returnSelf()); $response->expects($this->once())->method('getBody')->will($this->returnValue($stream)); } /** * Get request and response instances * * @return array */ public function getRequests() { return [ $this->createMock(ServerRequestInterface::class), $this->createMock(ResponseInterface::class) ]; } /** * Get map of status codes to states * * @param int $code * @return [] */ public function getStatusCodesMap($code) { return [ 'isSuccessful' => !$code || ($code >= 200 && $code < 300), 'isRedirection' => $code >= 300 && $code < 400, 'isClientError' => $code >= 400 && $code < 500, 'isServerError' => $code >= 500 ]; } /** * Get map of request methods * * @param string $method * @return array */ public function getMethodsMap($method) { return [ 'isGetRequest' => $method === 'GET', 'isPostRequest' => $method === 'POST', 'isPutRequest' => $method === 'PUT', 'isDeleteRequest' => $method === 'DELETE', 'isHeadRequest' => $method === 'HEAD' ]; } }