diff options
Diffstat (limited to 'tests/Router')
-rw-r--r-- | tests/Router/Middleware/BasePathTest.php | 142 | ||||
-rw-r--r-- | tests/Router/Middleware/ErrorHandlerTest.php | 86 | ||||
-rw-r--r-- | tests/Router/Middleware/NotFoundTest.php | 363 | ||||
-rw-r--r-- | tests/Router/RouteTest.php | 42 | ||||
-rw-r--r-- | tests/Router/Routes/GlobTest.php | 323 | ||||
-rw-r--r-- | tests/Router/Routes/RouteBindingTest.php | 263 | ||||
-rw-r--r-- | tests/Router/Runner/CallbackTest.php | 89 | ||||
-rw-r--r-- | tests/Router/Runner/ControllerTest.php | 182 | ||||
-rw-r--r-- | tests/Router/Runner/PhpScriptTest.php | 152 | ||||
-rw-r--r-- | tests/Router/Runner/RunnerFactoryTest.php | 41 | ||||
-rw-r--r-- | tests/Router/RunnerFactoryTest.php | 58 | ||||
-rw-r--r-- | tests/Router/RunnerTest.php | 53 |
12 files changed, 930 insertions, 864 deletions
diff --git a/tests/Router/Middleware/BasePathTest.php b/tests/Router/Middleware/BasePathTest.php index eb9a2e8..d615d70 100644 --- a/tests/Router/Middleware/BasePathTest.php +++ b/tests/Router/Middleware/BasePathTest.php @@ -1,25 +1,22 @@ <?php +namespace Jasny\Router\Middleware; + use Jasny\Router\Middleware\BasePath; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UriInterface; -class BasePathTest extends PHPUnit_Framework_TestCase -{ - /** - * Test creating middleware with invalid parameter - * - * @dataProvider invalidConstructProvider - */ - public function testInvalidConstruct($basePath) - { - $this->expectException(\InvalidArgumentException::class); - - $pathHandler = new BasePath($basePath); - } +use Jasny\Router\TestHelpers; +/** + * @covers Jasny\Router\Middleware\BasePath + */ +class BasePathTest extends \PHPUnit_Framework_TestCase +{ + use TestHelpers; + /** * Provide data for testing invalid BasePath creation * @@ -39,17 +36,14 @@ class BasePathTest extends PHPUnit_Framework_TestCase } /** - * Test creating BasePath instance + * Test creating middleware with invalid parameter * - * @dataProvider validConstructProvider - * @param string $basePath + * @dataProvider invalidConstructProvider + * @expectedException InvalidArgumentException */ - public function testValidConstruct($basePath, $validBasePath) + public function testInvalidConstruct($basePath) { - $pathHandler = new BasePath($basePath); - - $this->assertNotEmpty($pathHandler->getBasePath(), "Empty base path"); - $this->assertEquals($validBasePath, $pathHandler->getBasePath(), "Base path was not set correctly"); + new BasePath($basePath); } /** @@ -71,41 +65,32 @@ class BasePathTest extends PHPUnit_Framework_TestCase } /** - * Test invoke with invalid 'next' param + * Test creating BasePath instance + * + * @dataProvider validConstructProvider + * @param string $basePath */ - public function testInvokeInvalidNext() + public function testValidConstruct($basePath, $validBasePath) { - $middleware = new BasePath('/foo'); - list($request, $response) = $this->getRequests(); - - $this->expectException(\InvalidArgumentException::class); + $pathHandler = new BasePath($basePath); - $result = $middleware($request, $response, 'not_callable'); + $this->assertNotEmpty($pathHandler->getBasePath(), "Empty base path"); + $this->assertEquals($validBasePath, $pathHandler->getBasePath(), "Base path was not set correctly"); } /** - * Test case when given request path does not starts with given base path - * - * @dataProvider notFoundProvider - * @param string $basePath - * @param string $path + * Test invoke with invalid 'next' param + * + * @expectedException InvalidArgumentException */ - public function testNotFound($basePath, $path) + public function testInvokeInvalidNext() { - $middleware = new BasePath($basePath); - list($request, $response) = $this->getRequests(); - - $this->expectRequestGetPath($request, $path); - $this->expectNotFound($response); - - $result = $middleware($request, $response, function($response, $request) { - $response->nextCalled = true; + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); - return $response; - }); + $middleware = new BasePath('/foo'); - $this->assertEquals(get_class($response), get_class($result), "Middleware should return response object"); - $this->assertFalse(isset($response->nextCalled), "'next' was called"); + $middleware($request, $response, 'not_callable'); } /** @@ -128,28 +113,27 @@ class BasePathTest extends PHPUnit_Framework_TestCase } /** - * Test correct case, when path contains base path - * - * @dataProvider foundProvider + * Test case when given request path does not starts with given base path + * @dataProvider notFoundProvider + * * @param string $basePath * @param string $path - * @param string $noBasePath */ - public function testFound($basePath, $path, $noBasePath) + public function testNotFound($basePath, $path) { - $middleware = new BasePath($basePath); - list($request, $response) = $this->getRequests(); - - $this->expectRequestSetBasePath($request, $basePath, $path, $noBasePath); + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); - $result = $middleware($request, $response, function($request, $response) { - $response->nextCalled = true; + $middleware = new BasePath($basePath); + + $this->expectRequestGetPath($request, $path); + $finalResponse = $this->expectNotFound($response); - return $response; - }); + $next = $this->createCallbackMock($this->never()); + + $result = $middleware($request, $response, $next); - $this->assertEquals(get_class($response), get_class($result), "Middleware should return response object"); - $this->assertTrue($response->nextCalled, "'next' was not called"); + $this->assertSame($finalResponse, $result); } /** @@ -172,19 +156,31 @@ class BasePathTest extends PHPUnit_Framework_TestCase } /** - * Get requests for testing - * - * @param string $path - * @return array + * Test correct case, when path contains base path + * @dataProvider foundProvider + * + * @param string $basePath + * @param string $path + * @param string $noBasePath */ - public function getRequests($path = null) + public function testFound($basePath, $path, $noBasePath) { $request = $this->createMock(ServerRequestInterface::class); $response = $this->createMock(ResponseInterface::class); + $finalRespose = $this->createMock(ResponseInterface::class); + + $middleware = new BasePath($basePath); + + $this->expectRequestSetBasePath($request, $basePath, $path, $noBasePath); - return [$request, $response]; + $next = $this->createCallbackMock($this->once(), [$request, $response], $finalRespose); + + $result = $middleware($request, $response, $next); + + $this->assertSame($finalRespose, $result); } + /** * Expect that request will return a path * @@ -221,15 +217,19 @@ class BasePathTest extends PHPUnit_Framework_TestCase * Expect for not found error * * @param ResponseInterface $response + * @return ResponseInterface */ public function expectNotFound(ResponseInterface $response) { + $finalResponse = $this->createMock(ResponseInterface::class); + + $response->expects($this->once())->method('withStatus')->with(404)->willReturn($finalResponse); + $stream = $this->createMock(StreamInterface::class); - $stream->expects($this->once())->method('rewind'); $stream->expects($this->once())->method('write')->with($this->equalTo('Not Found')); - $response->method('getBody')->will($this->returnValue($stream)); - $response->expects($this->once())->method('withBody')->with($this->equalTo($stream))->will($this->returnSelf()); - $response->expects($this->once())->method('withStatus')->with($this->equalTo(404), $this->equalTo('Not Found'))->will($this->returnSelf()); + $finalResponse->expects($this->once())->method('getBody')->willReturn($stream); + + return $finalResponse; } } diff --git a/tests/Router/Middleware/ErrorHandlerTest.php b/tests/Router/Middleware/ErrorHandlerTest.php deleted file mode 100644 index c4b6313..0000000 --- a/tests/Router/Middleware/ErrorHandlerTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -use Jasny\Router\Middleware\ErrorHandler; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; - -class ErrorHandlerTest extends PHPUnit_Framework_TestCase -{ - /** - * Test invoke with invalid 'next' param - */ - public function testInvokeInvalidNext() - { - $middleware = new ErrorHandler(); - list($request, $response) = $this->getRequests(); - - $this->expectException(\InvalidArgumentException::class); - - $result = $middleware($request, $response, 'not_callable'); - } - - /** - * Test that exception in 'next' callback is caught - */ - public function testInvokeCatchError() - { - $middleware = new ErrorHandler(); - list($request, $response) = $this->getRequests(); - - $this->expectCatchError($response); - - $result = $middleware($request, $response, function($request, $response) { - throw new Exception('Test exception'); - }); - - $this->assertEquals(get_class($response), get_class($result), "Middleware should return response object"); - } - - /** - * Test case when there is no error - */ - public function testInvokeNoError() - { - $middleware = new ErrorHandler(); - list($request, $response) = $this->getRequests(); - - $result = $middleware($request, $response, function($request, $response) { - $response->nextCalled = true; - - return $response; - }); - - $this->assertEquals(get_class($response), get_class($result), "Middleware should return response object"); - $this->assertTrue($result->nextCalled, "'next' was not called"); - } - - /** - * Get requests for testing - * - * @return array - */ - public function getRequests() - { - $request = $this->createMock(ServerRequestInterface::class); - $response = $this->createMock(ResponseInterface::class); - - return [$request, $response]; - } - - /** - * Expect for error - * - * @param ResponseInterface $response - */ - public function expectCatchError($response) - { - $stream = $this->createMock(StreamInterface::class); - $stream->expects($this->once())->method('rewind'); - $stream->expects($this->once())->method('write')->with($this->equalTo('Unexpected error')); - - $response->method('getBody')->will($this->returnValue($stream)); - $response->expects($this->once())->method('withBody')->with($this->equalTo($stream))->will($this->returnSelf()); - $response->expects($this->once())->method('withStatus')->with($this->equalTo(500), $this->equalTo('Internal Server Error'))->will($this->returnSelf()); - } -} diff --git a/tests/Router/Middleware/NotFoundTest.php b/tests/Router/Middleware/NotFoundTest.php index 0c40a68..b8cd657 100644 --- a/tests/Router/Middleware/NotFoundTest.php +++ b/tests/Router/Middleware/NotFoundTest.php @@ -5,261 +5,222 @@ use Jasny\Router\Middleware\NotFound; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * @covers Jasny\Router\Middleware\NotFound + */ class NotFoundTest extends PHPUnit_Framework_TestCase { - /** - * Test creating object with false parameters - * - * @dataProvider constructProvider - * @param string $notFound - * @param string $notAllowed - * @param boolean $positive - */ - public function testConstruct($notFound, $notAllowed, $positive) - { - if (!$positive) $this->expectException(\InvalidArgumentException::class); - - $middleware = new NotFound($this->getRoutes(), $notFound, $notAllowed); - - if ($positive) $this->skipTest(); - } - - /** - * Provide data for testing '__contruct' - */ - public function constructProvider() + public function invalidStatusProvider() { return [ - [null, 405, false], - [true, true, false], - [99, null, false], - [600, null, false], - [404, 99, false], - [404, 600, false], - [200, 405, true], - [404, 200, true] + [0], + [true], + ['foo bar zoo'], + [1000], + [['abc']] ]; } /** - * Test invoke with invalid 'next' param + * @dataProvider invalidStatusProvider + * @expectedException InvalidArgumentException + * + * @param string $status */ - public function testInvokeInvalidNext() - { - $middleware = new NotFound($this->getRoutes(), 404, 405); - list($request, $response) = $this->getRequests(); - - $this->expectException(\InvalidArgumentException::class); - - $result = $middleware($request, $response, 'not_callable'); - } - - /** - * Test that 'next' callback is invoked when route is found - * - * @dataProvider invokeProvider - * @param callback|int $notFound - * @param callback|int $notAllowed - * @param callback $next - */ - public function testInvokeFound($notFound, $notAllowed, $next) + public function testConstructInvalidNotFound($status) { - if (!$next) return $this->skipTest(); - - list($request, $response) = $this->getRequests(); - $routes = $this->getRoutes(); - $middleware = new NotFound($routes, $notFound, $notAllowed); - - $this->expectRoute($routes, $request, 'found'); - $this->notExpectSimpleError($response); - - $result = $middleware($request, $response, $next); - - $this->assertEquals(get_class($response), get_class($result), "Result must be an instance of 'ResponseInterface'"); - $this->assertTrue($result->nextCalled, "'next' was not called"); - $this->assertFalse(isset($result->notAllowedCalled), "'Not allowed' callback was called"); - $this->assertFalse(isset($result->notFoundCalled), "'Not found' callback was called"); + new NotFound($this->createMock(Routes::class), $status); } /** - * Test __invoke method in case of route is found with another method - * - * @dataProvider invokeProvider - * @param callback|int $notFound - * @param callback|int $notAllowed - * @param callback $next + * @expectedException InvalidArgumentException */ - public function testInvokeNotAllowed($notFound, $notAllowed, $next) + public function testConstructNotFoundNotNull() { - if (!$notAllowed) return $this->skipTest(); - - list($request, $response) = $this->getRequests(); - $routes = $this->getRoutes(); - $middleware = new NotFound($routes, $notFound, $notAllowed); - - $this->expectRoute($routes, $request, 'notAllowed'); - if (is_numeric($notAllowed)) { - $this->expectSimpleError($response, $notAllowed); - } - - $result = $middleware($request, $response, $next); - - $this->assertEquals(get_class($response), get_class($result), "Result must be an instance of 'ResponseInterface'"); - $this->assertFalse(isset($result->nextCalled), "'next' was called"); - - if (is_callable($notAllowed)) { - $this->assertTrue($result->notAllowedCalled, "'Not allowed' callback was not called"); - } + new NotFound($this->createMock(Routes::class), null); } /** - * Test __invoke method in case of route not found at all - * - * @dataProvider invokeProvider - * @param callback|int $notFound - * @param callback|int $notAllowed - * @param callback $next + * @dataProvider invalidStatusProvider + * @expectedException InvalidArgumentException + * + * @param string $status */ - public function testInvokeNotFound($notFound, $notAllowed, $next) + public function testConstructInvalidMethodNotAllowed($status) { - list($request, $response) = $this->getRequests(); - $routes = $this->getRoutes(); - $middleware = new NotFound($routes, $notFound, $notAllowed); - - $case = $notAllowed ? 'notFoundTwice' : 'notFoundOnce'; - $this->expectRoute($routes, $request, $case); - - if (is_numeric($notFound)) { - $this->expectSimpleError($response, $notFound); - } - - $result = $middleware($request, $response, $next); - - $this->assertEquals(get_class($response), get_class($result), "Result must be an instance of 'ResponseInterface'"); - $this->assertFalse(isset($result->nextCalled), "'next' was called"); - - if (is_callable($notAllowed)) { - $this->assertFalse(isset($result->notAllowedCalled), "'Not allowed' callback was called"); - } - if (is_callable($notFound)) { - $this->assertTrue($result->notFoundCalled, "'Not found' callback was not called"); - } + new NotFound($this->createMock(Routes::class), 404, $status); } /** - * Set expectations on finding route - * - * @param Routes $routes - * @param ServerRequestInterface $request - * @param string $case + * @expectedException InvalidArgumentException */ - public function expectRoute($routes, $request, $case) - { - if ($case === 'found' || $case === 'notFoundOnce') { - $found = $case === 'found'; + public function testInvokeInvalidNext() + { + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $middleware = new NotFound($this->createMock(Routes::class)); - $routes->expects($this->once())->method('hasRoute') - ->with($this->equalTo($request))->will($this->returnValue($found)); - } elseif ($case === 'notAllowed' || $case === 'notFoundTwice') { - $routes->expects($this->exactly(2))->method('hasRoute') - ->withConsecutive( - [$this->equalTo($request)], - [$this->equalTo($request), $this->equalTo(false)] - )->will($this->returnCallback(function($request, $searchMethod = true) use ($case) { - return $case === 'notFoundTwice' ? false : !$searchMethod; - })); - } + $middleware($request, $response, 'foo bar zoo'); } + /** * Provide data for testing invoke method */ public function invokeProvider() { - $callbacks = []; - foreach (['notFound', 'notAllowed', 'next'] as $type) { - $var = $type . 'Called'; - - $callbacks[$type] = function($request, $response) use ($var) { - $response->$var = true; - return $response; - }; - } - + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $mockCallback = function() { + return $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock(); + }; + return [ - [404, 405, $callbacks['next']], - [404, 405, null], - [404, null, $callbacks['next']], - [404, null, null], - [$callbacks['notFound'], $callbacks['notAllowed'], $callbacks['next']], - [$callbacks['notFound'], $callbacks['notAllowed'], null], - [$callbacks['notFound'], null, $callbacks['next']], - [$callbacks['notFound'], null, null] + [$request, $response, 404, 405, $mockCallback()], + [$request, $response, 404, null, $mockCallback()], + [$request, $response, '200', '402', $mockCallback()], + [$request, $response, $mockCallback(), $mockCallback(), $mockCallback()], + [$request, $response, $mockCallback(), null, $mockCallback()] ]; } /** - * Expect that response is set to simple deny answer - * - * @param ResponseInterface $response - * @param int $statusCode + * Test that 'next' callback is invoked when route is found + * @dataProvider invokeProvider + * + * @param ServerRequestInterface|MockObject $request + * @param ResponseInterface|MockObject $response + * @param callback|MockObject|int $notFound + * @param callback|MockObject|int $methodNotAllowed + * @param callback|MockObject $next */ - public function expectSimpleError(ResponseInterface $response, $statusCode) + public function testInvokeFound($request, $response, $notFound, $methodNotAllowed, $next) { - $stream = $this->createMock(StreamInterface::class); - $stream->expects($this->once())->method('rewind'); - $stream->expects($this->once())->method('write')->with($this->equalTo('Not Found')); + $finalResponse = $this->createMock(ResponseInterface::class); + + if ($notFound instanceof MockObject) { + $notFound->expects($this->never())->method('__invoke'); + } + + if ($methodNotAllowed instanceof MockObject) { + $methodNotAllowed->expects($this->never())->method('__invoke'); + } + + $next->expects($this->once())->method('__invoke')->with($request, $response)->willReturn($finalResponse); + + $response->expects($this->never())->method('withStatus'); + + $routes = $this->createMock(Routes::class); + $routes->expects($this->once())->method('hasRoute')->with($request)->willReturn(true); + + $middleware = new NotFound($routes, $notFound, $methodNotAllowed); + + $result = $middleware($request, $response, $next); - $response->method('getBody')->will($this->returnValue($stream)); - $response->expects($this->once())->method('withBody')->with($this->equalTo($stream))->will($this->returnSelf()); - $response->expects($this->once())->method('withStatus')->with($this->equalTo($statusCode), $this->equalTo('Not Found'))->will($this->returnSelf()); + $this->assertSame($finalResponse, $result); } /** - * Expect that there would be no simple error response - * - * @param ResponseInterface $response + * Test __invoke method in case of route is found with another method + * @dataProvider invokeProvider + * + * @param ServerRequestInterface|MockObject $request + * @param ResponseInterface|MockObject $response + * @param callback|MockObject|int $notFound + * @param callback|MockObject|int $methodNotAllowed + * @param callback|MockObject $next */ - public function notExpectSimpleError(ResponseInterface $response) + public function testInvokeNotFound($request, $response, $notFound, $methodNotAllowed, $next) { + $finalResponse = $this->createMock(ResponseInterface::class); $stream = $this->createMock(StreamInterface::class); - $stream->expects($this->never())->method('rewind'); - $stream->expects($this->never())->method('write'); - - $response->expects($this->never())->method('getBody'); - $response->expects($this->never())->method('withBody'); - $response->expects($this->never())->method('withStatus'); - } + + if ($notFound instanceof MockObject) { + $notFound->expects($this->once())->method('__invoke') + ->with($request, $response) + ->willReturn($finalResponse); + + $response->expects($this->never())->method('withStatus'); + } else { + $response->expects($this->once())->method('withStatus') + ->with($notFound) + ->willReturn($finalResponse); + + $finalResponse->expects($this->once())->method('getBody')->willReturn($stream); + $stream->expects($this->once())->method('write')->with('Not found'); + } + + if ($methodNotAllowed instanceof MockObject) { + $methodNotAllowed->expects($this->never())->method('__invoke'); + } + + $next->expects($this->never())->method('__invoke'); + + $routes = $this->createMock(Routes::class); + + $routes->expects($this->exactly(isset($methodNotAllowed) ? 2 : 1))->method('hasRoute') + ->withConsecutive([$request], [$request, false]) + ->willReturn(false); + + $middleware = new NotFound($routes, $notFound, $methodNotAllowed); - /** - * Get requests for testing - * - * @return array - */ - public function getRequests() - { - $request = $this->createMock(ServerRequestInterface::class); - $response = $this->createMock(ResponseInterface::class); + $result = $middleware($request, $response, $next); - return [$request, $response]; + $this->assertSame($finalResponse, $result); } /** - * Get routes array - * - * @return Routes + * Test __invoke method in case of route is found with another method + * @dataProvider invokeProvider + * + * @param ServerRequestInterface|MockObject $request + * @param ResponseInterface|MockObject $response + * @param callback|MockObject|int $notFound + * @param callback|MockObject|int $methodNotAllowed + * @param callback|MockObject $next */ - public function getRoutes() + public function testInvokeMethodNotAllowed($request, $response, $notFound, $methodNotAllowed, $next) { - return $this->getMockBuilder(Routes::class)->disableOriginalConstructor()->getMock(); - } + $finalResponse = $this->createMock(ResponseInterface::class); + $stream = $this->createMock(StreamInterface::class); + + $expect = $methodNotAllowed ?: $notFound; + + if ($expect !== $notFound && $notFound instanceof MockObject) { + $notFound->expects($this->never())->method('__invoke'); + } + + if ($expect instanceof MockObject) { + $expect->expects($this->once())->method('__invoke') + ->with($request, $response) + ->willReturn($finalResponse); + + $response->expects($this->never())->method('withStatus'); + } else { + $response->expects($this->once())->method('withStatus') + ->with($expect) + ->willReturn($finalResponse); + + $finalResponse->expects($this->once())->method('getBody')->willReturn($stream); + $stream->expects($this->once())->method('write')->with('Not found'); + } + + $next->expects($this->never())->method('__invoke'); + + $routes = $this->createMock(Routes::class); + + $routes->expects($this->exactly(isset($methodNotAllowed) ? 2 : 1))->method('hasRoute') + ->withConsecutive([$request], [$request, false]) + ->will($this->onConsecutiveCalls(false, true)); + + $middleware = new NotFound($routes, $notFound, $methodNotAllowed); - /** - * Skip the test pass - */ - public function skipTest() - { - return $this->assertTrue(true); + $result = $middleware($request, $response, $next); + + $this->assertSame($finalResponse, $result); } } diff --git a/tests/Router/RouteTest.php b/tests/Router/RouteTest.php new file mode 100644 index 0000000..4a72d27 --- /dev/null +++ b/tests/Router/RouteTest.php @@ -0,0 +1,42 @@ +<?php + +namespace Jasny\Router; + +use Jasny\Router\Route; + +/** + * @covers Jasny\Router\Route + */ +class RouteTest extends \PHPUnit_Framework_TestCase +{ + public function provider() + { + return [ + [['foo' => '$1', 'color' => 'red', 'number' => 42]], + [(object)['foo' => '$1', 'color' => 'red', 'number' => 42]] + ]; + } + + /** + * @dataProvider provider + * + * @param array|stdClass $values + */ + public function testConstructionWithObject($values) + { + $route = new Route($values); + + $this->assertAttributeSame('$1', 'foo', $route); + $this->assertAttributeSame('red', 'color', $route); + $this->assertAttributeSame(42, 'number', $route); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Route values should be an array, not a string + */ + public function testConstructionInvalidArgument() + { + new Route('foo'); + } +} diff --git a/tests/Router/Routes/GlobTest.php b/tests/Router/Routes/GlobTest.php index 3ed4d21..5efa77f 100644 --- a/tests/Router/Routes/GlobTest.php +++ b/tests/Router/Routes/GlobTest.php @@ -3,13 +3,17 @@ namespace Jasny\Router\Routes; use Jasny\Router\Routes\Glob; +use Jasny\Router\Route; use Psr\Http\Message\ServerRequestInterface; use ArrayObject; use BadMethodCallException; use InvalidArgumentException; -use AppendIterator; +/** + * @covers Jasny\Router\Routes\Glob + * @covers Jasny\Router\UrlParsing + */ class GlobTest extends \PHPUnit_Framework_TestCase { /** @@ -17,63 +21,55 @@ class GlobTest extends \PHPUnit_Framework_TestCase */ public function testConstructor() { - #Test empty constructor $glob = new Glob(); $this->assertInstanceOf('ArrayObject', $glob, "Should be an instance of 'ArrayObject'"); $this->assertEquals(0, $glob->count(), "Default count is not empty"); $this->assertEquals(0, $glob->getFlags(), "Default flags are not empty"); $this->assertEquals('ArrayIterator', $glob->getIteratorClass(), "Default iterator class is not correct"); - #Actual check for public values + // Actual check for available routes $count = 0; foreach ($glob as $value) { $count++; break; } - $this->assertEquals(0, $count); + $this->assertEquals(0, $count); } public function testConstructorWithArguments() { - #Test with params $values = [ '/foo/bar' => ['controller' => 'value1'], '/foo/*' => ['fn' => 'value3'], '/foo/*/bar' => ['file' => 'value5'], ]; - $glob = new Glob($values, ArrayObject::ARRAY_AS_PROPS, AppendIterator::class); + $glob = new Glob($values, ArrayObject::ARRAY_AS_PROPS); - $this->assertEquals(count($values), $glob->count(), "Routes count is not match"); + $this->assertCount(3, $glob, "Routes count do not match"); $this->assertEquals(ArrayObject::ARRAY_AS_PROPS, $glob->getFlags(), "Flags are not correct"); - $this->assertEquals(AppendIterator::class, $glob->getIteratorClass(), "Iterator class is not correct"); - foreach ($values as $pattern => $options) { - $this->assertTrue($glob->offsetExists($pattern), "Key '$pattern' is missing"); + foreach ($glob as $pattern => $route) { + $this->assertInstanceof(Route::class, $route); + $this->assertArrayHasKey($pattern, $values); + $this->assertArraysEqual($values[$pattern], (array)$route); } + + return $glob; } - + /** - * Test ArrayObject::exchangeArray method + * @depends testConstructorWithArguments * - * @dataProvider exchangeArrayProvider + * @param Glob $original */ - public function testExchangeArray($set, $reset) + public function testConstructorTraversable(Glob $original) { - $glob = new Glob($set); - $old = $glob->exchangeArray($reset); - - $this->assertEquals(count($set), count($old), "Old routes count is not match"); - $this->assertEquals(count($reset), $glob->count(), "Routes count is not match"); - - foreach ($reset as $pattern => $options) { - $this->assertTrue($glob->offsetExists($pattern), "Key is missing"); - } - foreach ($set as $pattern => $options) { - $this->assertTrue(!empty($old[$pattern]), "Old key is missing"); - $this->assertFalse($glob->offsetExists($pattern), "Key exists, but should not"); - } + $glob = new Glob($original); + + $this->assertCount(3, $glob, "Routes count do not match"); + $this->assertEquals($original->getArrayCopy(), $glob->getArrayCopy()); } /** @@ -98,70 +94,93 @@ class GlobTest extends \PHPUnit_Framework_TestCase } /** - * Test ArrayObject::offsetSet method + * Test ArrayObject::exchangeArray method * - * @dataProvider offsetSetProvider - * @param string $pattern - * @param array $options - * @param string $exception + * @dataProvider exchangeArrayProvider */ - public function testOffsetSet($pattern, $options, $exception) + public function testExchangeArray($set, $reset) { - if ($exception) $this->expectException($exception); - - $glob = new Glob(); - $glob->offsetSet($pattern, $options); + $glob = new Glob($set); + $old = $glob->exchangeArray($reset); - if ($exception) return; + $this->assertEquals(count($set), count($old), "Old routes count do not match"); + $this->assertEquals(count($reset), $glob->count(), "Routes count do not match"); - $this->assertEquals(1, $glob->count(), "Routes count is not match"); - $this->assertTrue($glob->offsetExists($pattern), "Key is missing"); - - #Verify that value was set correctly - $value = (array)$glob->offsetGet($pattern); - $this->assertEmpty(array_diff($options, $value), "Route was not set correct"); + foreach ($reset as $pattern => $options) { + $this->assertTrue($glob->offsetExists($pattern), "Key is missing"); + } + foreach ($set as $pattern => $options) { + $this->assertTrue(!empty($old[$pattern]), "Old key is missing"); + $this->assertFalse($glob->offsetExists($pattern), "Key exists, but should not"); + } } + /** * Provide data for testOffsetSet() */ public function offsetSetProvider() { return [ - ['/foo/*', ['controller' => 'bar'], ''], - ['/foo/*', ['fn' => 'bar'], ''], - ['/foo/*', ['file' => 'bar'], ''], - ['', ['controller' => 'bar'], BadMethodCallException::class], - ['foo', 'bar', InvalidArgumentException::class], - ['', '', BadMethodCallException::class] + ['/foo/*', ['controller' => 'bar']], + ['/foo/*', ['fn' => 'bar']], + ['/foo/*', ['file' => 'bar']], + ['/foo/*', $this->getMockBuilder(Route::class)->setConstructorArgs([['controller' => 'bar']])->getMock()] ]; } + + /** + * Test ArrayObject::offsetSet method + * @dataProvider offsetSetProvider + * + * @param string $pattern + * @param array $options + */ + public function testOffsetSet($pattern, $options) + { + $glob = new Glob(); + $glob[$pattern] = $options; + $this->assertCount(1, $glob); + $this->assertTrue(isset($glob[$pattern])); + + $route = $glob[$pattern]; + $this->assertInstanceOf(Route::class, $route); + + if ($options instanceof Route) { + $this->assertSame($options, $route); + } else { + $this->assertEquals([], array_diff($options, (array)$route)); + } + } + /** - * Test ArrayObject::append method - * * @expectedException BadMethodCallException */ - public function testAppend() + public function testOffsetSetInvalidPattern() { $glob = new Glob(); - $glob->append(['controller' => 'bar']); + $glob[''] = ['controller' => 'bar']; + } + + /** + * @expectedException InvalidArgumentException + */ + public function testOffsetSetInvalidRoute() + { + $glob = new Glob(); + $glob['/foo'] = 'bar'; } /** - * Test matching of url pattern to given uri + * Test ArrayObject::append method * - * @dataProvider fnMatchProvider - * @param string $pattern - * @param string $uri - * @param boolean $positive + * @expectedException BadMethodCallException */ - public function testFnMatch($pattern, $uri, $positive) - { + public function testAppend() + { $glob = new Glob(); - - $this->assertEquals($positive, $glob->fnmatch($pattern, $uri), - "Pattern and uri should " . ($positive ? "" : "not") . " match"); + $glob->append(['controller' => 'bar']); } /** @@ -205,12 +224,28 @@ class GlobTest extends \PHPUnit_Framework_TestCase } /** - * Testing getting route and it's existense + * Test matching of url pattern to given uri + * @dataProvider fnMatchProvider * + * @param string $pattern + * @param string $uri + * @param boolean $positive + */ + public function testFnMatch($pattern, $uri, $positive) + { + $glob = new Glob(); + + $this->assertEquals($positive, $glob->fnmatch($pattern, $uri), + "Pattern and uri should " . ($positive ? "" : "not") . " match"); + } + + /** + * Testing getting route and it's existense * @dataProvider getHasRouteProvider - * @param string $uri Uri of ServerRequest - * @param string $method Query method name - * @param boolean $positive If the test should be positive or negative + * + * @param string $uri Uri of ServerRequest + * @param string $method Query method name + * @param boolean $positive If the test should be positive or negative */ public function testGetHasRoute($uri, $method, $positive) { @@ -241,7 +276,7 @@ class GlobTest extends \PHPUnit_Framework_TestCase $this->assertTrue($exist, "Route not exists"); $this->assertTrue((bool)$match, "Found no match of uri with patterns"); - $this->assertEquals($values[$match]['controller'], $route['controller'], "False route obtained"); + $this->assertEquals($values[$match]['controller'], $route->controller, "False route obtained"); } /** @@ -295,152 +330,6 @@ class GlobTest extends \PHPUnit_Framework_TestCase } /** - * Test binding simple string when getting route - */ - public function testBindVarString() - { - $uri = '/foo/bar'; - $values = [$uri => ['controller' => 'value1', 'check' => 'value1']]; - - $glob = new Glob($values); - $request = $this->getServerRequest($uri); - $route = $glob->getRoute($request); - - $this->assertEquals($route['check'], $values[$uri]['check'], "Option value is not correct"); - } - - /** - * Test binding single url part to route option - * @dataProvider bindVarSingleUrlPartProvider - * @param string $patter - * @param string $uri - * @param array $options Route options - */ - public function testBindVarSingleUrlPart($pattern, $uri, $options) - { - $values = [$pattern => $options]; - - $glob = new Glob($values); - $request = $this->getServerRequest($uri); - $route = $glob->getRoute($request); - - $this->assertTrue((bool)$route, "Route not found"); - - if (!empty($options['check'])) { - $this->assertEquals('test', $route['check'], "Single pocket did not match"); - } elseif(empty($options['check2'])) { - $this->assertEquals('test1/test2', $route['check1'], "Single compound pocket did not match"); - } else { - $this->assertEquals('test1', $route['check1'], "First of two pockets did not match"); - $this->assertEquals('test2', $route['check2'], "Second of two pockets did not match"); - } - } - - /** - * Provide uri's and corresponding patterns for testBindVarSingleUrlPart() - */ - public function bindVarSingleUrlPartProvider() - { - return [ - ['/*', '/test', ['controller' => 'value', 'check' => '$1']], - ['/foo/*/bar', '/foo/test/bar', ['controller' => 'value', 'check' => '$2']], - ['/foo/bar/*', '/foo/bar/test', ['controller' => 'value', 'check' => '$3']], - ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['controller' => 'value', 'check1' => '$3', 'check2' => '$5']], - ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['controller' => 'value', 'check1' => '~$3~/~$5~']] - ]; - } - - /** - * Test binding multyple url parts to route option - * - * @dataProvider bindVarMultipleUrlPartsProvider - * @param string $uri - * @param array $options Route options - * @param boolean $positive - * @param string $exception - */ - public function testBindVarMultipleUrlParts($uri, $options, $positive, $exception) - { - if ($exception) $this->expectException(InvalidArgumentException::class); - - $values = [$uri => $options]; - $glob = new Glob($values); - $request = $this->getServerRequest($uri); - $route = $glob->getRoute($request); - - if ($exception) return; - - $values = explode('/', trim($uri, '/')); - $this->assertTrue((bool)$route, "Route not found"); - - $positive ? - $this->assertArraysEqual($values, $route['check'], "Multyple url parts are not picked correctly") : - $this->assertEmpty($route['check'], "Multyple parts element should be empty"); - - if (!empty($options['check2'])) { - array_shift($values); - $this->assertArraysEqual($values, $route['check2'], "Secondary multyple url parts are not picked correctly"); - } - } - - /** - * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts() - */ - public function bindVarMultipleUrlPartsProvider() - { - return [ - ['/foo', ['controller' => 'value', 'check' => '$1...'], false, InvalidArgumentException::class], - ['/', ['controller' => 'value', 'check' => ['$1...']], false, ''], - ['/foo', ['controller' => 'value', 'check' => ['$1...']], true, ''], - ['/foo/bar', ['controller' => 'value', 'check' => ['$1...'], 'check2' => ['$2...']], true, ''] - ]; - } - - /** - * Test binding element of superglobal array to route option - * - * @dataProvider bindVarSuperGlobalProvider - * @param string $uri - * @param array $options - * @param string $type ('get', 'post', 'cookie') - */ - public function testBindVarSuperGlobal($uri, $options, $type) - { - $test = ['check' => 'test']; - $glob = new Glob([$uri => $options]); - $request = $this->getServerRequest($uri, 'GET', [$type => $test]); - $route = $glob->getRoute($request); - - $this->assertEquals($test['check'], $route['check'], "Did not obtaine value for superglobal '$type'"); - } - - /** - * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts() - */ - public function bindVarSuperGlobalProvider() - { - return [ - ['/foo', ['controller' => 'value', 'check' => '$_GET[check]'], 'get'], - ['/foo', ['controller' => 'value', 'check' => '$_POST[check]'], 'post'], - ['/foo', ['controller' => 'value', 'check' => '$_COOKIE[check]'], 'cookie'] - ]; - } - - /** - * Test binding element of superglobal array to route option - */ - public function testBindVarRequestHeader() - { - $uri = '/foo/bar'; - $test = 'test_header_value'; - $glob = new Glob([$uri => ['controller' => 'value', 'check' => '$HTTP_REFERER']]); - $request = $this->getServerRequest($uri, 'GET', [], $test); - $route = $glob->getRoute($request); - - $this->assertEquals($test, $route['check'], "Did not obtaine value for header"); - } - - /** * Get ServerRequestInterface object * * @param string $uri @@ -469,7 +358,7 @@ class GlobTest extends \PHPUnit_Framework_TestCase */ public function assertArraysEqual(array $array1, array $array2) { - $this->assertEquals(count($array1), count($array2)); - $this->assertEmpty(array_diff($array1, $array2)); + $this->assertEmpty(array_diff($array2, $array1), 'Missing items'); + $this->assertEmpty(array_diff($array1, $array2), 'Additional items'); } } diff --git a/tests/Router/Routes/RouteBindingTest.php b/tests/Router/Routes/RouteBindingTest.php new file mode 100644 index 0000000..96536ac --- /dev/null +++ b/tests/Router/Routes/RouteBindingTest.php @@ -0,0 +1,263 @@ +<?php + +namespace Jasny\Router\Routes; + +use Jasny\Router\Routes\Glob; +use Jasny\Router\Route; +use Psr\Http\Message\ServerRequestInterface; + +use InvalidArgumentException; + +/** + * @covers Jasny\Router\Routes\RouteBinding + */ +class RouteBindingTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test binding simple string when getting route + */ + public function testBindVarString() + { + $uri = '/foo/bar'; + $values = [$uri => ['controller' => 'value1', 'check' => 'value1']]; + + $glob = new Glob($values); + $request = $this->getServerRequest($uri); + $route = $glob->getRoute($request); + + $this->assertEquals($values[$uri]['check'], $route->check); + } + + /** + * Provide uri's and corresponding patterns for testBindVarSingleUrlPart() + */ + public function bindVarSingleUrlPartProvider() + { + return [ + ['/*', '/test', ['check' => '$1'], 'test'], + ['/', '/', ['check' => '$1|test'], 'test'], + ['/foo/*/bar', '/foo/test/bar', ['check' => '$2'], 'test'], + ['/foo/bar/*', '/foo/bar/test', ['check' => '$3'], 'test'], + ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['check' => '$3', 'checkB' => '$5'], 'test1', 'test2'], + ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['check' => '~$3~/~$5~'], 'test1/test2'], + ['/', '/', ['check' => '$foo'], null], + ['/', '/', ['check' => 'test', 'checkB' => null], 'test', null] + ]; + } + + /** + * Test binding single url part to route option + * @dataProvider bindVarSingleUrlPartProvider + * + * @param string $pattern + * @param string $uri + * @param array $options Route options + * @param string $check Expected value for `check` + * @param string $checkB Expected value for `checkB` + */ + public function testBindVarSingleUrlPart($pattern, $uri, $options, $check, $checkB = null) + { + $values = [$pattern => $options]; + + $glob = new Glob($values); + $request = $this->getServerRequest($uri); + $route = $glob->getRoute($request); + + $this->assertNotNull($route, "Route not found"); + $this->assertInstanceOf(Route::class, $route); + + $this->assertEquals($check, $route->check); + + if (isset($checkB)) { + $this->assertEquals($checkB, $route->checkB); + } else { + $this->assertObjectNotHasAttribute('checkB', $route); + } + } + + public function testBindVarWithObject() + { + $object = new \Exception(); // Could be anything, just not stdClass + $glob = new Glob(['/' => ['object' => $object]]); + + $request = $this->getServerRequest('/'); + $route = $glob->getRoute($request); + + $this->assertNotNull($route, "Route not found"); + $this->assertInstanceOf(Route::class, $route); + + $this->assertSame($object, $route->object); + } + + public function bindVarWithSubProvider() + { + return [ + [['group' => ['check' => '$1']], 'array'], + [['group' => (object)['check' => '$1']], 'object'], + [['group' => ['sub' => (object)['check' => '$1']]], 'array', 'object'], + [['group' => (object)['sub' => ['check' => '$1']]], 'object', 'array'] + ]; + } + + /** + * @dataProvider bindVarWithSubProvider + * + * @param array $options + * @param string $type + * @param string $subtype + */ + public function testBindVarWithSub(array $options, $type, $subtype = null) + { + $glob = new Glob(['/*' => $options]); + + $request = $this->getServerRequest('/test'); + $route = $glob->getRoute($request); + + $this->assertNotNull($route, "Route not found"); + $this->assertInstanceOf(Route::class, $route); + + $this->assertInternalType($type, $route->group); + + $group = (array)$route->group; + + if (isset($subtype)) { + $this->assertArrayHasKey('sub', $group); + $this->assertInternalType($subtype, $group['sub']); + + $group = (array)$group['sub']; + } + + $this->assertEquals($group, ['check' => 'test']); + } + + + /** + * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts() + */ + public function bindVarMultipleUrlPartsProvider() + { + return [ + ['/foo', ['check' => '$1...'], false, InvalidArgumentException::class], + ['/', ['check' => ['$1...']], false], + ['/foo', ['check' => ['$1...']], true], + ['/foo/bar', ['check' => ['$1...'], 'checkB' => ['$2...']], + InvalidArgumentException::class] + ]; + } + + /** + * Test binding multyple url parts to route option + * @dataProvider bindVarMultipleUrlPartsProvider + * + * @param string $uri + * @param array $options Route options + * @param boolean $positive + * @param string $exception + */ + public function testBindVarMultipleUrlParts($uri, $options, $positive, $exception = false) + { + if ($exception) { + $this->expectException($exception); + } + + $glob = new Glob([$uri => $options]); + $request = $this->getServerRequest($uri); + $route = $glob->getRoute($request); + + if ($exception) return; + + $this->assertNotNull($route, "Route not found"); + $this->assertInstanceOf(Route::class, $route); + + $values = explode('/', trim($uri, '/')); + + $positive ? + $this->assertArraysEqual($values, $route->check, "Multyple url parts are not picked correctly") : + $this->assertEmpty($route->check, "Multyple parts element should be empty"); + + if (!empty($options->checkB)) { + array_shift($values); + $this->assertArraysEqual($values, $route->checkB, "Secondary multyple url parts are not picked correctly"); + } + } + + /** + * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts() + */ + public function bindVarSuperGlobalProvider() + { + return [ + ['/foo', ['check' => '$_GET[check]'], 'get'], + ['/foo', ['check' => '$_POST[check]'], 'post'], + ['/foo', ['check' => '$_COOKIE[check]'], 'cookie'] + ]; + } + + /** + * Test binding element of superglobal array to route option + * @dataProvider bindVarSuperGlobalProvider + * + * @param string $uri + * @param array $options + * @param string $type ('get', 'post', 'cookie') + */ + public function testBindVarSuperGlobal($uri, $options, $type) + { + $test = ['check' => 'test']; + $glob = new Glob([$uri => $options]); + $request = $this->getServerRequest($uri, 'GET', [$type => $test]); + $route = $glob->getRoute($request); + + $this->assertEquals($test['check'], $route->check, "Did not obtaine value for superglobal '$type'"); + } + + /** + * Test binding element of superglobal array to route option + */ + public function testBindVarRequestHeader() + { + $uri = '/foo/bar'; + $test = 'test_header_value'; + + $glob = new Glob([$uri => ['check' => '$HTTP_REFERER']]); + $request = $this->getServerRequest($uri, 'GET', [], $test); + + $route = $glob->getRoute($request); + $this->assertNotNull($route, "Route not found"); + + $this->assertEquals($test, $route->check); + } + + /** + * Get ServerRequestInterface object + * + * @param string $uri + * @param string $method Http query method + * @return ServerRequestInterface + */ + public function getServerRequest($uri, $method = 'GET', $globals = [], $header = '') + { + $request = $this->createMock(ServerRequestInterface::class); + $request->method('getUri')->willReturn($uri); + $request->method('getMethod')->willReturn($method); + $request->method('getQueryParams')->willReturn(isset($globals['get']) ? $globals['get'] : []); + $request->method('getParsedBody')->willReturn(isset($globals['post']) ? $globals['post'] : []); + $request->method('getCookieParams')->willReturn(isset($globals['cookie']) ? $globals['cookie'] : []); + $request->method('getHeaderLine')->willReturn($header); + + return $request; + } + + /** + * Assert that two 1-dimensional arrays are equal. + * Use if array elements are scalar values, or objects with __toString() method + * + * @param array $array1 + * @param array $array2 + */ + public function assertArraysEqual(array $array1, array $array2) + { + $this->assertEmpty(array_diff($array2, $array1), 'Missing items'); + $this->assertEmpty(array_diff($array1, $array2), 'Additional items'); + } +} diff --git a/tests/Router/Runner/CallbackTest.php b/tests/Router/Runner/CallbackTest.php index 8a31794..39c2798 100644 --- a/tests/Router/Runner/CallbackTest.php +++ b/tests/Router/Runner/CallbackTest.php @@ -1,64 +1,73 @@ <?php +namespace Jasny\Router; + use Jasny\Router\Route; use Jasny\Router\Runner\Callback; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -class CallbackTest extends PHPUnit_Framework_TestCase +use Jasny\Router\TestHelpers; + +/** + * @covers Jasny\Router\Runner\Callback + */ +class CallbackTest extends \PHPUnit_Framework_TestCase { + use TestHelpers; + /** * Test creating Callback runner - * - * @dataProvider callbackProvider - * @param Route $route - * @param boolean $positive */ - public function testCallback($route, $positive) + public function testCallback() { - $runner = new Callback($route); - $request = $this->createMock(ServerRequestInterface::class); $response = $this->createMock(ResponseInterface::class); - $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route)); - - if (!$positive) $this->expectException(\RuntimeException::class); - $result = $runner->run($request, $response); - - if (!$positive) return; - - $this->assertEquals($request, $result['request'], "Request object was not passed correctly to result"); - $this->assertEquals($response, $result['response'], "Response object was not passed correctly to result"); + $finalResponse = $this->createMock(ResponseInterface::class); + + $route = $this->createMock(Route::class); + $route->fn = $this->createCallbackMock($this->once(), [$request, $response], $finalResponse); + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = new Callback($route); + $result = $runner($request, $response); + + $this->assertSame($finalResponse, $result); } - - + /** - * Provide data fpr testing 'create' method + * @expectedException RuntimeException + * @expectedExceptionMessage 'fn' property of route shoud be a callable */ - public function callbackProvider() + public function testNoCallback() { - $callback = function($request, $response) { - return ['request' => $request, 'response' => $response]; - }; - - return [ - [Route::create(['fn' => $callback, 'value' => 'test']), true], - [Route::create(['fn' => [$this, 'getCallback'], 'value' => 'test']), true], - [Route::create(['controller' => 'TestController', 'value' => 'test']), false], - [Route::create(['file' => 'some_file.php', 'value' => 'test']), false], - [Route::create(['test' => 'test']), false], - ]; + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $route = $this->createMock(Route::class); + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = new Callback($route); + $runner($request, $response); } - + /** - * Testable callback for creating Route - * - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * @return array + * @expectedException RuntimeException + * @expectedExceptionMessage 'fn' property of route shoud be a callable */ - public function getCallback($request, $response) + public function testInvalidCallback() { - return ['request' => $request, 'response' => $response]; + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $route = $this->createMock(Route::class); + $route->fn = 'foo bar zoo'; + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = new Callback($route); + $runner($request, $response); } } diff --git a/tests/Router/Runner/ControllerTest.php b/tests/Router/Runner/ControllerTest.php index 433d36d..694fe5b 100644 --- a/tests/Router/Runner/ControllerTest.php +++ b/tests/Router/Runner/ControllerTest.php @@ -1,140 +1,80 @@ <?php +namespace Jasny\Router; + use Jasny\Router\Route; -use Jasny\Router\Runner\Controller; +use Jasny\Router\Runner; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -class ControllerTest extends PHPUnit_Framework_TestCase -{ - /** - * Tmp scripts - * @var array - **/ - public static $files = []; - - /** - * Test creating Controller runner - * - * @dataProvider phpScriptProvider - * @param Route $route - * @param boolean $positive - */ - public function testPhpScript($route, $positive) - { - $runner = new Controller($route); +use Jasny\Router\TestHelpers; +/** + * @covers Jasny\Router\Runner\Controller; + */ +class ControllerTest extends \PHPUnit_Framework_TestCase +{ + use TestHelpers; + + public function testInvoke() + { $request = $this->createMock(ServerRequestInterface::class); $response = $this->createMock(ResponseInterface::class); - $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route)); - - if (!$positive) $this->expectException(\RuntimeException::class); - $result = $runner->run($request, $response); - - $this->assertEquals($request, $result['request'], "Request object was not passed correctly to result"); - $this->assertEquals($response, $result['response'], "Response object was not passed correctly to result"); + $finalResponse = $this->createMock(ResponseInterface::class); + + $controller = $this->createCallbackMock($this->once(), [$request, $response], $finalResponse); + $class = get_class($controller); + + $route = $this->createMock(Route::class); + $route->controller = $class; + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = $this->getMockBuilder(Runner\Controller::class)->setMethods(['instantiate'])->getMock(); + $runner->expects($this->once())->method('instantiate')->with($class)->willReturn($controller); + + $result = $runner($request, $response); + + $this->assertSame($finalResponse, $result); } - - /** - * Provide data for testing 'create' method - */ - public function phpScriptProvider() - { - foreach (['noInvoke', 'withInvoke'] as $type) { - list($class, $path) = static::createTmpScript($type); - static::$files[$type] = compact('class', 'path'); - } - - return [ - [Route::create(['test' => 'test']), false], - [Route::create(['fn' => 'testFunction', 'value' => 'test']), false], - [Route::create(['controller' => 'TestController', 'value' => 'test']), false], - [Route::create(['controller' => '', 'value' => 'test']), false], - [Route::create(['controller' => static::$files['noInvoke']['class'], 'path' => static::$files['noInvoke']['path']]), false], - [Route::create(['controller' => static::$files['withInvoke']['class'], 'path' => static::$files['withInvoke']['path']]), true], - ]; - } - - /** - * Delete tmp test scripts - */ - public static function tearDownAfterClass() - { - foreach (static::$files as $path) { - unlink($path['path']); - } - } - + /** - * Create single tmp script file for testing - * - * @param string $type ('returnTrue', 'returnNotTrue') - * @return string $path + * @expectedException RuntimeException + * @expectedExceptionMessage Can not route to controller 'FooBarZoo': class not exists */ - public static function createTmpScript($type) - { - $dir = rtrim(sys_get_temp_dir(), '/'); - - do { - $name = static::getRandomString() . '-test-script.php'; - $path = $dir . '/' . $name; - - if (!file_exists($path)) break; - } while (true); - - if ($type === 'noInvoke') { - $class = 'RunnerTestConrtollerInvalid'; - $content = -<<<CONTENT -<?php - -class $class { - public \$route = null; - - public function __construct(\$route) + public function testInvalidClass() { - \$this->route = \$route; - } -} -CONTENT; - } else { - $class = 'RunnerTestConrtoller'; - $content = -<<<CONTENT -<?php - -class $class { - public \$route = null; - - public function __construct(\$route) - { - \$this->route = \$route; + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $route = $this->createMock(Route::class); + $route->controller = 'foo-bar-zoo'; + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = $this->getMockBuilder(Runner\Controller::class)->setMethods(['instantiate'])->getMock(); + $runner->expects($this->never())->method('instantiate'); + + $runner($request, $response); } - public function __invoke(Psr\Http\Message\ServerRequestInterface \$request, Psr\Http\Message\ResponseInterface \$response) - { - return ['request' => \$request, 'response' => \$response]; - } -} -CONTENT; - } - - $bytes = file_put_contents($path, $content); - static::assertTrue((int)$bytes > 0); - - require_once $path; - - return [$class, $path]; - } - /** - * Get random string of given length (no more then length of md5 hash) - * - * @param int $length - * @return string + * @expectedException RuntimeException + * @expectedExceptionMessage Can not route to controller 'StdClass': class does not have '__invoke' method */ - public static function getRandomString($length = 10) - { - return substr(md5(microtime(true) * mt_rand()), 0, $length); + public function testInvokeNotCallable() + { + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $route = $this->createMock(Route::class); + $route->controller = 'std-class'; + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = $this->getMockBuilder(Runner\Controller::class)->setMethods(['instantiate'])->getMock(); + $runner->expects($this->never())->method('instantiate'); + + $runner($request, $response); } } diff --git a/tests/Router/Runner/PhpScriptTest.php b/tests/Router/Runner/PhpScriptTest.php index 2f31f73..658998b 100644 --- a/tests/Router/Runner/PhpScriptTest.php +++ b/tests/Router/Runner/PhpScriptTest.php @@ -1,90 +1,128 @@ <?php +namespace Jasny\Router; + use Jasny\Router\Route; -use Jasny\Router\Runner\PhpScript; +use Jasny\Router\Runner; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -class PhpScriptTest extends PHPUnit_Framework_TestCase +use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamDirectory; + +/** + * @covers Jasny\Router\Runner\PhpScript + */ +class PhpScriptTest extends \PHPUnit_Framework_TestCase { /** - * Test creating PhpScript runner - * - * @dataProvider phpScriptProvider - * @param Route $route - * @param boolean $positive + * @var vfsStreamDirectory */ - public function testPhpScript($route, $positive) - { - $runner = new PhpScript($route); + protected $root; + + public function setUp() + { + $this->root = vfsStream::setup('root'); + $this->root->addChild(vfsStream::newFile('true.php')->setContent('<?php ?>')); + $this->root->addChild(vfsStream::newFile('foo.php')->setContent('<?php return "foo"; ?>')); + } + public function testInvoke() + { $request = $this->createMock(ServerRequestInterface::class); $response = $this->createMock(ResponseInterface::class); - $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route)); - - if (!$positive) $this->expectException(\RuntimeException::class); - $result = $runner->run($request, $response); + $finalResponse = $this->createMock(ResponseInterface::class); - if ($route->type === 'returnTrue') { - $this->assertEquals($response, $result, "Request object was not returned as result"); - } else { - $this->assertEquals($request, $result['request'], "Request object was not passed correctly to result"); - $this->assertEquals($response, $result['response'], "Response object was not passed correctly to result"); - } + $route = $this->createMock(Route::class); + $route->file = vfsStream::url('root/foo.php'); + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = $this->getMockBuilder(Runner\PhpScript::class)->setMethods(['includeScript'])->getMock(); + $runner->expects($this->once())->method('includeScript') + ->with(vfsStream::url('root/foo.php'), $request, $response) + ->willReturn($finalResponse); - unlink($route->file); + $result = $runner($request, $response); + + $this->assertSame($finalResponse, $result); } - - /** - * Provide data fpr testing 'create' method - */ + public function phpScriptProvider() { + $routeTrue = $this->createMock(Route::class); + $routeTrue->file = vfsStream::url('root/true.php'); + + $routeFoo = $this->createMock(Route::class); + $routeFoo->file = vfsStream::url('root/foo.php'); + return [ - [Route::create(['test' => 'test']), false], - [Route::create(['fn' => 'testFunction', 'value' => 'test']), false], - [Route::create(['controller' => 'TestController', 'value' => 'test']), false], - [Route::create(['file' => '', 'value' => 'test']), false], - [Route::create(['file' => 'some_file.php', 'value' => 'test']), false], - [Route::create(['file' => '../' . basename(getcwd()), 'value' => 'test']), false], - [Route::create(['file' => $this->createTmpScript('returnTrue'), 'type' => 'returnTrue']), true], - [Route::create(['file' => $this->createTmpScript('returnNotTrue'), 'type' => 'returnNotTrue']), true] + [$routeTrue, 1], + [$routeFoo, 'foo'] ]; } - + /** - * Create single tmp script file for testing - * - * @param string $type ('returnTrue', 'returnNotTrue') - * @return string $path + * @dataProvider phpScriptProvider + * + * @param Route $route + * @param mixed $expected */ - public function createTmpScript($type) + public function testInvokeIncludeScript($route, $expected) { - $dir = rtrim(sys_get_temp_dir(), '/'); - - do { - $name = $this->getRandomString() . '-test-script.php'; - $path = $dir . '/' . $name; + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route)); - if (!file_exists($path)) break; - } while (true); + $runner = new Runner\PhpScript($route); + + if ($expected === 1) { + $expected = $response; + } + + $result = $runner->run($request, $response); + + $this->assertSame($expected, $result); + } - $content = $type === 'returnTrue' ? "<?php\n return true;" : "<?php\n return ['request' => \$request, 'response' => \$response];"; - $bytes = file_put_contents($path, $content); + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Failed to route using 'vfs://root/bar.php': File doesn't exist + */ + public function testInvokeWithNonExistingFile() + { + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); - $this->assertTrue((int)$bytes > 0); + $route = $this->createMock(Route::class); + $route->file = vfsStream::url('root/bar.php'); + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = $this->getMockBuilder(Runner\PhpScript::class)->setMethods(['includeScript'])->getMock(); + $runner->expects($this->never())->method('includeScript'); - return $path; + $runner($request, $response); } /** - * Get random string of given length (no more then length of md5 hash) - * - * @param int $length - * @return string + * @expectedException \RuntimeException + * @expectedExceptionMessage Won't route to 'vfs://root/../bar.php': '~', '..' are not allowed in filename */ - public function getRandomString($length = 10) - { - return substr(md5(microtime(true) * mt_rand()), 0, $length); + public function testInvokeWithIlligalFilename() + { + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $route = $this->createMock(Route::class); + $route->file = vfsStream::url('root/../bar.php'); + + $request->expects($this->once())->method('getAttribute')->with('route')->willReturn($route); + + $runner = $this->getMockBuilder(Runner\PhpScript::class)->setMethods(['includeScript'])->getMock(); + $runner->expects($this->never())->method('includeScript'); + + $runner($request, $response); } + } diff --git a/tests/Router/Runner/RunnerFactoryTest.php b/tests/Router/Runner/RunnerFactoryTest.php deleted file mode 100644 index c18656d..0000000 --- a/tests/Router/Runner/RunnerFactoryTest.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -use Jasny\Router\Route; -use Jasny\Router\Runner\RunnerFactory; -use Jasny\Router\Runner\Controller; -use Jasny\Router\Runner\Callback; -use Jasny\Router\Runner\PhpScript; - -class RunnerFactoryTest extends PHPUnit_Framework_TestCase -{ - /** - * Test creating Runner object using factory - * - * @dataProvider createProvider - * @param Route $route - * @param string $class Runner class to use - * @param boolean $positive - */ - public function testCreate($route, $class, $positive) - { - if (!$positive) $this->expectException(\InvalidArgumentException::class); - - $factory = new RunnerFactory(); - $runner = $factory($route); - - $this->assertInstanceOf($class, $runner, "Runner object has invalid class"); - } - - /** - * Provide data fpr testing 'create' method - */ - public function createProvider() - { - return [ - [Route::create(['controller' => 'TestController', 'value' => 'test']), Controller::class, true], - [Route::create(['fn' => 'testFunction', 'value' => 'test']), Callback::class, true], - [Route::create(['file' => 'some_file.php', 'value' => 'test']), PhpScript::class, true], - [Route::create(['test' => 'test']), '', false], - ]; - } -} diff --git a/tests/Router/RunnerFactoryTest.php b/tests/Router/RunnerFactoryTest.php new file mode 100644 index 0000000..a9fbe92 --- /dev/null +++ b/tests/Router/RunnerFactoryTest.php @@ -0,0 +1,58 @@ +<?php + +namespace Jasny\Router; + +use Jasny\Router\RunnerFactory; +use Jasny\Router\Route; +use Jasny\Router\Runner; + +class RunnerFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * Provide data fpr testing 'create' method + */ + public function createProvider() + { + $routeController = $this->createMock(Route::class); + $routeController->controller = 'foo-bar'; + + $routeCallback = $this->createMock(Route::class); + $routeCallback->fn = function() {}; + + $routePhpScript = $this->createMock(Route::class); + $routePhpScript->file = 'some_file.php'; + + return [ + [$routeController, Runner\Controller::class], + [$routeCallback, Runner\Callback::class], + [$routePhpScript, Runner\PhpScript::class], + ]; + } + + /** + * Test creating Runner object using factory + * @dataProvider createProvider + * + * @param Route $route + * @param string $class Runner class to use + */ + public function testCreate($route, $class) + { + $factory = new RunnerFactory(); + $runner = $factory($route); + + $this->assertInstanceOf($class, $runner, "Runner object has invalid class"); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Route has neither 'controller', 'fn' or 'file' defined + */ + public function testCreatWithInvalideRoute() + { + $route = $this->createMock(Route::class); + + $factory = new RunnerFactory(); + $factory($route); + } +} diff --git a/tests/Router/RunnerTest.php b/tests/Router/RunnerTest.php index 7db4c02..3e558b4 100644 --- a/tests/Router/RunnerTest.php +++ b/tests/Router/RunnerTest.php @@ -1,46 +1,39 @@ <?php -use Jasny\Router\Route; use Jasny\Router\Runner; -use Jasny\Router\Runner\Controller; -use Jasny\Router\Runner\Callback; -use Jasny\Router\Runner\PhpScript; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; +use Jasny\Router\TestHelpers; + +/** + * @covers Jasny\Router\Runner + */ class RunnerTest extends PHPUnit_Framework_TestCase { + use TestHelpers; + /** * Test runner __invoke method */ public function testInvoke() { - $runner = $this->getMockBuilder(Runner::class)->disableOriginalConstructor()->getMockForAbstractClass(); - $queries = [ - 'request' => $this->createMock(ServerRequestInterface::class), - 'response' => $this->createMock(ResponseInterface::class) - ]; - - #Test that 'run' receives correct arguments inside '__invoke' - $runner->method('run')->will($this->returnCallback(function($arg1, $arg2) { - return ['request' => $arg1, 'response' => $arg2]; - })); - - $result = $runner($queries['request'], $queries['response']); - $this->assertEquals($result['request'], $queries['request'], "Request was not returned correctly from 'run'"); - $this->assertEquals($result['response'], $queries['response'], "Response was not returned correctly from 'run'"); - - #The same test with calling 'next' callback - $result = $runner($queries['request'], $queries['response'], function($request, $prevResponse) use ($queries) { - $this->assertEquals($request, $queries['request'], "Request is not correct in 'next'"); - $this->assertEquals($prevResponse['request'], $queries['request'], "Prev response was not passed correctly to 'next'"); - $this->assertEquals($prevResponse['response'], $queries['response'], "Prev response was not passed correctly to 'next'"); - - return $queries + ['next_called' => true]; - }); + $runner = $this->getMockBuilder(Runner::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $request = $this->createMock(ServerRequestInterface::class); + + $response = $this->createMock(ResponseInterface::class); + $runResponse = $this->createMock(ResponseInterface::class); + $finalResponse = $this->createMock(ResponseInterface::class); - $this->assertTrue($result['next_called'], "'Next' callback was not called"); - $this->assertEquals($result['request'], $queries['request'], "Request was not returned correctly from 'run' with 'next'"); - $this->assertEquals($result['response'], $queries['response'], "Request was not returned correctly from 'run' with 'next'"); + $runner->expects($this->once())->method('run') + ->with($request, $response) + ->willReturn($runResponse); + + $next = $this->createCallbackMock($this->once(), [$request, $runResponse], $finalResponse); + + $runner($request, $response, $next); } } |