-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unit tests for \OnMoon\OpenApiServerBundle\Controller\ApiController #158
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,331 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OnMoon\OpenApiServerBundle\Test\Unit\CodeGenerator; | ||
|
||
use cebe\openapi\spec\OpenApi; | ||
use Exception; | ||
use OnMoon\OpenApiServerBundle\Controller\ApiController; | ||
use OnMoon\OpenApiServerBundle\Interfaces\ApiLoader; | ||
use OnMoon\OpenApiServerBundle\Interfaces\RequestHandler; | ||
use OnMoon\OpenApiServerBundle\Interfaces\SetClientIp; | ||
use OnMoon\OpenApiServerBundle\Interfaces\SetRequest; | ||
use OnMoon\OpenApiServerBundle\Serializer\DtoSerializer; | ||
use OnMoon\OpenApiServerBundle\Specification\Definitions\Operation; | ||
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; | ||
use Symfony\Component\Routing\RouteCollection; | ||
use Symfony\Component\Routing\RouterInterface; | ||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; | ||
use Throwable; | ||
|
||
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 | ||
{ | ||
$this->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'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be tested without exceptions, by creating a getter on this class and asserting that it returns the expected ip address after the controller is executed. |
||
} | ||
}; | ||
} | ||
|
||
/** | ||
* @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('"TestClass" has 0 public methods, exactly one expected'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe an anonymous class can be used here and get_class to assert it's name? |
||
|
||
$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 TestClass2 is not a named type'); | ||
|
||
$request = new Request( | ||
[], | ||
[], | ||
['_route_params' => '', '_route' => 'test'], | ||
); | ||
$apiController->handle($request); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
// phpcs:disable | ||
class TestClass | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
// phpcs:disable | ||
class TestClass2 | ||
{ | ||
/** @phpstan-ignore-next-line */ | ||
public function test($test): void | ||
{ | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be tested without exceptions, by creating a getter on this class and asserting that it returns the expected response object after the controller is executed.