diff --git a/.github/workflows/grumphp.yaml b/.github/workflows/grumphp.yaml index d2d5da9..45f4bc3 100644 --- a/.github/workflows/grumphp.yaml +++ b/.github/workflows/grumphp.yaml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.0', '8.1'] + php-versions: ['8.0', '8.1', '8.2'] composer-options: ['', '--prefer-lowest'] fail-fast: false name: PHP ${{ matrix.php-versions }} @ ${{ matrix.operating-system }} with ${{ matrix.composer-options }} @@ -35,12 +35,8 @@ jobs: key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-composer- - name: Install dependencies PHP - run: composer update --prefer-dist --no-progress --no-suggest ${{ matrix.composer-options }} --ignore-platform-reqs - - name: Set git variables - run: | - git config --global user.email "you@example.com" - git config --global user.name "Your Name" + run: composer update --prefer-dist --no-progress --ignore-platform-req=php+ ${{ matrix.composer-options }} - name: Run the tests run: php vendor/bin/grumphp run --no-interaction env: - PHP_CS_FIXER_IGNORE_ENV: 1 + PHP_CS_FIXER_IGNORE_ENV: 1 \ No newline at end of file diff --git a/README.md b/README.md index f0c2caa..ab2a4b5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![Travis](https://img.shields.io/travis/phpro/api-problem-bundle/master.svg)](http://travis-ci.org/phpro/api-problem-bundle) [![Installs](https://img.shields.io/packagist/dt/phpro/api-problem-bundle.svg)](https://packagist.org/packages/phpro/api-problem-bundle/stats) [![Packagist](https://img.shields.io/packagist/v/phpro/api-problem-bundle.svg)](https://packagist.org/packages/phpro/api-problem-bundle) @@ -34,16 +33,16 @@ return [ ## How it works -``` -Use Phpro\ApiProblem\Exception\ApiProblemException +```php +use Phpro\ApiProblem\Exception\ApiProblemException; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; -SomeController { - +class SomeController +{ /** * @Route('/some-route', defaults={"_format" = "json"}) */ - someAction() { + public function someAction() { throw new ApiProblemException( new HttpApiProblem('400', 'It aint all bad ...') ); diff --git a/composer.json b/composer.json index a3e63b4..3d3a97d 100644 --- a/composer.json +++ b/composer.json @@ -12,20 +12,24 @@ "require": { "php": "^8.0", "phpro/api-problem": "^1.0", - "symfony/dependency-injection": "^5.3 || ^6.0", - "symfony/event-dispatcher": "^5.3 || ^6.0", - "symfony/http-kernel": "^5.3 || ^6.0" + "symfony/dependency-injection": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/http-kernel": "^5.4 || ^6.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.3", - "matthiasnoback/symfony-dependency-injection-test": "^4.2", - "phpro/grumphp-shim": "^1.6.0", + "friendsofphp/php-cs-fixer": "^3.13", + "matthiasnoback/symfony-dependency-injection-test": "^4.3", + "phpro/grumphp-shim": "^1.14.0", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.4", - "symfony/security-core": "^5.3 || ^6.0" + "phpspec/prophecy": "1.15", + "phpunit/phpunit": "^9.5", + "symfony/security-core": "^5.4 || ^6.0" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "phpro/grumphp-shim": true + } }, "autoload": { "psr-4": { diff --git a/grumphp.yml.dist b/grumphp.yml.dist index beb9e7e..e7c9646 100644 --- a/grumphp.yml.dist +++ b/grumphp.yml.dist @@ -1,6 +1,6 @@ grumphp: tasks: - phpcsfixer2: + phpcsfixer: config: ".php-cs-fixer.dist.php" config_contains_finder: true phpunit: ~ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e6a9d21..39ae7ee 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,16 +1,21 @@ - + ./test - - - src - - - - - + + + + src + + + + + diff --git a/test/EventListener/JsonApiProblemExceptionListenerTest.php b/test/EventListener/JsonApiProblemExceptionListenerTest.php index 61e145b..b07795a 100644 --- a/test/EventListener/JsonApiProblemExceptionListenerTest.php +++ b/test/EventListener/JsonApiProblemExceptionListenerTest.php @@ -5,7 +5,9 @@ namespace PhproTest\ApiProblemBundle\EventListener; use Exception; + use function json_encode; + use Phpro\ApiProblem\ApiProblemInterface; use Phpro\ApiProblem\DebuggableApiProblemInterface; use Phpro\ApiProblem\Http\HttpApiProblem; @@ -19,28 +21,12 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Throwable; /** @covers \Phpro\ApiProblemBundle\EventListener\JsonApiProblemExceptionListener */ class JsonApiProblemExceptionListenerTest extends TestCase { use ProphecyTrait; - /** - * @var ObjectProphecy|Request - */ - private $request; - - /** - * @var ExceptionEvent - */ - private $event; - - /** - * @var Throwable - */ - private $exception; - /** * @var ExceptionTransformerInterface|ObjectProphecy */ @@ -48,15 +34,6 @@ class JsonApiProblemExceptionListenerTest extends TestCase protected function setUp(): void { - $this->request = $this->prophesize(Request::class); - $httpKernel = $this->prophesize(HttpKernelInterface::class); - $this->exception = new Exception('error'); - $this->event = new ExceptionEvent( - $httpKernel->reveal(), - $this->request->reveal(), - HttpKernelInterface::MASTER_REQUEST, - $this->exception - ); $this->exceptionTransformer = $this->prophesize(ExceptionTransformerInterface::class); $this->exceptionTransformer->accepts(Argument::any())->willReturn(false); } @@ -65,63 +42,87 @@ protected function setUp(): void public function it_does_nothing_on_non_json_requests(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('html'); - $this->request->getContentType()->willReturn('text/html'); - $listener->onKernelException($this->event); - $this->assertNull($this->event->getResponse()); + $request = new Request(); + $request->headers->set('Accept', 'text/html'); + $request->headers->set('Content-Type', 'text/html'); + + $event = $this->buildEvent($request); + $listener->onKernelException($event); + + $this->assertNull($event->getResponse()); } /** @test */ public function it_runs_on_json_route_formats(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('json'); - $this->request->getContentType()->willReturn(null); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(500, $this->parseDataForException()); + $request = new Request(); + $request->headers->set('Accept', 'application/json'); + $request->headers->remove('Content-Type'); + $exception = new Exception('error'); + $event = $this->buildEvent($request, $exception); + + $listener->onKernelException($event); + + $this->assertApiProblemWithResponseBody($event, 500, $this->parseDataForException($exception)); } /** @test */ public function it_runs_on_json_content_types(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('html'); - $this->request->getContentType()->willReturn('application/json'); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(500, $this->parseDataForException()); + $request = new Request(); + $request->headers->set('Accept', 'text/html'); + $request->headers->set('Content-Type', 'application/json'); + $exception = new Exception('error'); + $event = $this->buildEvent($request, $exception); + + $listener->onKernelException($event); + $this->assertApiProblemWithResponseBody($event, 500, $this->parseDataForException($exception)); } /** @test */ public function it_runs_on_json_accept_header(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('json'); - $this->request->getContentType()->willReturn('html'); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(500, $this->parseDataForException()); + $request = new Request(); + $request->headers->set('Accept', 'application/json'); + $request->headers->set('Content-Type', 'text/html'); + $exception = new Exception('error'); + $event = $this->buildEvent($request, $exception); + + $listener->onKernelException($event); + $this->assertApiProblemWithResponseBody($event, 500, $this->parseDataForException($exception)); } /** @test */ public function it_parses_an_api_problem_response(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('json'); - $this->request->getContentType()->willReturn('application/json'); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(500, $this->parseDataForException()); + $request = new Request(); + $request->headers->set('Accept', 'application/json'); + $request->headers->set('Content-Type', 'application/json'); + $exception = new Exception('error'); + $event = $this->buildEvent($request, $exception); + + $listener->onKernelException($event); + $this->assertApiProblemWithResponseBody($event, 500, $this->parseDataForException($exception)); } /** @test */ public function it_uses_an_exception_transformer(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('json'); - $this->request->getContentType()->willReturn('application/json'); + + $request = new Request(); + $request->headers->set('Accept', 'application/json'); + $request->headers->set('Content-Type', 'text/html'); + $event = $this->buildEvent($request); $apiProblem = $this->prophesize(ApiProblemInterface::class); $apiProblem->toArray()->willReturn([]); @@ -129,16 +130,19 @@ public function it_uses_an_exception_transformer(): void $this->exceptionTransformer->accepts(Argument::type(Exception::class))->willReturn(true); $this->exceptionTransformer->transform(Argument::type(Exception::class))->willReturn($apiProblem->reveal()); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(400, []); + $listener->onKernelException($event); + $this->assertApiProblemWithResponseBody($event, 400, []); } /** @test */ public function it_returns_the_status_code_from_the_api_problem(): void { $listener = new JsonApiProblemExceptionListener($this->exceptionTransformer->reveal(), false); - $this->request->getPreferredFormat()->willReturn('json'); - $this->request->getContentType()->willReturn('application/json'); + + $request = new Request(); + $request->headers->set('Accept', 'application/json'); + $request->headers->set('Content-Type', 'text/html'); + $event = $this->buildEvent($request); $apiProblem = $this->prophesize(ApiProblemInterface::class); $apiProblem->toArray()->willReturn(['status' => 123]); @@ -146,8 +150,8 @@ public function it_returns_the_status_code_from_the_api_problem(): void $this->exceptionTransformer->accepts(Argument::type(Exception::class))->willReturn(true); $this->exceptionTransformer->transform(Argument::type(Exception::class))->willReturn($apiProblem->reveal()); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(123, ['status' => 123]); + $listener->onKernelException($event); + $this->assertApiProblemWithResponseBody($event, 123, ['status' => 123]); } /** @test */ @@ -160,35 +164,52 @@ public function it_parses_a_debuggable_api_problem_response(): void $apiProblem->toDebuggableArray()->willReturn($data); $apiProblem->toArray()->willReturn($data); - $this->exceptionTransformer->accepts($this->exception)->willReturn(true); - $this->exceptionTransformer->transform($this->exception)->willReturn($apiProblem->reveal()); + $exception = new Exception('error'); + $this->exceptionTransformer->accepts($exception)->willReturn(true); + $this->exceptionTransformer->transform($exception)->willReturn($apiProblem->reveal()); - $this->request->getPreferredFormat()->willReturn('json'); - $this->request->getContentType()->willReturn('application/json'); + $request = new Request(); + $request->headers->set('Accept', 'application/json'); + $request->headers->set('Content-Type', 'text/html'); + $event = $this->buildEvent($request, $exception); - $listener->onKernelException($this->event); - $this->assertApiProblemWithResponseBody(500, $data); + $listener->onKernelException($event); + $this->assertApiProblemWithResponseBody($event, 500, $data); } - private function assertApiProblemWithResponseBody(int $expectedResponseCode, array $expectedData): void + private function assertApiProblemWithResponseBody(ExceptionEvent $event, int $expectedResponseCode, array $expectedData): void { - $response = $this->event->getResponse(); + $response = $event->getResponse(); $this->assertInstanceOf(JsonResponse::class, $response); $this->assertSame($expectedResponseCode, $response->getStatusCode()); $this->assertSame('application/problem+json', $response->headers->get('Content-Type')); $this->assertJsonStringEqualsJsonString( json_encode($expectedData), - $this->event->getResponse()->getContent() + $event->getResponse()->getContent() ); } - private function parseDataForException(): array + private function parseDataForException(Exception $exception): array { return [ 'status' => 500, 'type' => HttpApiProblem::TYPE_HTTP_RFC, 'title' => HttpApiProblem::getTitleForStatusCode(500), - 'detail' => $this->exception->getMessage(), + 'detail' => $exception->getMessage(), ]; } + + private function buildEvent(Request $request, Exception $exception = null): ExceptionEvent + { + $exception ??= new Exception('error'); + + $httpKernel = $this->prophesize(HttpKernelInterface::class); + + return new ExceptionEvent( + $httpKernel->reveal(), + $request, + HttpKernelInterface::MASTER_REQUEST, + $exception + ); + } }