diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 781e9da..2eb0371 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: [7.4, 8.0, 8.1, 8.2, 8.3] + php: [7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5] steps: - name: Checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff596d..fbd2433 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ See https://github.com/slimphp/Slim-Csrf/releases for a full list ## 2.x +- Added: Support for PHP 8.4 and 8.5 - Fixed: The key names are now the correct format to be sent as headers. This is a potential BC break. The key names now use a dash rather than an @@ -12,6 +13,10 @@ See https://github.com/slimphp/Slim-Csrf/releases for a full list - Changed: Increased likelihood that tokens are unique. +## 1.5.1 + +- Added: Support for PHP 8.4 and 8.5 + ## 1.5.0 - Added: Support for PHP 8.2 and 8.3 diff --git a/composer.json b/composer.json index f7fdd08..541d299 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,7 @@ "psr/http-server-middleware": "^1.0" }, "require-dev": { - "phpspec/prophecy": "^1.19", - "phpspec/prophecy-phpunit": "^2.2", - "phpunit/phpunit": "^9.6", + "phpunit/phpunit": "^9.6 || ^10.5", "squizlabs/php_codesniffer": "^3.10" }, "autoload": { @@ -37,5 +35,19 @@ "psr-4": { "Slim\\Csrf\\Tests\\": "tests" } + }, + "scripts": { + "sniffer:check": "phpcs --standard=phpcs.xml", + "sniffer:fix": "phpcbf --standard=phpcs.xml", + "test": "phpunit --do-not-cache-result --colors=always", + "check": [ + "@sniffer:check", + "@test:coverage" + ], + "test:coverage": [ + "@putenv XDEBUG_MODE=coverage", + "phpunit --do-not-cache-result --colors=always --coverage-clover build/coverage/clover.xml --coverage-html build/coverage --coverage-text" + ] } + } diff --git a/tests/GuardTest.php b/tests/GuardTest.php index 2359745..4d7d230 100644 --- a/tests/GuardTest.php +++ b/tests/GuardTest.php @@ -12,8 +12,6 @@ use ArrayIterator; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -29,7 +27,15 @@ class GuardTest extends TestCase { - use ProphecyTrait; + protected function setAccessible(ReflectionMethod $property, bool $accessible = true): void + { + // only if PHP version < 8.1 + if (PHP_VERSION_ID >= 80100) { + return; + } + $property->setAccessible($accessible); + } + /** * Helper function to mask a token using private method {@link Guard::maskToken()} @@ -44,7 +50,7 @@ class GuardTest extends TestCase private function maskToken(Guard $middleware, string $token): string { $maskTokenMethod = new ReflectionMethod($middleware, 'maskToken'); - $maskTokenMethod->setAccessible(true); + $this->setAccessible($maskTokenMethod); return $maskTokenMethod->invoke($middleware, $token); } @@ -52,11 +58,11 @@ private function maskToken(Guard $middleware, string $token): string public function testStrengthLowerThan16ThrowsException() { $storage = []; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('CSRF middleware instantiation failed. Minimum strength is 16.'); - new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 200, 15); + new Guard($responseFactory, 'test', $storage, null, 200, 15); } /** @@ -64,11 +70,11 @@ public function testStrengthLowerThan16ThrowsException() */ public function testSetStorageThrowsExceptionWhenFallingBackOnSessionThatHasNotBeenStarted() { - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Invalid CSRF storage.'); - new Guard($responseFactoryProphecy->reveal(), 'test'); + new Guard($responseFactory, 'test'); } /** @@ -77,117 +83,123 @@ public function testSetStorageThrowsExceptionWhenFallingBackOnSessionThatHasNotB public function testSetStorageSetsKeysOnSessionObjectWhenNotExist() { session_start(); - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - new Guard($responseFactoryProphecy->reveal(), 'test'); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + new Guard($responseFactory, 'test'); $this->assertArrayHasKey('test', $_SESSION); } public function testSetFailureHandler() { - $self = $this; - $storage = []; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); $called = 0; - $handler = function () use ($self, &$called) { + $handler = function () use (&$called) { $called++; - $responseProphecy = $self->prophesize(ResponseInterface::class); - return $responseProphecy->reveal(); + return $this->createMock(ResponseInterface::class); }; $mw->setFailureHandler($handler); - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy - ->getMethod() - ->willReturn('POST') - ->shouldBeCalledOnce(); + $request = $this->createMock(ServerRequestInterface::class); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('POST'); - $requestProphecy - ->withAttribute(Argument::type('string'), Argument::type('string')) - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledTimes(2); + $request + ->expects($this->exactly(2)) + ->method('withAttribute') + ->with($this->isType('string'), $this->isType('string')) + ->willReturn($request); - $requestProphecy - ->getParsedBody() - ->willReturn([]) - ->shouldBeCalledOnce(); + $request + ->expects($this->once()) + ->method('getParsedBody') + ->willReturn([]); - $requestProphecy - ->getHeader(Argument::type('string')) - ->willReturn([]) - ->shouldBeCalledTimes(2); + $request + ->expects($this->exactly(2)) + ->method('getHeader') + ->with($this->isType('string')) + ->willReturn([]); - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); + $requestHandler = $this->createMock(RequestHandlerInterface::class); - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $mw->process($request, $requestHandler); $this->assertEquals(1, $called); } public function testDefaultFailureHandler() { - $streamProphecy = $this->prophesize(StreamInterface::class); - $streamProphecy - ->write('Failed CSRF check!') - ->shouldBeCalledOnce(); - - $responseProphecy = $this->prophesize(ResponseInterface::class); - - $responseProphecy - ->getBody() - ->willReturn($streamProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseProphecy - ->withStatus(400) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseProphecy - ->withHeader('Content-Type', 'text/plain') - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseProphecy - ->withBody($streamProphecy->reveal()) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $responseFactoryProphecy - ->createResponse() - ->willReturn($responseProphecy->reveal()); + $stream = $this->createMock(StreamInterface::class); + $stream + ->expects($this->once()) + ->method('write') + ->with('Failed CSRF check!'); + + $response = $this->createMock(ResponseInterface::class); + + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn($stream); + + $response + ->expects($this->once()) + ->method('withStatus') + ->with(400) + ->willReturn($response); + + $response + ->expects($this->once()) + ->method('withHeader') + ->with('Content-Type', 'text/plain') + ->willReturn($response); + + $response + ->expects($this->once()) + ->method('withBody') + ->with($stream) + ->willReturn($response); + + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $responseFactory + ->expects($this->once()) + ->method('createResponse') + ->willReturn($response); $storage = []; - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); - - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy - ->getMethod() - ->willReturn('POST') - ->shouldBeCalledOnce(); - - $requestProphecy - ->withAttribute(Argument::type('string'), Argument::type('string')) - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledTimes(2); - - $requestProphecy - ->getParsedBody() - ->willReturn([]) - ->shouldBeCalledOnce(); - - $requestProphecy - ->getHeader(Argument::type('string')) - ->willReturn([]) - ->shouldBeCalledTimes(2); - - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); - - $response = $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); - $this->assertSame($response, $responseProphecy->reveal()); + $mw = new Guard($responseFactory, 'test', $storage); + + $request = $this->createMock(ServerRequestInterface::class); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('POST'); + + $request + ->expects($this->exactly(2)) + ->method('withAttribute') + ->with($this->isType('string'), $this->isType('string')) + ->willReturn($request); + + $request + ->expects($this->once()) + ->method('getParsedBody') + ->willReturn([]); + + $request + ->expects($this->exactly(2)) + ->method('getHeader') + ->with($this->isType('string')) + ->willReturn([]); + + $requestHandler = $this->createMock(RequestHandlerInterface::class); + + $actualResponse = $mw->process($request, $requestHandler); + $this->assertSame($actualResponse, $response); } public function testValidateToken() @@ -195,8 +207,8 @@ public function testValidateToken() $storage = [ 'test-name' => 'value' ]; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); $maskedToken = $this->maskToken($mw, 'value'); $this->assertTrue($mw->validateToken('test-name', $maskedToken)); @@ -212,8 +224,8 @@ public function testNotValidatingBadToken() $storage = [ 'test-name' => 'value' ]; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); $maskedToken = 'MY_BAD_BASE64???'; $this->assertFalse($mw->validateToken('test-name', $maskedToken), 'Token contains bad base64 characters'); @@ -227,14 +239,14 @@ public function testNotValidatingBadToken() public function testGetTokenNameAndValue() { $storage = []; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); $this->assertNull($mw->getTokenName()); $this->assertNull($mw->getTokenValue()); $loadLastKeyPairMethod = new ReflectionMethod($mw, 'loadLastKeyPair'); - $loadLastKeyPairMethod->setAccessible(true); + $this->setAccessible($loadLastKeyPairMethod); $loadLastKeyPairMethod->invoke($mw); $storage = [ @@ -246,7 +258,7 @@ public function testGetTokenNameAndValue() $this->assertEquals('test-name', $mw->getTokenName()); $unmaskTokenMethod = new ReflectionMethod($mw, 'unmaskToken'); - $unmaskTokenMethod->setAccessible(true); + $this->setAccessible($unmaskTokenMethod); $unmaskedToken = $unmaskTokenMethod->invoke($mw, $mw->getTokenValue()); $this->assertEquals('value', $unmaskedToken); } @@ -254,8 +266,8 @@ public function testGetTokenNameAndValue() public function testGetPersistentTokenMode() { $storage = []; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 200, 16, true); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage, null, 200, 16, true); $this->assertTrue($mw->getPersistentTokenMode()); } @@ -263,8 +275,8 @@ public function testGetPersistentTokenMode() public function testGetTokenNameKeyAndValue() { $storage = []; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); $this->assertEquals('test-name', $mw->getTokenNameKey()); $this->assertEquals('test-value', $mw->getTokenValueKey()); @@ -275,11 +287,11 @@ public function testRemoveTokenFromStorage() $storage = [ 'test-name' => 'value', ]; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); $removeTokenFromStorageMethod = new ReflectionMethod($mw, 'removeTokenFromStorage'); - $removeTokenFromStorageMethod->setAccessible(true); + $this->setAccessible($removeTokenFromStorageMethod); $removeTokenFromStorageMethod->invoke($mw, 'test-name'); $this->assertArrayNotHasKey('test-name', $storage); @@ -291,11 +303,11 @@ public function testEnforceStorageLimitWithArray() 'test-name' => 'value', 'test-name2' => 'value2', ]; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 1); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage, null, 1); $enforceStorageLimitMethod = new ReflectionMethod($mw, 'enforceStorageLimit'); - $enforceStorageLimitMethod->setAccessible(true); + $this->setAccessible($enforceStorageLimitMethod); $enforceStorageLimitMethod->invoke($mw); $this->assertArrayNotHasKey('test-name', $storage); @@ -308,11 +320,11 @@ public function testNotEnforceStorageLimitWithArrayWhenLimitIsZero() 'test-name' => 'value', 'test-name2' => 'value2', ]; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 0); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage, null, 0); $enforceStorageLimitMethod = new ReflectionMethod($mw, 'enforceStorageLimit'); - $enforceStorageLimitMethod->setAccessible(true); + $this->setAccessible($enforceStorageLimitMethod); $enforceStorageLimitMethod->invoke($mw); $this->assertSame($initial_storage, $storage); @@ -324,11 +336,11 @@ public function testEnforceStorageLimitWithIterator() 'test-name' => 'value', 'test-name2' => 'value', ]); - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 1); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage, null, 1); $enforceStorageLimitMethod = new ReflectionMethod($mw, 'enforceStorageLimit'); - $enforceStorageLimitMethod->setAccessible(true); + $this->setAccessible($enforceStorageLimitMethod); $enforceStorageLimitMethod->invoke($mw); $this->assertArrayNotHasKey('test-name', $storage); @@ -341,38 +353,38 @@ public function testTokenIsRemovedFromStorageWhenPersistentModeIsOff() 'test-name' => 'test-value123', ]; - $responseProphecy = $this->prophesize(ResponseInterface::class) - ->willImplement(ResponseInterface::class); - - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); - $requestHandlerProphecy - ->handle(Argument::type(ServerRequestInterface::class)) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); - - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy - ->getMethod() - ->willReturn('POST') - ->shouldBeCalledOnce(); - $requestProphecy - ->withAttribute(Argument::type('string'), Argument::type('string')) - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledTimes(2); - $requestProphecy - ->getParsedBody() + $response = $this->createMock(ResponseInterface::class); + + $requestHandler = $this->createMock(RequestHandlerInterface::class); + $requestHandler + ->expects($this->once()) + ->method('handle') + ->with($this->isInstanceOf(ServerRequestInterface::class)) + ->willReturn($response); + + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + + $mw = new Guard($responseFactory, 'test', $storage); + + $request = $this->createMock(ServerRequestInterface::class); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('POST'); + $request + ->expects($this->exactly(2)) + ->method('withAttribute') + ->with($this->isType('string'), $this->isType('string')) + ->willReturn($request); + $request + ->expects($this->once()) + ->method('getParsedBody') ->willReturn([ 'test-name' => 'test-name', 'test-value' => $this->maskToken($mw, 'test-value123'), - ]) - ->shouldBeCalledOnce(); + ]); - - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $mw->process($request, $requestHandler); self::assertArrayNotHasKey('test-name', $storage); } @@ -383,57 +395,62 @@ public function testTokenIsRemovedFromStorageWhenPersistentModeIsOffOnFailure() 'test-name2' => 'test-value234', ]; - $streamProphecy = $this->prophesize(StreamInterface::class); - $streamProphecy - ->write('Failed CSRF check!') - ->shouldBeCalledOnce(); - - $responseProphecy = $this->prophesize(ResponseInterface::class); - - $responseProphecy - ->getBody() - ->willReturn($streamProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseProphecy - ->withStatus(400) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseProphecy - ->withHeader('Content-Type', 'text/plain') - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseProphecy - ->withBody($streamProphecy->reveal()) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $responseFactoryProphecy - ->createResponse() - ->willReturn($responseProphecy->reveal()); - - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); - - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 1); + $stream = $this->createMock(StreamInterface::class); + $stream + ->expects($this->once()) + ->method('write') + ->with('Failed CSRF check!'); + + $response = $this->createMock(ResponseInterface::class); + + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn($stream); + + $response + ->expects($this->once()) + ->method('withStatus') + ->with(400) + ->willReturn($response); + + $response + ->expects($this->once()) + ->method('withHeader') + ->with('Content-Type', 'text/plain') + ->willReturn($response); + + $response + ->expects($this->once()) + ->method('withBody') + ->with($stream) + ->willReturn($response); + + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $responseFactory + ->expects($this->once()) + ->method('createResponse') + ->willReturn($response); + + $requestHandler = $this->createMock(RequestHandlerInterface::class); + + $mw = new Guard($responseFactory, 'test', $storage, null, 1); $mw->setStorage($storage); // pass $storage in by reference so we can inspect it later - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy - ->getMethod() - ->willReturn('GET') - ->shouldBeCalledOnce(); + $request = $this->createMock(ServerRequestInterface::class); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('GET'); - $requestProphecy - ->getParsedBody() + $request + ->expects($this->once()) + ->method('getParsedBody') ->willReturn([ 'test-name' => 'test-value123', - ]) - ->shouldBeCalledOnce(); + ]); - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $mw->process($request, $requestHandler); $this->assertArrayNotHasKey('test-name', $storage); } @@ -445,66 +462,66 @@ public function testTokenInBodyOfGetIsInvalid() ]; // we set up a failure handler that we expect to be called because a GET cannot have a token - $self = $this; $failureHandlerCalled = 0; - $failureHandler = function () use ($self, &$failureHandlerCalled) { + $failureHandler = function () use (&$failureHandlerCalled) { $failureHandlerCalled++; - $responseProphecy = $self->prophesize(ResponseInterface::class); - return $responseProphecy->reveal(); + return $this->createMock(ResponseInterface::class); }; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, $failureHandler); + $mw = new Guard($responseFactory, 'test', $storage, $failureHandler); - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); + $requestHandler = $this->createMock(RequestHandlerInterface::class); - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy - ->getMethod() - ->willReturn('GET') - ->shouldBeCalledOnce(); - $requestProphecy - ->getParsedBody() + $request = $this->createMock(ServerRequestInterface::class); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('GET'); + $request + ->expects($this->once()) + ->method('getParsedBody') ->willReturn([ 'test-name' => 'test-name', 'test-value' => 'test-value123', - ]) - ->shouldBeCalledOnce(); + ]); - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $mw->process($request, $requestHandler); self::assertSame(1, $failureHandlerCalled); } public function testProcessAppendsNewTokensWhenPersistentTokenModeIsOff() { $storage = []; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); - - $responseProphecy = $this->prophesize(ResponseInterface::class); - - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy->getParsedBody()->willReturn(null)->shouldBeCalledOnce(); - $requestProphecy->getHeader(Argument::type('string'))->willReturn([])->shouldBeCalledTimes(2); - $requestProphecy - ->getMethod() - ->willReturn('GET') - ->shouldBeCalledOnce(); - - $requestProphecy - ->withAttribute(Argument::type('string'), Argument::type('string')) - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledTimes(2); - - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); - - $requestHandlerProphecy - ->handle($requestProphecy) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage); + + $response = $this->createMock(ResponseInterface::class); + + $request = $this->createMock(ServerRequestInterface::class); + $request->expects($this->once())->method('getParsedBody')->willReturn(null); + $request->expects($this->exactly(2))->method('getHeader')->with($this->isType('string'))->willReturn([]); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('GET'); + + $request + ->expects($this->exactly(2)) + ->method('withAttribute') + ->with($this->isType('string'), $this->isType('string')) + ->willReturn($request); + + $requestHandler = $this->createMock(RequestHandlerInterface::class); + + $requestHandler + ->expects($this->once()) + ->method('handle') + ->with($request) + ->willReturn($response); + + $mw->process($request, $requestHandler); } public function testProcessAppendsNewTokensWhenPersistentTokenModeIsOn() @@ -512,37 +529,43 @@ public function testProcessAppendsNewTokensWhenPersistentTokenModeIsOn() $storage = [ 'test-name123' => 'test-value123', ]; - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 200, 16, true); - - $responseProphecy = $this->prophesize(ResponseInterface::class); - - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy->getParsedBody()->willReturn(null)->shouldBeCalledOnce(); - $requestProphecy->getHeader(Argument::type('string'))->willReturn([])->shouldBeCalledTimes(2); - $requestProphecy - ->getMethod() - ->willReturn('GET') - ->shouldBeCalledOnce(); - - $requestProphecy - ->withAttribute('test-name', 'test-name123') - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledOnce(); - - $requestProphecy - ->withAttribute('test-value', Argument::type('string')) - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledOnce(); - - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); - - $requestHandlerProphecy - ->handle($requestProphecy) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage, null, 200, 16, true); + + $response = $this->createMock(ResponseInterface::class); + + $request = $this->createMock(ServerRequestInterface::class); + $request->expects($this->once())->method('getParsedBody')->willReturn(null); + $request->expects($this->exactly(2))->method('getHeader')->with($this->isType('string'))->willReturn([]); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('GET'); + + $withAttributeCallCount = 0; + $request + ->expects($this->exactly(2)) + ->method('withAttribute') + ->willReturnCallback(function ($key, $value) use ($request, &$withAttributeCallCount) { + $withAttributeCallCount++; + if ($withAttributeCallCount === 1) { + $this->assertEquals('test-name', $key); + $this->assertEquals('test-name123', $value); + } elseif ($withAttributeCallCount === 2) { + $this->assertEquals('test-value', $key); + $this->assertIsString($value); + } + return $request; + }); + + $requestHandler = $this->createMock(RequestHandlerInterface::class); + $requestHandler + ->expects($this->once()) + ->method('handle') + ->with($request) + ->willReturn($response); + + $mw->process($request, $requestHandler); } public function testCanGetLastKeyPairFromIterator() @@ -551,11 +574,11 @@ public function testCanGetLastKeyPairFromIterator() 'test-key1' => 'value1', 'test-key2' => 'value2', ]); - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage, null, 1); + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + $mw = new Guard($responseFactory, 'test', $storage, null, 1); $enforceStorageLimitMethod = new ReflectionMethod($mw, 'getLastKeyPair'); - $enforceStorageLimitMethod->setAccessible(true); + $this->setAccessible($enforceStorageLimitMethod); $keyPair = $enforceStorageLimitMethod->invoke($mw); $this->assertIsArray($keyPair); @@ -564,7 +587,7 @@ public function testCanGetLastKeyPairFromIterator() $this->assertEquals('test-key2', $keyPair['test-name']); $unmaskTokenMethod = new ReflectionMethod($mw, 'unmaskToken'); - $unmaskTokenMethod->setAccessible(true); + $this->setAccessible($unmaskTokenMethod); $unmaskedToken = $unmaskTokenMethod->invoke($mw, $keyPair['test-value']); $this->assertEquals('value2', $unmaskedToken); } @@ -575,41 +598,50 @@ public function testTokenFromHeaderOnDelete() 'test-name' => 'test-value123', ]; - $responseProphecy = $this->prophesize(ResponseInterface::class) - ->willImplement(ResponseInterface::class); - - $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class); - $requestHandlerProphecy - ->handle(Argument::type(ServerRequestInterface::class)) - ->willReturn($responseProphecy->reveal()) - ->shouldBeCalledOnce(); - - $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); - - $mw = new Guard($responseFactoryProphecy->reveal(), 'test', $storage); - - $requestProphecy = $this->prophesize(ServerRequestInterface::class); - $requestProphecy - ->getMethod() - ->willReturn('DELETE') - ->shouldBeCalledOnce(); - $requestProphecy - ->withAttribute(Argument::type('string'), Argument::type('string')) - ->willReturn($requestProphecy->reveal()) - ->shouldBeCalledTimes(2); - $requestProphecy - ->getParsedBody() - ->willReturn([]) - ->shouldBeCalledOnce(); - $requestProphecy - ->getHeader('test-name') - ->willReturn(['test-name']) - ->shouldBeCalledOnce(); - $requestProphecy - ->getHeader('test-value') - ->willReturn([$this->maskToken($mw, 'test-value123')]) - ->shouldBeCalledOnce(); - - $mw->process($requestProphecy->reveal(), $requestHandlerProphecy->reveal()); + $response = $this->createMock(ResponseInterface::class); + + $requestHandler = $this->createMock(RequestHandlerInterface::class); + $requestHandler + ->expects($this->once()) + ->method('handle') + ->with($this->isInstanceOf(ServerRequestInterface::class)) + ->willReturn($response); + + $responseFactory = $this->createMock(ResponseFactoryInterface::class); + + $mw = new Guard($responseFactory, 'test', $storage); + + $request = $this->createMock(ServerRequestInterface::class); + $request + ->expects($this->once()) + ->method('getMethod') + ->willReturn('DELETE'); + $request + ->expects($this->exactly(2)) + ->method('withAttribute') + ->with($this->isType('string'), $this->isType('string')) + ->willReturn($request); + $request + ->expects($this->once()) + ->method('getParsedBody') + ->willReturn([]); + $maskedToken = $this->maskToken($mw, 'test-value123'); + $getHeaderCallCount = 0; + $request + ->expects($this->exactly(2)) + ->method('getHeader') + ->willReturnCallback(function ($header) use (&$getHeaderCallCount, $maskedToken) { + $getHeaderCallCount++; + if ($getHeaderCallCount === 1) { + $this->assertEquals('test-name', $header); + return ['test-name']; + } elseif ($getHeaderCallCount === 2) { + $this->assertEquals('test-value', $header); + return [$maskedToken]; + } + return []; + }); + + $mw->process($request, $requestHandler); } }