From 2da639971f754a0d573a695ac272cc9042a8cc04 Mon Sep 17 00:00:00 2001 From: Egor Zotov Date: Thu, 24 Dec 2020 18:48:01 +0300 Subject: [PATCH 1/3] Unit tests for \OnMoon\OpenApiServerBundle\Controller\ApiController --- test/unit/Controller/ApiControllerTest.php | 325 ++++++++++++++++++ .../test_class_without_public_methods.php | 9 + .../test_class_without_typed_param.php | 11 + 3 files changed, 345 insertions(+) create mode 100644 test/unit/Controller/ApiControllerTest.php create mode 100644 test/unit/Controller/test_class_without_public_methods.php create mode 100644 test/unit/Controller/test_class_without_typed_param.php diff --git a/test/unit/Controller/ApiControllerTest.php b/test/unit/Controller/ApiControllerTest.php new file mode 100644 index 0000000..fbfd7c3 --- /dev/null +++ b/test/unit/Controller/ApiControllerTest.php @@ -0,0 +1,325 @@ +specificationLoader = $this->createMock(SpecificationLoader::class); + $this->router = $this->createMock(RouterInterface::class); + $this->serializer = $this->createMock(DtoSerializer::class); + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $this->requestValidator = $this->createMock(RequestSchemaValidator::class); + } + + public function testHandleWithoutRouteThrowsException(): void + { + $this->router->method('getRouteCollection')->willReturn(new RouteCollection()); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + + $this->expectException(Throwable::class); + $this->expectExceptionMessage('Route not found'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } + + public function testHandleWithNullApiLoaderThrowsException(): void + { + $routeCollection = new RouteCollection(); + $routeCollection->add('test', new Route('/test', [], [], ['_openapi_operation' => 'test_option'])); + $this->router->method('getRouteCollection')->willReturn($routeCollection); + $this->specificationLoader->method('load')->willReturn(new Specification(['test_option' => new Operation('/', 'test', 'name')], new OpenApi([]))); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + + $this->expectException(Throwable::class); + $this->expectExceptionMessage('ApiLoader not found. Try re-generating code'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } + + public function testHandleWithNotImplementedRequestThrowsException(): void + { + $apiLoader = new class implements ApiLoader { + public function get(string $interfaceName): ?RequestHandler + { + return null; + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return ['operation_name' => 'test_function_string']; + } + }; + + $routeCollection = new RouteCollection(); + $routeCollection->add('test', new Route('/test', [], [], ['_openapi_operation' => 'test_option'])); + $this->router->method('getRouteCollection')->willReturn($routeCollection); + $this->specificationLoader->method('load')->willReturn(new Specification(['test_option' => new Operation('/', 'test', 'operation_name')], new OpenApi([]))); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + $apiController->setApiLoader($apiLoader); + + $this->expectException(Throwable::class); + $this->expectExceptionMessage('Api call implementation not found. Please implement "test_function_string" interface'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } + + public function testHandleWithSetRequestRequestHandler(): void + { + $apiLoader = new class implements ApiLoader { + public function get(string $interfaceName): ?RequestHandler + { + return new class implements SetRequest, RequestHandler { + public function setRequest(Request $request): void + { + throw new Exception('test request was set'); + } + }; + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return ['operation_name' => 'test_function_string']; + } + }; + + $routeCollection = new RouteCollection(); + $routeCollection->add('test', new Route('/test', [], [], ['_openapi_operation' => 'test_option'])); + $this->router->method('getRouteCollection')->willReturn($routeCollection); + $this->specificationLoader->method('load')->willReturn(new Specification(['test_option' => new Operation('/', 'test', 'operation_name')], new OpenApi([]))); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + $apiController->setApiLoader($apiLoader); + + $this->expectException(Throwable::class); + $this->expectExceptionMessage('test request was set'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } + + public function testHandleWithSetClientIpRequestHandler(): void + { + $apiLoader = new class implements ApiLoader { + public function get(string $interfaceName): ?RequestHandler + { + return new class implements SetClientIp, RequestHandler { + public function setClientIp(string $ip): void + { + throw new Exception('test ip was set'); + } + }; + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return ['operation_name' => 'test_function_string']; + } + }; + + $routeCollection = new RouteCollection(); + $routeCollection->add('test', new Route('/test', [], [], ['_openapi_operation' => 'test_option'])); + $this->router->method('getRouteCollection')->willReturn($routeCollection); + $this->specificationLoader->method('load')->willReturn(new Specification(['test_option' => new Operation('/', 'test', 'operation_name')], new OpenApi([]))); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + $apiController->setApiLoader($apiLoader); + + $this->expectException(Throwable::class); + $this->expectExceptionMessage('test ip was set'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } + + public function testHandleWithClassWithoutPublicMethodsImplementationThrowsException(): void + { + require getcwd() . '/test/unit/Controller/test_class_without_public_methods.php'; + + $apiLoader = new class implements ApiLoader { + public function get(string $interfaceName): ?RequestHandler + { + return new class implements RequestHandler { + }; + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return ['operation_name' => 'TestClass']; + } + }; + + $routeCollection = new RouteCollection(); + $routeCollection->add('test', new Route('/test', [], [], ['_openapi_operation' => 'test_option'])); + $this->router->method('getRouteCollection')->willReturn($routeCollection); + $this->specificationLoader->method('load')->willReturn(new Specification(['test_option' => new Operation('/', 'test', 'operation_name')], new OpenApi([]))); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + $apiController->setApiLoader($apiLoader); + + $this->expectException(Throwable::class); + $this->expectException(Throwable::class); + $this->expectExceptionMessage('"test_class" has 0 public methods, exactly one expected'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } + + public function testHandleWithClassWithoutTypedParamThrowsException(): void + { + require getcwd() . '/test/unit/Controller/test_class_without_typed_param.php'; + + $apiLoader = new class implements ApiLoader { + public function get(string $interfaceName): ?RequestHandler + { + return new class implements RequestHandler { + }; + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return ['operation_name' => 'TestClass2']; + } + }; + + $routeCollection = new RouteCollection(); + $routeCollection->add('test', new Route('/test', [], [], ['_openapi_operation' => 'test_option'])); + $this->router->method('getRouteCollection')->willReturn($routeCollection); + $this->specificationLoader->method('load')->willReturn(new Specification(['test_option' => new Operation('/', 'test', 'operation_name')], new OpenApi([]))); + + $apiController = new ApiController( + $this->specificationLoader, + $this->router, + $this->serializer, + $this->eventDispatcher, + $this->requestValidator + ); + $apiController->setApiLoader($apiLoader); + + $this->expectException(Throwable::class); + $this->expectException(Throwable::class); + $this->expectExceptionMessage('Input parameter for test_class2 is not a named type'); + + $request = new Request( + [], + [], + ['_route_params' => '', '_route' => 'test'], + ); + $apiController->handle($request); + } +} diff --git a/test/unit/Controller/test_class_without_public_methods.php b/test/unit/Controller/test_class_without_public_methods.php new file mode 100644 index 0000000..bfc2fb7 --- /dev/null +++ b/test/unit/Controller/test_class_without_public_methods.php @@ -0,0 +1,9 @@ + Date: Mon, 28 Dec 2020 11:16:07 +0300 Subject: [PATCH 2/3] fixes --- test/unit/Controller/ApiControllerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/Controller/ApiControllerTest.php b/test/unit/Controller/ApiControllerTest.php index fbfd7c3..f883bb7 100644 --- a/test/unit/Controller/ApiControllerTest.php +++ b/test/unit/Controller/ApiControllerTest.php @@ -267,7 +267,7 @@ public static function getSubscribedServices(): array $this->expectException(Throwable::class); $this->expectException(Throwable::class); - $this->expectExceptionMessage('"test_class" has 0 public methods, exactly one expected'); + $this->expectExceptionMessage('"TestClass" has 0 public methods, exactly one expected'); $request = new Request( [], @@ -313,7 +313,7 @@ public static function getSubscribedServices(): array $this->expectException(Throwable::class); $this->expectException(Throwable::class); - $this->expectExceptionMessage('Input parameter for test_class2 is not a named type'); + $this->expectExceptionMessage('Input parameter for TestClass2 is not a named type'); $request = new Request( [], From 5864c46ffc2f925dc6544b4589c383a91c9a12a7 Mon Sep 17 00:00:00 2001 From: Egor Zotov Date: Mon, 28 Dec 2020 11:46:57 +0300 Subject: [PATCH 3/3] fixes --- test/unit/Controller/ApiControllerTest.php | 8 +++++++- .../unit/Controller/test_class_without_public_methods.php | 1 - test/unit/Controller/test_class_without_typed_param.php | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/unit/Controller/ApiControllerTest.php b/test/unit/Controller/ApiControllerTest.php index f883bb7..094cf7f 100644 --- a/test/unit/Controller/ApiControllerTest.php +++ b/test/unit/Controller/ApiControllerTest.php @@ -16,6 +16,7 @@ use OnMoon\OpenApiServerBundle\Specification\Definitions\Specification; use OnMoon\OpenApiServerBundle\Specification\SpecificationLoader; use OnMoon\OpenApiServerBundle\Validator\RequestSchemaValidator; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; @@ -24,17 +25,22 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Throwable; -use function getcwd; +use function Safe\getcwd; /** * @covers \OnMoon\OpenApiServerBundle\CodeGenerator\AttributeGenerator */ class ApiControllerTest extends TestCase { + /** @var SpecificationLoader|MockObject */ private SpecificationLoader $specificationLoader; + /** @var RouterInterface|MockObject */ private RouterInterface $router; + /** @var DtoSerializer|MockObject */ private DtoSerializer $serializer; + /** @var EventDispatcherInterface|MockObject */ private EventDispatcherInterface $eventDispatcher; + /** @var RequestSchemaValidator|MockObject */ private RequestSchemaValidator $requestValidator; public function setUp(): void diff --git a/test/unit/Controller/test_class_without_public_methods.php b/test/unit/Controller/test_class_without_public_methods.php index bfc2fb7..d326790 100644 --- a/test/unit/Controller/test_class_without_public_methods.php +++ b/test/unit/Controller/test_class_without_public_methods.php @@ -3,7 +3,6 @@ declare(strict_types=1); // phpcs:disable - class TestClass { } diff --git a/test/unit/Controller/test_class_without_typed_param.php b/test/unit/Controller/test_class_without_typed_param.php index 1240c13..e60a4e7 100644 --- a/test/unit/Controller/test_class_without_typed_param.php +++ b/test/unit/Controller/test_class_without_typed_param.php @@ -5,6 +5,7 @@ // phpcs:disable class TestClass2 { + /** @phpstan-ignore-next-line */ public function test($test): void { }