createMock(ResponseInterface::class); $finalResponse = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method($method)->with('Foo', 'bar')->willReturn($finalResponse); $controller = $this->getController(['getResponse', 'setResponse']); $controller->expects($this->once())->method('getResponse')->willReturn($response); $controller->expects($this->once())->method('setResponse')->with($finalResponse); $controller->setResponseHeader('Foo', 'bar', $replace); } /** * Test function respondWith * * @return array */ public function respondWithProvider() { return [ [[200, 'application/json'], 200, 'application/json'], [[200, 'json'], 200, 'application/json'], [[204], 204, null], [[204, null], 204, null], [['400 Foo Bar'], [400, 'Foo Bar'], null], [[null, 'application/json'], null, 'application/json'], [['application/json'], null, 'application/json'], [['json'], null, 'application/json'], [['html'], null, 'text/html'], [['text'], null, 'text/plain'] ]; } /** * Test respondWith function * @dataProvider respondWithProvider * * @param array $args * @param int|array $status Expected status code or [code, phrase] * @param string $contentType */ public function testRespondWith($args, $status, $contentType) { $response = $this->createMock(ResponseInterface::class); $statusResponse = isset($status) ? $this->createMock(ResponseInterface::class) : $response; $finalResponse = isset($contentType) ? $this->createMock(ResponseInterface::class) : $statusResponse; $response->expects(isset($status) ? $this->once() : $this->never())->method('withStatus') ->with(...(array)$status) ->willReturn($statusResponse); $statusResponse->expects(isset($contentType) ? $this->once() : $this->never())->method('withHeader') ->with('Content-Type', $contentType) ->willReturn($finalResponse); $controller = $this->getController(['getResponse', 'setResponse']); $controller->expects($this->once())->method('getResponse')->willReturn($response); $controller->expects($this->once())->method('setResponse')->with($finalResponse); $controller->respondWith(...$args); } /** * @expectedException \UnexpectedValueException * @expectedExceptionMessage Format 'foo-bar-zoo' doesn't correspond with a MIME type */ public function testRespondWithFormat() { $response = $this->createMock(ResponseInterface::class); $controller = $this->getController(['getResponse', 'setResponse']); $controller->expects($this->once())->method('getResponse')->willReturn($response); $controller->expects($this->never())->method('setResponse'); $controller->respondWith(null, 'foo-bar-zoo'); } public function implicitStatusCodeProvider() { return [ ['ok', 200], ['created', 201], ['accepted', 202], ['noContent', 204], ['partialContent', 206, [1, 2, 100]], ['redirect', 303, ['example.com']], ['back', 303], ['notModified', 304], ['badRequest', 400, ['']], ['requireAuth', 401], ['requireLogin', 401], ['paymentRequired', 402], ['forbidden', 403], ['notFound', 404], ['conflict', 409, ['']], ['tooManyRequests', 429], ['error', 500] ]; } /** * Test the default status code of different response methods * @dataProvider implicitStatusCodeProvider * * @param string $function * @param int $code Expected status code * @param array $args */ public function testImplicitStatus($function, $code, $args = []) { $response = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method('withStatus')->with($code)->willReturnSelf(); $response->expects($this->any())->method('withHeader')->willReturnSelf(); $controller = $this->getController(['getResponse', 'output', 'getLocalReferer']); $controller->method('getResponse')->willReturn($response); $controller->$function(...$args); } public function explicitStatusCodeProvider() { return [ ['noContent', 205], ['redirect', 301, ['example.com']], ['redirect', 307, ['example.com']], ['badRequest', 412, ['']], ['notFound', 405, ['']], ['error', 500, ['']] ]; } /** * Test setting the status code of different response methods * @dataProvider explicitStatusCodeProvider * * @param string $function * @param int $code Expected status code * @param array $args */ public function testExlicitStatus($function, $code, $args = []) { $response = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method('withStatus')->with($code)->willReturnSelf(); $response->expects($this->any())->method('withHeader')->willReturnSelf(); $controller = $this->getController(['getResponse', 'output', 'getLocalReferer']); $controller->method('getResponse')->willReturn($response); $args[] = $code; $controller->$function(...$args); } public function implicitMessageProvider() { return [ ['paymentRequired', 'Payment required'], ['forbidden', 'Access denied'], ['notFound', 'Not found'], ['tooManyRequests', 'Too many requests'], ['error', 'An unexpected error occured'] ]; } /** * Test the default messages of different response methods * @dataProvider implicitMessageProvider * * @param string $function * @param string $message Expected message * @param array $args */ public function testImplicitMessage($function, $message, $args = []) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($message); $response = $this->createMock(ResponseInterface::class); $response->expects($this->any())->method('getHeaderLine')->with('Content-Type') ->willReturn('text/plain'); $response->expects($this->once())->method('withStatus')->willReturnSelf(); $response->expects($this->once())->method('getBody')->willReturn($stream); $controller = $this->getController(['getResponse']); $controller->method('getResponse')->willReturn($response); $controller->$function(...$args); } public function explicitMessageProvider() { return [ ['badRequest'], ['paymentRequired'], ['forbidden'], ['notFound'], ['conflict'], ['tooManyRequests'], ['error'] ]; } /** * Test the default messages of different response methods * @dataProvider explicitMessageProvider * * @param string $function */ public function testExplicitMessage($function) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with("Hello world"); $response = $this->createMock(ResponseInterface::class); $response->expects($this->any())->method('getHeaderLine')->with('Content-Type') ->willReturn('text/plain'); $response->expects($this->once())->method('withStatus')->willReturnSelf(); $response->expects($this->once())->method('getBody')->willReturn($stream); $controller = $this->getController(['getResponse']); $controller->method('getResponse')->willReturn($response); $controller->$function("Hello world"); } /** * Test 'created' function with a location */ public function testCreated() { $controller = $this->getController(['respondWith', 'setResponseHeader']); $controller->expects($this->once())->method('respondWith')->with(201); $controller->expects($this->once())->method('setResponseHeader')->with('Location', '/foo/bar'); $controller->created('/foo/bar'); } /** * Test 'partialContent' function with Range header */ public function testPartialContent() { $controller = $this->getController(['respondWith', 'setResponseHeader']); $controller->expects($this->once())->method('respondWith')->with(206); $controller->expects($this->exactly(2))->method('setResponseHeader')->withConsecutive( ['Content-Range', 'bytes 100-200/500'], ['Content-Length', 100] ); $controller->partialContent(100, 200, 500); } public function redirectStatusProvider() { return [ [301], [302], ['307 Temporary Redirect'] ]; } /** * Test 'redirect' function * @dataProvider redirectStatusProvider * * @param int|string $status */ public function testRedirect($status) { $controller = $this->getController(['respondWith', 'setResponseHeader', 'output']); $controller->expects($this->once())->method('respondWith')->with($status); $controller->expects($this->once())->method('setResponseHeader')->with('Location', '/foo'); $controller->expects($this->once())->method('output') ->with('You are being redirected to /foo', 'text/html'); $controller->redirect('/foo', $status); } /** * Provide data fot testing 'getLocalReferer' function * * @return array */ public function localRefererProvider() { return [ [null, '/'], ['/', '/'], ['/some/path', '/some/path'] ]; } /** * Test 'back' function * @dataProvider localRefererProvider * * @param string $referer * @param string $location */ public function testBack($referer, $location) { $controller = $this->getController(['getLocalReferer', 'respondWith', 'setResponseHeader', 'output']); $controller->expects($this->once())->method('getLocalReferer')->willReturn($referer); $controller->expects($this->once())->method('respondWith')->with(303); $controller->expects($this->once())->method('setResponseHeader')->with('Location', $location); $controller->expects($this->once())->method('output') ->with('You are being redirected to ' . $location . '', 'text/html'); $controller->back(); } /** * Provide data for testing output * * @return array */ public function outputProvider() { $xml = "\nTest\n"; $dom = new \DOMDocument(); $dom->loadXML($xml); return [ ['green beans', null, 'green beans', 'text/html'], ['hello world', 'text', 'hello world', 'text/plain'], ['abc();', 'js', 'abc();', 'application/javascript'], ['h1 { color: blue; }', 'css', 'h1 { color: blue; }', 'text/css'], ['{ "testKey": "testValue" }', 'json', '{ "testKey": "testValue" }', 'application/json'], [['testKey' => 'testValue'], 'json', '{"testKey":"testValue"}', 'application/json'], [simplexml_load_string($xml), 'xml', $xml, 'application/xml'], [$dom, 'xml', $xml, 'application/xml'], [$dom->firstChild->firstChild, 'xml', 'Test', 'application/xml'] ]; } /** * Test output * @dataProvider outputProvider * * @param mixed $data * @param string $format * @param string $content Expected content * @param string $contentType Expected Content-Type */ public function testOutputWithFormat($data, $format, $content, $contentType) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($content); $response = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method('getBody')->willReturn($stream); $controller = $this->getController(['getResponse', 'setResponseHeader']); $controller->method('getResponse')->willReturn($response); $controller->expects($this->once())->method('setResponseHeader')->with('Content-Type', $contentType); $controller->output($data, $format); } /** * Test output, getting the format from the Content-Type response header * @dataProvider outputProvider * * @param mixed $data * @param string $format * @param string $content Expected content * @param string $contentType Expected Content-Type */ public function testOutputWithoutFormat($data, $format, $content, $contentType) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($content); $response = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method('getHeaderline')->with('Content-Type') ->willReturn($format ? $contentType : ''); $response->expects($this->once())->method('getBody')->willReturn($stream); $controller = $this->getController(['getResponse', 'setResponseHeader']); $controller->method('getResponse')->willReturn($response); $controller->expects($format ? $this->never() : $this->once())->method('setResponseHeader') ->with('Content-Type', $contentType); $controller->output($data); } /** * Test output when using byDefaultSerializeTo * @dataProvider outputProvider * * @param mixed $data * @param string $format * @param string $content Expected content * @param string $contentType Expected Content-Type */ public function testByDefaultSerializeTo($data, $format, $content, $contentType) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($content); $response = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method('getBody')->willReturn($stream); $controller = $this->getController(['getResponse', 'setResponseHeader']); $controller->method('getResponse')->willReturn($response); $controller->expects($this->once())->method('setResponseHeader')->with('Content-Type', $contentType); $controller->byDefaultSerializeTo($format); $controller->output($data); } /** * Test output when using byDefaultSerializeTo as fallback * @dataProvider outputProvider * * @param mixed $data * @param string $format * @param string $content Expected content * @param string $contentType Expected Content-Type */ public function testByDefaultSerializeToFallback($data, $format, $content, $contentType) { $stream = $this->createMock(StreamInterface::class); $stream->expects($this->once())->method('write')->with($content); $response = $this->createMock(ResponseInterface::class); $response->expects($this->once())->method('getHeaderline')->with('Content-Type')->willReturn('text/plain'); $response->expects($this->once())->method('getBody')->willReturn($stream); $controller = $this->getController(['getResponse', 'setResponseHeader']); $controller->method('getResponse')->willReturn($response); $controller->expects(is_string($data) ? $this->never() : $this->once())->method('setResponseHeader') ->with('Content-Type', $contentType); $controller->byDefaultSerializeTo($format); $controller->output($data); } /** * Provide data for testing output * * @return array */ public function unserializableProvider() { $xml = "\nTest\n"; $stringable = $this->createPartialMock('stdClass', ['__toString']); $stringable->method('__toString')->willReturn('I was an object'); return [ [['abc', 'def'], 'text/plain', "Unable to serialize array to 'text/plain'"], [['abc', 'def'], null, "Unable to serialize array to 'text/html'"], [new \stdClass(), 'text', "Unable to serialize stdClass object to 'text/plain'"], [new \stdClass(), 'xml', "Unable to serialize stdClass object to XML"], ]; } /** * Test that serializeData throws an UnexpectedValueException * @dataProvider unserializableProvider * * @param mixed $data * @param string $format * @param string $message Excpected exception message */ public function testSerializeDataException($data, $format, $message) { $this->expectException(\UnexpectedValueException::class); $this->expectExceptionMessage($message); $response = $this->createMock(ResponseInterface::class); $controller = $this->getController(['getResponse', 'setResponseHeader']); $controller->method('getResponse')->willReturn($response); $controller->output($data, $format); } }