diff --git a/src/Component/spec/Symfony/EventDispatcher/OperationEventDispatcherSpec.php b/src/Component/spec/Symfony/EventDispatcher/OperationEventDispatcherSpec.php deleted file mode 100644 index 1109b6f09..000000000 --- a/src/Component/spec/Symfony/EventDispatcher/OperationEventDispatcherSpec.php +++ /dev/null @@ -1,132 +0,0 @@ -beConstructedWith($eventDispatcher); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(OperationEventDispatcher::class); - } - - function it_dispatches_events( - EventDispatcherInterface $eventDispatcher, - \stdClass $data, - ): void { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - $show = (new Show(eventShortName: 'read'))->withResource($resource); - - $context = new Context(); - - $operationEvent = new OperationEvent($data->getWrappedObject(), [ - 'operation' => $show, - 'context' => $context, - ]); - - $eventDispatcher->dispatch($operationEvent, 'app.book.read')->shouldBeCalled(); - - $this->dispatch($data, $show, $context)->shouldHaveType(OperationEvent::class); - } - - function it_dispatches_events_for_bulk_operations( - EventDispatcherInterface $eventDispatcher, - \ArrayObject $data, - ): void { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - $bulkDelete = (new BulkDelete(eventShortName: 'delete'))->withResource($resource); - - $context = new Context(); - - $operationEvent = new OperationEvent($data->getWrappedObject(), [ - 'operation' => $bulkDelete, - 'context' => $context, - ]); - - $eventDispatcher->dispatch($operationEvent, 'app.book.bulk_delete')->shouldBeCalled(); - - $this->dispatchBulkEvent($data, $bulkDelete, $context)->shouldHaveType(OperationEvent::class); - } - - function it_dispatches_pre_events( - EventDispatcherInterface $eventDispatcher, - \stdClass $data, - ): void { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - $create = (new Create(eventShortName: 'create'))->withResource($resource); - - $context = new Context(); - - $operationEvent = new OperationEvent($data->getWrappedObject(), [ - 'operation' => $create, - 'context' => $context, - ]); - - $eventDispatcher->dispatch($operationEvent, 'app.book.pre_create')->shouldBeCalled(); - - $this->dispatchPreEvent($data, $create, $context)->shouldHaveType(OperationEvent::class); - } - - function it_dispatches_post_events( - EventDispatcherInterface $eventDispatcher, - \stdClass $data, - ): void { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - $create = (new Create(eventShortName: 'create'))->withResource($resource); - - $context = new Context(); - - $operationEvent = new OperationEvent($data->getWrappedObject(), [ - 'operation' => $create, - 'context' => $context, - ]); - - $eventDispatcher->dispatch($operationEvent, 'app.book.post_create')->shouldBeCalled(); - - $this->dispatchPostEvent($data, $create, $context)->shouldHaveType(OperationEvent::class); - } - - function it_dispatches_initialize_events( - EventDispatcherInterface $eventDispatcher, - \stdClass $data, - ): void { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - $create = (new Create(eventShortName: 'create'))->withResource($resource); - - $context = new Context(); - - $operationEvent = new OperationEvent($data->getWrappedObject(), [ - 'operation' => $create, - 'context' => $context, - ]); - - $eventDispatcher->dispatch($operationEvent, 'app.book.initialize_create')->shouldBeCalled(); - - $this->dispatchInitializeEvent($data, $create, $context)->shouldHaveType(OperationEvent::class); - } -} diff --git a/src/Component/spec/Symfony/EventDispatcher/State/DispatchPostReadEventProviderSpec.php b/src/Component/spec/Symfony/EventDispatcher/State/DispatchPostReadEventProviderSpec.php deleted file mode 100644 index 2dc4decdc..000000000 --- a/src/Component/spec/Symfony/EventDispatcher/State/DispatchPostReadEventProviderSpec.php +++ /dev/null @@ -1,91 +0,0 @@ -beConstructedWith($provider, $operationEventDispatcher); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(DispatchPostReadEventProvider::class); - } - - function it_dispatches_events_for_index_operation( - ProviderInterface $provider, - OperationEventDispatcherInterface $operationEventDispatcher, - ): void { - $operation = new Index(provider: '\App\Provider'); - $context = new Context(); - - $operationEvent = new OperationEvent(); - - $provider->provide($operation, $context)->shouldBeCalled(); - - $operationEventDispatcher->dispatch(null, $operation, $context)->willReturn($operationEvent)->shouldBeCalled(); - $provider->provide($operation, $context)->shouldBeCalled(); - - $operationEventDispatcher->dispatch(null, $operation, $context)->shouldBeCalled(); - - $this->provide($operation, $context); - } - - function it_dispatches_events_for_show_operation( - ProviderInterface $provider, - OperationEventDispatcherInterface $operationEventDispatcher, - ): void { - $operation = new Show(provider: '\App\Provider'); - $context = new Context(); - - $operationEvent = new OperationEvent(); - - $provider->provide($operation, $context)->shouldBeCalled(); - - $operationEventDispatcher->dispatch(null, $operation, $context)->willReturn($operationEvent)->shouldBeCalled(); - $provider->provide($operation, $context)->shouldBeCalled(); - - $operationEventDispatcher->dispatch(null, $operation, $context)->shouldBeCalled(); - - $this->provide($operation, $context); - } - - function it_does_not_dispatch_events_for_create_operation( - ProviderInterface $provider, - OperationEventDispatcherInterface $operationEventDispatcher, - ): void { - $operation = new Create(provider: '\App\Provider'); - $context = new Context(); - - $provider->provide($operation, $context)->shouldBeCalled(); - - $operationEventDispatcher->dispatch(null, $operation, $context)->shouldNotBeCalled(); - - $this->provide($operation, $context); - } -} diff --git a/src/Component/spec/Symfony/ExpressionLanguage/ArgumentParserSpec.php b/src/Component/spec/Symfony/ExpressionLanguage/ArgumentParserSpec.php deleted file mode 100644 index d74154427..000000000 --- a/src/Component/spec/Symfony/ExpressionLanguage/ArgumentParserSpec.php +++ /dev/null @@ -1,46 +0,0 @@ -beConstructedWith(new ExpressionLanguage(), $variablesCollection); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(ArgumentParser::class); - } - - function it_parses_expressions(VariablesCollectionInterface $variablesCollection): void - { - $variablesCollection->getVariables()->willReturn(['foo' => 'fighters']); - - $this->parseExpression('foo')->shouldReturn('fighters'); - } - - function it_merges_variables(VariablesCollectionInterface $variablesCollection): void - { - $variablesCollection->getVariables()->willReturn(['foo' => 'fighters']); - - $this->parseExpression('foo', ['foo' => 'bar'])->shouldReturn('bar'); - } -} diff --git a/src/Component/spec/Symfony/ExpressionLanguage/RequestVariablesSpec.php b/src/Component/spec/Symfony/ExpressionLanguage/RequestVariablesSpec.php deleted file mode 100644 index 0adf366c4..000000000 --- a/src/Component/spec/Symfony/ExpressionLanguage/RequestVariablesSpec.php +++ /dev/null @@ -1,43 +0,0 @@ -beConstructedWith($requestStack); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(RequestVariables::class); - } - - function it_returns_request_vars( - RequestStack $requestStack, - Request $request, - ): void { - $requestStack->getCurrentRequest()->willReturn($request); - - $this->getVariables()->shouldReturn([ - 'request' => $request, - ]); - } -} diff --git a/src/Component/spec/Symfony/ExpressionLanguage/TokenVariablesSpec.php b/src/Component/spec/Symfony/ExpressionLanguage/TokenVariablesSpec.php deleted file mode 100644 index 8b8d80beb..000000000 --- a/src/Component/spec/Symfony/ExpressionLanguage/TokenVariablesSpec.php +++ /dev/null @@ -1,87 +0,0 @@ -beConstructedWith($tokenStorage); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(TokenVariables::class); - } - - function it_returns_token_and_user_vars( - TokenStorageInterface $tokenStorage, - TokenInterface $token, - UserInterface $user, - ): void { - $tokenStorage->getToken()->willReturn($token); - - $token->getUser()->willReturn($user); - - $this->getVariables()->shouldReturn([ - 'token' => $token, - 'user' => $user, - ]); - } - - function it_returns_a_null_token_if_there_is_no_token_on_storage( - TokenStorageInterface $tokenStorage, - TokenInterface $token, - ): void { - $tokenStorage->getToken()->willReturn(null); - - $token->getUser()->willReturn(null); - - $this->getVariables()['token']->shouldHaveType(NullToken::class); - } - - function it_can_return_null_as_user( - TokenStorageInterface $tokenStorage, - TokenInterface $token, - UserInterface $user, - ): void { - $tokenStorage->getToken()->willReturn($token); - - $token->getUser()->willReturn(null); - - $this->getVariables()->shouldReturn([ - 'token' => $token, - 'user' => null, - ]); - } - - function it_throws_an_exception_when_there_is_no_token_storage( - TokenStorageInterface $tokenStorage, - TokenInterface $token, - UserInterface $user, - ): void { - $this->beConstructedWith(null); - - $this->shouldThrow(new \LogicException('The "symfony/security-bundle" must be installed and configured to use the "token" & "user" attribute. Try running "composer require symfony/security-bundle"')) - ->during('getVariables') - ; - } -} diff --git a/src/Component/spec/Symfony/ExpressionLanguage/VariablesCollectionSpec.php b/src/Component/spec/Symfony/ExpressionLanguage/VariablesCollectionSpec.php deleted file mode 100644 index d6ee5c9f2..000000000 --- a/src/Component/spec/Symfony/ExpressionLanguage/VariablesCollectionSpec.php +++ /dev/null @@ -1,47 +0,0 @@ -beConstructedWith([$firstVariables->getWrappedObject(), $secondVariables->getWrappedObject()]); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(VariablesCollection::class); - } - - function it_merges_variables( - VariablesInterface $firstVariables, - VariablesInterface $secondVariables, - ): void { - $firstVariables->getVariables()->willReturn(['foo' => 'bar', 'user' => '123']); - $secondVariables->getVariables()->willReturn(['foo' => 'fighters', 'value' => 'xyz']); - - $this->getVariables()->shouldReturn([ - 'foo' => 'fighters', - 'user' => '123', - 'value' => 'xyz', - ]); - } -} diff --git a/src/Component/spec/Symfony/Form/Factory/FormFactorySpec.php b/src/Component/spec/Symfony/Form/Factory/FormFactorySpec.php deleted file mode 100644 index c123994d6..000000000 --- a/src/Component/spec/Symfony/Form/Factory/FormFactorySpec.php +++ /dev/null @@ -1,86 +0,0 @@ -beConstructedWith($formFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(FormFactory::class); - } - - function it_creates_a_form( - Operation $operation, - SymfonyFormFactoryInterface $formFactory, - FormInterface $form, - ): void { - $operation->getFormType()->willReturn('App\Form\DummyType'); - $operation->getFormOptions()->willReturn(['foo' => 'fighters']); - - $formFactory->createNamed('', 'App\Form\DummyType', null, ['foo' => 'fighters', 'csrf_protection' => false]) - ->willReturn($form) - ->shouldBeCalled() - ; - - $this->create($operation, new Context())->shouldReturn($form); - } - - function it_creates_a_form_form_html_request( - Operation $operation, - SymfonyFormFactoryInterface $formFactory, - FormInterface $form, - Request $request, - ): void { - $operation->getFormType()->willReturn('App\Form\DummyType'); - $operation->getFormOptions()->willReturn(['foo' => 'fighters']); - - $request->getRequestFormat()->willReturn('html'); - - $formFactory->create('App\Form\DummyType', null, ['foo' => 'fighters']) - ->willReturn($form) - ->shouldBeCalled() - ; - - $this->create($operation, new Context(new RequestOption($request->getWrappedObject())))->shouldReturn($form); - } - - function it_throws_an_exception_when_operation_has_no_form_type( - Operation $operation, - SymfonyFormFactoryInterface $formFactory, - FormInterface $form, - ): void { - $operation->getFormType()->willReturn(null); - $operation->getFormOptions()->willReturn([]); - - $operation->getName()->willReturn('app_dummy_create'); - - $this->shouldThrow(new \RuntimeException('Operation "app_dummy_create" has no configured form type.')) - ->during('create', [$operation, new Context()]) - ; - } -} diff --git a/src/Component/spec/Symfony/Request/RepositoryArgumentResolverSpec.php b/src/Component/spec/Symfony/Request/RepositoryArgumentResolverSpec.php deleted file mode 100644 index 1cc796e1b..000000000 --- a/src/Component/spec/Symfony/Request/RepositoryArgumentResolverSpec.php +++ /dev/null @@ -1,121 +0,0 @@ -shouldHaveType(RepositoryArgumentResolver::class); - } - - function it_gets_arguments_to_sent_to_the_repository( - Request $request, - ParameterBag $attributes, - ): void { - $request->attributes = $attributes; - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $attributes->all('_route_params')->willReturn(['id' => 'my_id']); - - $callable = [RepositoryWithCallables::class, 'find']; - $reflector = CallableReflection::from($callable); - - $this->getArguments($request, $reflector)->shouldReturn([ - 'id' => 'my_id', - ]); - } - - function it_uses_query_params_when_route_params_are_not_matching( - Request $request, - ParameterBag $attributes, - ): void { - $request->attributes = $attributes; - $request->query = new InputBag(['id' => 'my_id']); - $request->request = new InputBag(); - - $attributes->all('_route_params')->willReturn(['_sylius' => ['resource' => 'app.dummy']]); - - $callable = [RepositoryWithCallables::class, 'find']; - $reflector = CallableReflection::from($callable); - - $this->getArguments($request, $reflector)->shouldReturn([ - 'id' => 'my_id', - ]); - } - - function it_uses_request_params_when_route_params_are_not_matching( - Request $request, - ParameterBag $attributes, - ): void { - $request->attributes = $attributes; - $request->query = new InputBag(); - $request->request = new InputBag(['id' => 'my_id']); - - $attributes->all('_route_params')->willReturn(['_sylius' => ['resource' => 'app.dummy']]); - - $callable = [RepositoryWithCallables::class, 'find']; - $reflector = CallableReflection::from($callable); - - $this->getArguments($request, $reflector)->shouldReturn([ - 'id' => 'my_id', - ]); - } - - function it_encapsulates_arguments_when_the_method_has_only_one_required_array_argument( - Request $request, - ParameterBag $attributes, - ): void { - $request->attributes = $attributes; - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $attributes->all('_route_params')->willReturn(['enabled' => 'true', 'author' => 'author@example.com']); - - $callable = [RepositoryWithCallables::class, 'findOneBy']; - $reflector = CallableReflection::from($callable); - - $this->getArguments($request, $reflector)->shouldReturn([ - [ - 'enabled' => 'true', - 'author' => 'author@example.com', - ], - ]); - } - - function it_return_array_values_when_method_is_magic( - Request $request, - ParameterBag $attributes, - ): void { - $request->attributes = $attributes; - $request->query = new InputBag(); - $request->request = new InputBag(['ids' => ['first_id', 'second_id']]); - - $attributes->all('_route_params')->willReturn(['_sylius' => ['resource' => 'app.dummy']]); - - $callable = [new RepositoryWithCallables(), '__call']; - $reflector = CallableReflection::from($callable); - - $this->getArguments($request, $reflector)->shouldReturn([['first_id', 'second_id']]); - } -} diff --git a/src/Component/spec/Symfony/Request/State/ApiResponderSpec.php b/src/Component/spec/Symfony/Request/State/ApiResponderSpec.php deleted file mode 100644 index 6993a570e..000000000 --- a/src/Component/spec/Symfony/Request/State/ApiResponderSpec.php +++ /dev/null @@ -1,128 +0,0 @@ -beConstructedWith(new ApiHeadersInitiator()); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(ApiResponder::class); - } - - function it_returns_a_response_with_http_created_for_resource_create( - Request $request, - ParameterBag $attributes, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $request->attributes = $attributes; - - $request->getRequestFormat()->willReturn('json'); - $request->getMimeType('json')->willReturn('application/json'); - - $attributes->getBoolean('is_valid', true)->willReturn(true)->shouldBeCalled(); - $attributes->get('form')->willReturn(null); - - $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); - $operation = (new Create())->withResource($resource); - - $response = $this->respond('serialized_data', $operation, $context); - $response->shouldHaveType(Response::class); - $response->getStatusCode()->shouldReturn(Response::HTTP_CREATED); - } - - function it_returns_a_response_with_http_no_content_for_resource_update( - Request $request, - ParameterBag $attributes, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $request->attributes = $attributes; - - $request->getRequestFormat()->willReturn('json'); - $request->getMimeType('json')->willReturn('application/json'); - - $attributes->getBoolean('is_valid', true)->willReturn(true)->shouldBeCalled(); - $attributes->get('form')->willReturn(null); - - $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); - $operation = (new Update())->withResource($resource); - - $response = $this->respond('serialized_data', $operation, $context); - $response->shouldHaveType(Response::class); - $response->getStatusCode()->shouldReturn(Response::HTTP_NO_CONTENT); - } - - function it_returns_a_response_with_http_no_content_for_resource_delete( - Request $request, - ParameterBag $attributes, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $request->attributes = $attributes; - - $request->getRequestFormat()->willReturn('json'); - $request->getMimeType('json')->willReturn('application/json'); - - $attributes->getBoolean('is_valid', true)->willReturn(true)->shouldBeCalled(); - $attributes->get('form')->willReturn(null); - - $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); - $operation = (new Delete())->withResource($resource); - - $response = $this->respond('serialized_data', $operation, $context); - $response->shouldHaveType(Response::class); - $response->getStatusCode()->shouldReturn(Response::HTTP_NO_CONTENT); - } - - function it_returns_a_response_with_http_unprocessable_entity_for_invalid_resource( - Request $request, - ParameterBag $attributes, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $request->attributes = $attributes; - - $request->getRequestFormat()->willReturn('json'); - $request->getMimeType('json')->willReturn('application/json'); - - $attributes->getBoolean('is_valid', true)->willReturn(false)->shouldBeCalled(); - $attributes->get('form')->willReturn(null); - - $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); - $operation = (new Create())->withResource($resource); - - $response = $this->respond('serialized_data', $operation, $context); - $response->shouldHaveType(Response::class); - $response->getStatusCode()->shouldReturn(Response::HTTP_UNPROCESSABLE_ENTITY); - } -} diff --git a/src/Component/spec/Symfony/Request/State/ProviderSpec.php b/src/Component/spec/Symfony/Request/State/ProviderSpec.php deleted file mode 100644 index 05d3fbb89..000000000 --- a/src/Component/spec/Symfony/Request/State/ProviderSpec.php +++ /dev/null @@ -1,175 +0,0 @@ -beConstructedWith($locator, new RepositoryArgumentResolver(), $argumentParser); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(Provider::class); - } - - function it_calls_repository_as_callable( - Operation $operation, - Request $request, - ): void { - $operation->getRepository()->willReturn([RepositoryWithCallables::class, 'find']); - $operation->getRepositoryArguments()->willReturn(null); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id']]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldHaveType(\stdClass::class); - $response->id->shouldReturn('my_id'); - } - - function it_calls_repository_as_string( - Operation $operation, - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - \stdClass $stdClass, - ): void { - $operation->getRepository()->willReturn('App\Repository'); - $operation->getRepositoryMethod()->willReturn(null); - $operation->getRepositoryArguments()->willReturn(null); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->findOneBy(['id' => 'my_id'])->willReturn($stdClass); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($stdClass); - } - - function it_calls_create_paginator_by_default_on_collection_operations( - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - Pagerfanta $pagerfanta, - ): void { - $operation = new Index(repository: 'App\Repository'); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->createPaginator()->willReturn($pagerfanta)->shouldBeCalled(); - $pagerfanta->setCurrentPage(1)->willReturn($pagerfanta)->shouldBeCalled(); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($pagerfanta); - } - - function it_sets_current_page_from_request_when_data_is_a_paginator( - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - Pagerfanta $pagerfanta, - ): void { - $operation = new Index(repository: 'App\Repository'); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag(['page' => 42]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->createPaginator()->willReturn($pagerfanta)->shouldBeCalled(); - $pagerfanta->setCurrentPage(42)->willReturn($pagerfanta)->shouldBeCalled(); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($pagerfanta); - $pagerfanta->getCurrentPage()->willReturn(42); - } - - function it_calls_repository_as_string_with_specific_repository_method( - Operation $operation, - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - \stdClass $stdClass, - ): void { - $operation->getRepository()->willReturn('App\Repository'); - $operation->getRepositoryMethod()->willReturn('find'); - $operation->getRepositoryArguments()->willReturn(null); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->find('my_id')->willReturn($stdClass); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($stdClass); - } - - function it_calls_repository_as_string_with_specific_repository_method_an_arguments( - Operation $operation, - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - ArgumentParserInterface $argumentParser, - \stdClass $stdClass, - ): void { - $operation->getRepository()->willReturn('App\Repository'); - $operation->getRepositoryMethod()->willReturn('find'); - $operation->getRepositoryArguments()->willReturn(['id' => "request.attributes.get('id')"]); - - $argumentParser->parseExpression("request.attributes.get('id')")->willReturn('my_id'); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->find('my_id')->willReturn($stdClass); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($stdClass); - } -} diff --git a/src/Component/spec/Symfony/Request/State/ResponderSpec.php b/src/Component/spec/Symfony/Request/State/ResponderSpec.php deleted file mode 100644 index c0cf91ab1..000000000 --- a/src/Component/spec/Symfony/Request/State/ResponderSpec.php +++ /dev/null @@ -1,107 +0,0 @@ -beConstructedWith($locator); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(Responder::class); - } - - function it_uses_html_responder_on_html_format( - \stdClass $data, - Request $request, - ContainerInterface $locator, - ResponderInterface $htmlResponder, - HttpOperation $operation, - Response $response, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - $request->getRequestFormat()->willReturn('html'); - - $locator->has('sylius.state_responder.html')->willReturn(true); - $locator->get('sylius.state_responder.html')->willReturn($htmlResponder); - - $htmlResponder->respond($data, $operation, $context)->willReturn($response)->shouldBeCalled(); - - $this->respond($data, $operation, $context); - } - - function it_uses_api_responder_on_json_format( - \stdClass $data, - Request $request, - ContainerInterface $locator, - ResponderInterface $apiResponder, - HttpOperation $operation, - Response $response, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - $request->getRequestFormat()->willReturn('json'); - - $locator->has('sylius.state_responder.api')->willReturn(true); - $locator->get('sylius.state_responder.api')->willReturn($apiResponder); - - $apiResponder->respond($data, $operation, $context)->willReturn($response)->shouldBeCalled(); - - $this->respond($data, $operation, $context); - } - - function it_throw_an_exception_when_html_responder_was_not_found( - \stdClass $data, - Request $request, - ContainerInterface $locator, - HttpOperation $operation, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - $request->getRequestFormat()->willReturn('html'); - - $locator->has('sylius.state_responder.html')->willReturn(false); - - $this->shouldThrow(new \LogicException('Responder "sylius.state_responder.html" was not found but it should.')) - ->during('respond', [$data, $operation, $context]) - ; - } - - function it_throw_an_exception_when_json_responder_was_not_found( - \stdClass $data, - Request $request, - ContainerInterface $locator, - HttpOperation $operation, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - $request->getRequestFormat()->willReturn('json'); - - $locator->has('sylius.state_responder.api')->willReturn(false); - - $this->shouldThrow(new \LogicException('Responder "sylius.state_responder.api" was not found but it should.')) - ->during('respond', [$data, $operation, $context]) - ; - } -} diff --git a/src/Component/spec/Symfony/Request/State/TwigResponderSpec.php b/src/Component/spec/Symfony/Request/State/TwigResponderSpec.php deleted file mode 100644 index ccba96902..000000000 --- a/src/Component/spec/Symfony/Request/State/TwigResponderSpec.php +++ /dev/null @@ -1,148 +0,0 @@ -beConstructedWith($redirectHandler, $contextFactory, $twig); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(TwigResponder::class); - } - - function it_returns_a_response_for_resource_show( - \stdClass $data, - Request $request, - ParameterBag $attributes, - ContextFactoryInterface $contextFactory, - Environment $twig, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $request->attributes = $attributes; - - $request->isMethodSafe()->willReturn(true)->shouldBeCalled(); - - $attributes->getBoolean('is_valid', true)->willReturn(false)->shouldBeCalled(); - $attributes->get('form')->willReturn(null); - - $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); - $operation = (new Show(template: 'book/show.html.twig'))->withResource($resource); - - $contextFactory->create($data, $operation, $context)->willReturn(['book' => $data]); - - $twig->render('book/show.html.twig', [ - 'book' => $data->getWrappedObject(), - ])->willReturn('result')->shouldBeCalled(); - - $response = $this->respond($data, $operation, $context); - $response->getStatusCode()->shouldReturn(200); - } - - function it_returns_a_response_for_resource_index( - \ArrayObject $data, - Request $request, - ParameterBag $attributes, - ContextFactoryInterface $contextFactory, - Environment $twig, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $request->attributes = $attributes; - - $request->isMethodSafe()->willReturn(true)->shouldBeCalled(); - - $attributes->getBoolean('is_valid', true)->willReturn(true)->shouldBeCalled(); - $attributes->get('form')->willReturn(null); - - $resource = new ResourceMetadata(alias: 'app.book', pluralName: 'books'); - $operation = (new Index(template: 'book/index.html.twig'))->withResource($resource); - - $contextFactory->create($data, $operation, $context)->willReturn(['books' => $data]); - - $twig->render('book/index.html.twig', [ - 'books' => $data->getWrappedObject(), - ])->willReturn('result')->shouldBeCalled(); - - $this->respond($data, $operation, $context); - } - - function it_redirect_to_route_after_creation( - \ArrayObject $data, - Request $request, - ParameterBag $attributes, - RedirectHandlerInterface $redirectHandler, - RedirectResponse $response, - ): void { - $data->offsetSet('id', 'xyz'); - $request->attributes = $attributes; - - $request->isMethodSafe()->willReturn(false)->shouldBeCalled(); - $attributes->getBoolean('is_valid', true)->willReturn(true)->shouldBeCalled(); - - $operation = new Create(); - - $redirectHandler->redirectToResource($data, $operation, $request)->willReturn($response); - - $this->respond($data, $operation, new Context(new RequestOption($request->getWrappedObject())))->shouldReturn($response); - } - - function it_response_is_unprocessable_when_validation_has_failed( - \ArrayObject $data, - Request $request, - ParameterBag $attributes, - ContextFactoryInterface $contextFactory, - Environment $twig, - ): void { - $context = new Context(new RequestOption($request->getWrappedObject())); - - $data->offsetSet('id', 'xyz'); - $request->attributes = $attributes; - - $request->isMethodSafe()->willReturn(false)->shouldBeCalled(); - - $attributes->getBoolean('is_valid', true)->willReturn(false)->shouldBeCalled(); - - $operation = new Create(); - - $contextFactory->create($data, $operation, $context)->willReturn(['books' => $data]); - - $twig->render('', ['books' => $data])->willReturn('twig_content'); - - $response = $this->respond($data, $operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->getStatusCode()->shouldReturn(422); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/OperationRouteFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/OperationRouteFactorySpec.php deleted file mode 100644 index edc46a93f..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/OperationRouteFactorySpec.php +++ /dev/null @@ -1,292 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(OperationRouteFactory::class); - } - - function it_generates_create_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Create(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies/new')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/new'); - $route->getMethods()->shouldReturn(['GET', 'POST']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_index_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Index(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - new Index(), - ); - - $route->getPath()->shouldReturn('/dummies'); - $route->getMethods()->shouldReturn(['GET']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_show_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Show(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies/{id}')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/{id}'); - $route->getMethods()->shouldReturn(['GET']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_update_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Update(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies/{id}/edit')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/{id}/edit'); - $route->getMethods()->shouldReturn(['GET', 'PUT', 'POST']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_delete_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Delete(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies/{id}')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/{id}'); - $route->getMethods()->shouldReturn(['DELETE', 'POST']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_bulk_delete_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new BulkDelete(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies/bulk_delete')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/bulk_delete'); - $route->getMethods()->shouldReturn(['DELETE', 'POST']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_bulk_update_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new BulkUpdate(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('dummies/bulk_update')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/bulk_update'); - $route->getMethods()->shouldReturn(['PUT', 'PATCH']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_custom_operations_routes( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new HttpOperation(methods: ['PATCH'], path: 'dummies/{id}/custom'); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath(Argument::cetera())->willReturn('')->shouldNotBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata('app.dummy'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/{id}/custom'); - $route->getMethods()->shouldReturn(['PATCH']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - ], - ]); - } - - function it_generates_routes_with_sections( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Show(); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('/dummies/{id}')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata(alias: 'app.dummy', section: 'admin'), - $operation, - ); - - $route->getPath()->shouldReturn('/dummies/{id}'); - $route->getMethods()->shouldReturn(['GET']); - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - 'section' => 'admin', - ], - ]); - } - - function it_generates_routes_with_vars( - OperationRoutePathFactoryInterface $routePathFactory, - ): void { - $operation = new Index(vars: ['subheader' => 'Managing your library']); - - $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); - - $routePathFactory->createRoutePath($operation, 'dummies')->willReturn('/dummies')->shouldBeCalled(); - - $route = $this->create( - $metadata, - new ResourceMetadata(alias: 'app.dummy'), - $operation, - ); - - $route->getDefaults()->shouldReturn([ - '_controller' => 'sylius.main_controller', - '_sylius' => [ - 'resource' => 'app.dummy', - 'vars' => ['subheader' => 'Managing your library'], - ], - ]); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RouteName/OperationRouteNameFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RouteName/OperationRouteNameFactorySpec.php deleted file mode 100644 index 8a8a79028..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RouteName/OperationRouteNameFactorySpec.php +++ /dev/null @@ -1,52 +0,0 @@ -shouldHaveType(OperationRouteNameFactory::class); - } - - function it_create_a_route_name(): void - { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - $operation = (new Create())->withResource($resource); - - $this->createRouteName($operation)->shouldReturn('app_book_create'); - } - - function it_create_a_route_name_with_a_section(): void - { - $resource = new ResourceMetadata(alias: 'app.book', section: 'admin', name: 'book', applicationName: 'app'); - $operation = (new Create())->withResource($resource); - - $this->createRouteName($operation)->shouldReturn('app_admin_book_create'); - } - - function it_throws_an_exception_when_operation_has_no_resource(): void - { - $operation = new Create(); - - $this->shouldThrow(new \RuntimeException('No resource was found on the operation "create"')) - ->during('createRouteName', [$operation]) - ; - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactorySpec.php deleted file mode 100644 index 945052116..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactorySpec.php +++ /dev/null @@ -1,47 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(BulkOperationRoutePathFactory::class); - } - - function it_generates_route_path_for_bulk_delete_operations(): void - { - $operation = new BulkDelete(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/bulk_delete'); - } - - function it_generates_route_path_for_bulk_update_operations(): void - { - $operation = new BulkUpdate(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/bulk_update'); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactorySpec.php deleted file mode 100644 index 6aa4db95f..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactorySpec.php +++ /dev/null @@ -1,54 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(CollectionOperationRoutePathFactory::class); - } - - function it_generates_route_path_for_index_operations(): void - { - $operation = new Index(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies'); - } - - function it_generates_route_path_for_index_operations_with_custom_short_name(): void - { - $operation = new Index(shortName: 'list'); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/list'); - } - - function it_generates_route_path_for_api_get_collection_operations(): void - { - $operation = new Api\GetCollection(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies'); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactorySpec.php deleted file mode 100644 index 1ebfb3b57..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactorySpec.php +++ /dev/null @@ -1,54 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(CreateOperationRoutePathFactory::class); - } - - function it_generates_route_path_for_create_operations(): void - { - $operation = new Create(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/new'); - } - - function it_generates_route_path_for_create_operations_with_custom_short_name(): void - { - $operation = new Create(shortName: 'register'); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/register'); - } - - function it_generates_route_path_for_api_post_operations(): void - { - $operation = new Api\Post(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies'); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactorySpec.php deleted file mode 100644 index dbbffde1f..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactorySpec.php +++ /dev/null @@ -1,69 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(DeleteOperationRoutePathFactory::class); - } - - function it_generates_route_path_for_delete_operations(): void - { - $operation = new Delete(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}/delete'); - } - - function it_generates_route_path_for_delete_operations_with_custom_identifier(): void - { - $operation = (new Delete())->withResource(new ResourceMetadata(identifier: 'code')); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{code}/delete'); - } - - function it_generates_route_path_for_update_operations_with_custom_short_name(): void - { - $operation = new Delete(shortName: 'remove'); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}/remove'); - } - - function it_generates_route_path_for_api_delete_operations(): void - { - $operation = new Api\Delete(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}'); - } - - function it_generates_route_path_for_api_delete_operations_with_custom_short_name(): void - { - $operation = new Api\Delete(shortName: 'remove'); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}/remove'); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactorySpec.php deleted file mode 100644 index 63ff2e0e0..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactorySpec.php +++ /dev/null @@ -1,62 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(ShowOperationRoutePathFactory::class); - } - - function it_generates_route_path_for_show_operations(): void - { - $operation = new Show(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}'); - } - - function it_generates_route_path_for_delete_operations_with_custom_identifier(): void - { - $operation = (new Show())->withResource(new ResourceMetadata(identifier: 'code')); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{code}'); - } - - function it_generates_route_path_for_show_operations_with_custom_short_name(): void - { - $operation = new Show(shortName: 'details'); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}/details'); - } - - function it_generates_route_path_for_api_get_operations(): void - { - $operation = new Api\Get(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}'); - } -} diff --git a/src/Component/spec/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactorySpec.php b/src/Component/spec/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactorySpec.php deleted file mode 100644 index 024540268..000000000 --- a/src/Component/spec/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactorySpec.php +++ /dev/null @@ -1,69 +0,0 @@ -beConstructedWith($routePathFactory); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(UpdateOperationRoutePathFactory::class); - } - - function it_generates_route_path_for_update_operations(): void - { - $operation = new Update(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}/edit'); - } - - function it_generates_route_path_for_update_operations_with_custom_identifier(): void - { - $operation = (new Update())->withResource(new ResourceMetadata(identifier: 'code')); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{code}/edit'); - } - - function it_generates_route_path_for_update_operations_with_custom_short_name(): void - { - $operation = new Update(shortName: 'edition'); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}/edition'); - } - - function it_generates_route_path_for_api_put_operations(): void - { - $operation = new Api\Put(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}'); - } - - function it_generates_route_path_for_api_patch_operations(): void - { - $operation = new Api\Patch(); - - $this->createRoutePath($operation, '/dummies')->shouldReturn('/dummies/{id}'); - } -} diff --git a/src/Component/spec/Symfony/Routing/RedirectHandlerSpec.php b/src/Component/spec/Symfony/Routing/RedirectHandlerSpec.php deleted file mode 100644 index 82f6fff81..000000000 --- a/src/Component/spec/Symfony/Routing/RedirectHandlerSpec.php +++ /dev/null @@ -1,201 +0,0 @@ -beConstructedWith( - $router, - $argumentParser, - $operationRouteNameFactory, - ); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(RedirectHandler::class); - } - - function it_redirects_to_resource_with_id_argument_by_default( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $data->id = 'xyz'; - $operation = new Create(redirectToRoute: 'app_dummy_index'); - $resource = new ResourceMetadata(alias: 'app.book'); - $operation = $operation->withResource($resource); - - $router->generate('app_dummy_index', ['id' => 'xyz'])->willReturn('/dummies')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_resource_with_custom_identifier_argument_by_default( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $data->code = 'xyz'; - $operation = new Create(redirectToRoute: 'app_dummy_index'); - $resource = new ResourceMetadata(alias: 'app.ok', identifier: 'code'); - $operation = $operation->withResource($resource); - - $router->generate('app_dummy_index', ['code' => 'xyz'])->willReturn('/dummies')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_resource_with_id_via_property_access( - Request $request, - RouterInterface $router, - ): void { - $data = new BoardGameResource('uid'); - $operation = new Create(redirectToRoute: 'app_board_game_index'); - $resource = new ResourceMetadata(alias: 'app.board_game'); - $operation = $operation->withResource($resource); - - $router->generate('app_board_game_index', ['id' => 'uid'])->willReturn('/board-games')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_resource_with_custom_arguments( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $data->code = 'xyz'; - $operation = new Create(redirectToRoute: 'app_dummy_index', redirectArguments: ['code' => 'resource.code']); - $resource = new ResourceMetadata(alias: 'app.book'); - $operation = $operation->withResource($resource); - - $router->generate('app_dummy_index', ['code' => 'xyz'])->willReturn('/dummies')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_resource_with_id_via_the_getter( - Request $request, - RouterInterface $router, - ArgumentParserInterface $argumentParser, - ): void { - $data = new BoardGameResource('uid'); - $operation = new Create(redirectToRoute: 'app_board_game_index', redirectArguments: ['id' => 'resource.id()']); - $resource = new ResourceMetadata(alias: 'app.board_game'); - $operation = $operation->withResource($resource); - - $argumentParser->parseExpression('resource.id()', ['resource' => $data])->willReturn('uid'); - - $router->generate('app_board_game_index', ['id' => 'uid'])->willReturn('/board-games')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_resource_without_arguments_after_delete_operation_by_default( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $data->id = 'xyz'; - $operation = new Delete(redirectToRoute: 'app_dummy_index'); - $resource = new ResourceMetadata(alias: 'app.book'); - $operation = $operation->withResource($resource); - - $router->generate('app_dummy_index', [])->willReturn('/dummies')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_resource_without_arguments_after_bulk_operation_by_default( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $data->id = 'xyz'; - $operation = new BulkUpdate(redirectToRoute: 'app_dummy_index'); - $resource = new ResourceMetadata(alias: 'app.book'); - $operation = $operation->withResource($resource); - - $router->generate('app_dummy_index', [])->willReturn('/dummies')->shouldBeCalled(); - - $this->redirectToResource($data, $operation, $request); - } - - function it_redirects_to_route( - \stdClass $data, - RouterInterface $router, - ): void { - $router->generate('app_dummy_index', [])->willReturn('/dummies')->shouldBeCalled(); - - $this->redirectToRoute($data, 'app_dummy_index'); - } - - function it_throws_an_exception_when_operation_has_no_resource( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $operation = new Create(redirectToRoute: 'app_dummy_index', name: 'app_dummy_create'); - - $router->generate(Argument::cetera())->shouldNotBeCalled(); - - $this->shouldThrow( - new \RuntimeException('Operation "app_dummy_create" has no resource, but it should.'), - )->during('redirectToResource', [$data, $operation, $request]); - } - - function it_throws_an_exception_when_operation_has_no_route_redirection( - \stdClass $data, - Request $request, - RouterInterface $router, - ): void { - $operation = new Create(name: 'app_dummy_create'); - - $router->generate(Argument::cetera())->shouldNotBeCalled(); - - $this->shouldThrow( - new \RuntimeException('Operation "app_dummy_create" has no redirection route, but it should.'), - )->during('redirectToResource', [$data, $operation, $request]); - } -} - -final class BoardGameResource -{ - public function __construct(private string $id) - { - } - - public function id(): string - { - return $this->id; - } -} diff --git a/src/Component/spec/Symfony/Validator/EventListener/ValidationExceptionListenerSpec.php b/src/Component/spec/Symfony/Validator/EventListener/ValidationExceptionListenerSpec.php deleted file mode 100644 index d8c9d2e68..000000000 --- a/src/Component/spec/Symfony/Validator/EventListener/ValidationExceptionListenerSpec.php +++ /dev/null @@ -1,116 +0,0 @@ -beConstructedWith($serializer); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(ValidationExceptionListener::class); - } - - function it_transforms_validation_exception_to_a_response( - KernelInterface $kernel, - Request $request, - SerializerInterface $serializer, - ): void { - $violationList = new ConstraintViolationList(); - $exception = new ValidationException($violationList); - - $event = new ExceptionEvent( - $kernel->getWrappedObject(), - $request->getWrappedObject(), - HttpKernelInterface::MAIN_REQUEST, - $exception, - ); - - $request->getRequestFormat()->willReturn('json'); - $request->getMimeType('json')->willReturn('application/json'); - - $serializer->serialize($violationList, 'json')->willReturn('serialized_exception')->shouldBeCalled(); - - $this->onKernelException($event); - - $response = $event->getResponse(); - - Assert::isInstanceOf($response, Response::class); - Assert::eq($response->getContent(), 'serialized_exception'); - Assert::eq($response->getStatusCode(), 422); - Assert::eq($response->headers, new ResponseHeaderBag([ - 'Content-Type' => 'application/json; charset=utf-8', - 'X-Content-Type-Options' => 'nosniff', - 'X-Frame-Options' => 'deny', - ])); - } - - function it_does_nothing_on_other_exceptions( - KernelInterface $kernel, - Request $request, - SerializerInterface $serializer, - \Throwable $exception, - ): void { - $event = new ExceptionEvent( - $kernel->getWrappedObject(), - $request->getWrappedObject(), - HttpKernelInterface::MAIN_REQUEST, - $exception->getWrappedObject(), - ); - - $serializer->serialize(Argument::cetera())->shouldNotBeCalled(); - - $this->onKernelException($event); - - Assert::null($event->getResponse()); - } - - function it_throws_an_exception_when_serializer_is_not_available( - KernelInterface $kernel, - Request $request, - ): void { - $this->beConstructedWith(null); - - $violationList = new ConstraintViolationList(); - $exception = new ValidationException($violationList); - - $event = new ExceptionEvent( - $kernel->getWrappedObject(), - $request->getWrappedObject(), - HttpKernelInterface::MAIN_REQUEST, - $exception, - ); - - $this->shouldThrow(new \LogicException('The Symfony Serializer is not available. Try running "composer require symfony/serializer".')) - ->during('onKernelException', [$event]) - ; - } -} diff --git a/src/Component/spec/Symfony/Validator/Exception/ValidationExceptionSpec.php b/src/Component/spec/Symfony/Validator/Exception/ValidationExceptionSpec.php deleted file mode 100644 index 562d7c611..000000000 --- a/src/Component/spec/Symfony/Validator/Exception/ValidationExceptionSpec.php +++ /dev/null @@ -1,65 +0,0 @@ -beConstructedWith(new ConstraintViolationList([ - $firstViolation->getWrappedObject(), - $secondViolation->getWrappedObject(), - ])); - } - - function it_transforms_exception_into_a_string( - ConstraintViolationInterface $firstViolation, - ConstraintViolationInterface $secondViolation, - ): void { - $firstViolation->getPropertyPath()->willReturn('name'); - $firstViolation->getMessage()->willReturn('This value should not be blank.'); - - $secondViolation->getPropertyPath()->willReturn('email'); - $secondViolation->getMessage()->willReturn('This value should not be blank.'); - - $this->__toString()->shouldReturn("name: This value should not be blank.\nemail: This value should not be blank."); - } - - function it_can_be_constructed_with_a_message(): void - { - $this->beConstructedWith(new ConstraintViolationList([]), 'You should not pass!'); - - $this->getMessage()->shouldReturn('You should not pass!'); - } - - function it_can_be_constructed_with_a_code(): void - { - $this->beConstructedWith(new ConstraintViolationList([]), '', 42); - - $this->getCode()->shouldReturn(42); - } - - function it_can_be_constructed_with_a_previous_exception(\Exception $previous): void - { - $this->beConstructedWith(new ConstraintViolationList([]), '', 0, $previous->getWrappedObject()); - - $this->getPrevious()->shouldReturn($previous); - } -} diff --git a/src/Component/spec/Symfony/Workflow/OperationStateMachineSpec.php b/src/Component/spec/Symfony/Workflow/OperationStateMachineSpec.php deleted file mode 100644 index 0bd6aafeb..000000000 --- a/src/Component/spec/Symfony/Workflow/OperationStateMachineSpec.php +++ /dev/null @@ -1,107 +0,0 @@ -beConstructedWith($registry); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(OperationStateMachine::class); - } - - function it_returns_if_transition_is_possible( - \stdClass $data, - Registry $registry, - Workflow $workflow, - ): void { - $operation = new Create(stateMachineTransition: 'publish'); - - $registry->get($data, null)->willReturn($workflow); - - $workflow->can($data, 'publish')->willReturn(true); - - $this->can($data, $operation, new Context())->shouldReturn(true); - } - - function it_applies_transition( - \stdClass $data, - Registry $registry, - Workflow $workflow, - Marking $marking, - ): void { - $operation = new Create(stateMachineTransition: 'publish'); - - $registry->get($data, null)->willReturn($workflow); - - $workflow->apply($data, 'publish')->willReturn($marking)->shouldBeCalled(); - - $this->apply($data, $operation, new Context()); - } - - function it_throws_an_exception_when_operation_has_no_defined_transition( - \stdClass $data, - Registry $registry, - Workflow $workflow, - Marking $marking, - ): void { - $operation = new Create(name: 'app_dummy_create'); - - $registry->get($data, null)->willReturn($workflow); - - $this->shouldThrow(new \InvalidArgumentException('No State machine transition was found on operation "app_dummy_create".')) - ->during('can', [$data, $operation, new Context()]) - ; - - $this->shouldThrow(new \InvalidArgumentException('No State machine transition was found on operation "app_dummy_create".')) - ->during('apply', [$data, $operation, new Context()]) - ; - } - - function it_throws_an_exception_when_symfony_workflow_is_not_available( - \stdClass $data, - ): void { - $this->beConstructedWith(null); - - $operation = new Create(stateMachineTransition: 'publish'); - - $this->shouldThrow( - new \LogicException('You can not use the "state-machine" if Symfony workflow is not available. Try running "composer require symfony/workflow".'), - )->during('can', [$data, $operation, new Context()]); - } - - function it_throws_an_exception_when_operation_does_not_implement_a_state_machine( - \stdClass $data, - ): void { - $operation = new Index(); - - $this->shouldThrow( - new \LogicException(sprintf('Expected an instance of %s. Got: %s', StateMachineAwareOperationInterface::class, Index::class)), - )->during('can', [$data, $operation, new Context()]); - } -} diff --git a/src/Component/tests/Integration/Symfony/ExpressionLanguage/ArgumentParserTest.php b/src/Component/tests/Integration/Symfony/ExpressionLanguage/ArgumentParserTest.php new file mode 100644 index 000000000..d62a1ed11 --- /dev/null +++ b/src/Component/tests/Integration/Symfony/ExpressionLanguage/ArgumentParserTest.php @@ -0,0 +1,63 @@ +get('sylius.expression_language.argument_parser.factory'); + + $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); + $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); + $this->assertTrue($argumentParser->parseExpression('user === null')); + $this->assertTrue($argumentParser->parseExpression('request === null')); + } + + public function testRepositoryArgumentParser(): void + { + self::bootKernel(); + + $container = static::getContainer(); + + /** @var ArgumentParserInterface $argumentParser */ + $argumentParser = $container->get('sylius.expression_language.argument_parser.repository'); + + $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); + $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); + $this->assertTrue($argumentParser->parseExpression('user === null')); + $this->assertTrue($argumentParser->parseExpression('request === null')); + } + + public function testRoutingArgumentParser(): void + { + self::bootKernel(); + + $container = static::getContainer(); + + /** @var ArgumentParserInterface $argumentParser */ + $argumentParser = $container->get('sylius.expression_language.argument_parser.routing'); + $this->assertTrue($argumentParser->parseExpression('request === null')); + + $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); + } +} diff --git a/src/Component/tests/Symfony/EventDispatcher/OperationEventDispatcherTest.php b/src/Component/tests/Symfony/EventDispatcher/OperationEventDispatcherTest.php new file mode 100644 index 000000000..3b3b0023d --- /dev/null +++ b/src/Component/tests/Symfony/EventDispatcher/OperationEventDispatcherTest.php @@ -0,0 +1,137 @@ +eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $this->operationEventDispatcher = new OperationEventDispatcher($this->eventDispatcher); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(OperationEventDispatcher::class, $this->operationEventDispatcher); + } + + public function testItDispatchesEvents(): void + { + $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); + $show = (new Show(eventShortName: 'read'))->withResource($resource); + $context = new Context(); + $data = new \stdClass(); + + $operationEvent = new OperationEvent($data, [ + 'operation' => $show, + 'context' => $context, + ]); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with($operationEvent, 'app.book.read'); + + $this->operationEventDispatcher->dispatch($data, $show, $context); + } + + public function testItDispatchesEventsForBulkOperations(): void + { + $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); + $bulkDelete = (new BulkDelete(eventShortName: 'delete'))->withResource($resource); + $context = new Context(); + $data = new \ArrayObject(); + + $operationEvent = new OperationEvent($data, [ + 'operation' => $bulkDelete, + 'context' => $context, + ]); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with($operationEvent, 'app.book.bulk_delete'); + + $this->operationEventDispatcher->dispatchBulkEvent($data, $bulkDelete, $context); + } + + public function testItDispatchesPreEvents(): void + { + $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); + $create = (new Create(eventShortName: 'create'))->withResource($resource); + $context = new Context(); + $data = new \stdClass(); + + $operationEvent = new OperationEvent($data, [ + 'operation' => $create, + 'context' => $context, + ]); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with($operationEvent, 'app.book.pre_create'); + + $this->operationEventDispatcher->dispatchPreEvent($data, $create, $context); + } + + public function testItDispatchesPostEvents(): void + { + $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); + $create = (new Create(eventShortName: 'create'))->withResource($resource); + $context = new Context(); + $data = new \stdClass(); + + $operationEvent = new OperationEvent($data, [ + 'operation' => $create, + 'context' => $context, + ]); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with($operationEvent, 'app.book.post_create'); + + $this->operationEventDispatcher->dispatchPostEvent($data, $create, $context); + } + + public function testItDispatchesInitializeEvents(): void + { + $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); + $create = (new Create(eventShortName: 'create'))->withResource($resource); + $context = new Context(); + $data = new \stdClass(); + + $operationEvent = new OperationEvent($data, [ + 'operation' => $create, + 'context' => $context, + ]); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with($operationEvent, 'app.book.initialize_create'); + + $this->operationEventDispatcher->dispatchInitializeEvent($data, $create, $context); + } +} diff --git a/src/Component/tests/Symfony/EventDispatcher/State/DispatchPostReadEventProviderTest.php b/src/Component/tests/Symfony/EventDispatcher/State/DispatchPostReadEventProviderTest.php new file mode 100644 index 000000000..2641779f5 --- /dev/null +++ b/src/Component/tests/Symfony/EventDispatcher/State/DispatchPostReadEventProviderTest.php @@ -0,0 +1,99 @@ +provider = $this->createMock(ProviderInterface::class); + $this->operationEventDispatcher = $this->createMock(OperationEventDispatcherInterface::class); + $this->dispatchPostReadEventProvider = new DispatchPostReadEventProvider( + $this->provider, + $this->operationEventDispatcher, + ); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(DispatchPostReadEventProvider::class, $this->dispatchPostReadEventProvider); + } + + public function testItDispatchesEventsForIndexOperation(): void + { + $operation = new Index(provider: '\App\Provider'); + $context = new Context(); + $operationEvent = new OperationEvent(); + + $this->provider->expects($this->once()) + ->method('provide') + ->with($operation, $context); + + $this->operationEventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(null, $operation, $context) + ->willReturn($operationEvent); + + $this->dispatchPostReadEventProvider->provide($operation, $context); + } + + public function testItDispatchesEventsForShowOperation(): void + { + $operation = new Show(provider: '\App\Provider'); + $context = new Context(); + $operationEvent = new OperationEvent(); + + $this->provider->expects($this->once()) + ->method('provide') + ->with($operation, $context); + + $this->operationEventDispatcher->expects($this->once()) + ->method('dispatch') + ->with(null, $operation, $context) + ->willReturn($operationEvent); + + $this->dispatchPostReadEventProvider->provide($operation, $context); + } + + public function testItDoesNotDispatchEventsForCreateOperation(): void + { + $operation = new Create(provider: '\App\Provider'); + $context = new Context(); + + $this->provider->expects($this->once()) + ->method('provide') + ->with($operation, $context); + + $this->operationEventDispatcher->expects($this->never()) + ->method('dispatch'); + + $this->dispatchPostReadEventProvider->provide($operation, $context); + } +} diff --git a/src/Component/tests/Symfony/ExpressionLanguage/ArgumentParserTest.php b/src/Component/tests/Symfony/ExpressionLanguage/ArgumentParserTest.php index d62a1ed11..c57295599 100644 --- a/src/Component/tests/Symfony/ExpressionLanguage/ArgumentParserTest.php +++ b/src/Component/tests/Symfony/ExpressionLanguage/ArgumentParserTest.php @@ -11,53 +11,45 @@ declare(strict_types=1); -namespace Sylius\Component\Resource\tests\Symfony\ExpressionLanguage; +namespace Sylius\Resource\Tests\Symfony\ExpressionLanguage; -use Sylius\Resource\Symfony\ExpressionLanguage\ArgumentParserInterface; -use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use PHPUnit\Framework\TestCase; +use Sylius\Resource\Symfony\ExpressionLanguage\ArgumentParser; +use Sylius\Resource\Symfony\ExpressionLanguage\VariablesCollectionInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -final class ArgumentParserTest extends KernelTestCase +final class ArgumentParserTest extends TestCase { - public function testResourceFactoryArgumentParser(): void - { - self::bootKernel(); - - $container = static::getContainer(); + private ArgumentParser $argumentParser; - /** @var ArgumentParserInterface $argumentParser */ - $argumentParser = $container->get('sylius.expression_language.argument_parser.factory'); + private VariablesCollectionInterface $variablesCollection; - $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); - $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); - $this->assertTrue($argumentParser->parseExpression('user === null')); - $this->assertTrue($argumentParser->parseExpression('request === null')); + protected function setUp(): void + { + $this->variablesCollection = $this->createMock(VariablesCollectionInterface::class); + $this->argumentParser = new ArgumentParser(new ExpressionLanguage(), $this->variablesCollection); } - public function testRepositoryArgumentParser(): void + public function testItIsInitializable(): void { - self::bootKernel(); + $this->assertInstanceOf(ArgumentParser::class, $this->argumentParser); + } - $container = static::getContainer(); + public function testItParsesExpressions(): void + { + $this->variablesCollection->method('getVariables')->willReturn(['foo' => 'fighters']); - /** @var ArgumentParserInterface $argumentParser */ - $argumentParser = $container->get('sylius.expression_language.argument_parser.repository'); + $result = $this->argumentParser->parseExpression('foo'); - $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); - $this->assertTrue($argumentParser->parseExpression('token.getUser() === null')); - $this->assertTrue($argumentParser->parseExpression('user === null')); - $this->assertTrue($argumentParser->parseExpression('request === null')); + $this->assertSame('fighters', $result); } - public function testRoutingArgumentParser(): void + public function testItMergesVariables(): void { - self::bootKernel(); - - $container = static::getContainer(); + $this->variablesCollection->method('getVariables')->willReturn(['foo' => 'fighters']); - /** @var ArgumentParserInterface $argumentParser */ - $argumentParser = $container->get('sylius.expression_language.argument_parser.routing'); - $this->assertTrue($argumentParser->parseExpression('request === null')); + $result = $this->argumentParser->parseExpression('foo', ['foo' => 'bar']); - $this->assertInstanceOf(ArgumentParserInterface::class, $argumentParser); + $this->assertSame('bar', $result); } } diff --git a/src/Component/tests/Symfony/ExpressionLanguage/RequestVariablesTest.php b/src/Component/tests/Symfony/ExpressionLanguage/RequestVariablesTest.php new file mode 100644 index 000000000..2c6e3a5da --- /dev/null +++ b/src/Component/tests/Symfony/ExpressionLanguage/RequestVariablesTest.php @@ -0,0 +1,48 @@ +requestStack = $this->createMock(RequestStack::class); + $this->requestVariables = new RequestVariables($this->requestStack); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(RequestVariables::class, $this->requestVariables); + } + + public function testItReturnsRequestVars(): void + { + $request = new Request(); + + $this->requestStack->method('getCurrentRequest')->willReturn($request); + + $result = $this->requestVariables->getVariables(); + + $this->assertSame(['request' => $request], $result); + } +} diff --git a/src/Component/tests/Symfony/ExpressionLanguage/TokenVariablesTest.php b/src/Component/tests/Symfony/ExpressionLanguage/TokenVariablesTest.php new file mode 100644 index 000000000..c60649c32 --- /dev/null +++ b/src/Component/tests/Symfony/ExpressionLanguage/TokenVariablesTest.php @@ -0,0 +1,82 @@ +tokenStorage = $this->createMock(TokenStorageInterface::class); + $this->tokenVariables = new TokenVariables($this->tokenStorage); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(TokenVariables::class, $this->tokenVariables); + } + + public function testItReturnsTokenAndUserVars(): void + { + $token = $this->createMock(TokenInterface::class); + $user = $this->createMock(UserInterface::class); + + $this->tokenStorage->method('getToken')->willReturn($token); + $token->method('getUser')->willReturn($user); + + $this->assertSame([ + 'token' => $token, + 'user' => $user, + ], $this->tokenVariables->getVariables()); + } + + public function testItReturnsANullTokenIfThereIsNoTokenOnStorage(): void + { + $this->tokenStorage->method('getToken')->willReturn(null); + + $this->assertInstanceOf(NullToken::class, $this->tokenVariables->getVariables()['token']); + } + + public function testItCanReturnNullAsUser(): void + { + $token = $this->createMock(TokenInterface::class); + + $this->tokenStorage->method('getToken')->willReturn($token); + $token->method('getUser')->willReturn(null); + + $this->assertSame([ + 'token' => $token, + 'user' => null, + ], $this->tokenVariables->getVariables()); + } + + public function testItThrowsAnExceptionWhenThereIsNoTokenStorage(): void + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The "symfony/security-bundle" must be installed and configured to use the "token" & "user" attribute. Try running "composer require symfony/security-bundle"'); + + $tokenVariables = new TokenVariables(null); + $tokenVariables->getVariables(); + } +} diff --git a/src/Component/tests/Symfony/ExpressionLanguage/VariablesCollectionTest.php b/src/Component/tests/Symfony/ExpressionLanguage/VariablesCollectionTest.php new file mode 100644 index 000000000..d67141bdf --- /dev/null +++ b/src/Component/tests/Symfony/ExpressionLanguage/VariablesCollectionTest.php @@ -0,0 +1,55 @@ +createMock(VariablesInterface::class); + $secondVariables = $this->createMock(VariablesInterface::class); + + $this->variablesCollection = new VariablesCollection([$firstVariables, $secondVariables]); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(VariablesCollection::class, $this->variablesCollection); + } + + public function testItMergesVariables(): void + { + $firstVariables = $this->createMock(VariablesInterface::class); + $secondVariables = $this->createMock(VariablesInterface::class); + + $firstVariables->method('getVariables')->willReturn(['foo' => 'bar', 'user' => '123']); + $secondVariables->method('getVariables')->willReturn(['foo' => 'fighters', 'value' => 'xyz']); + + $this->variablesCollection = new VariablesCollection([$firstVariables, $secondVariables]); + + $result = $this->variablesCollection->getVariables(); + + $this->assertSame([ + 'foo' => 'fighters', + 'user' => '123', + 'value' => 'xyz', + ], $result); + } +} diff --git a/src/Component/tests/Symfony/Form/Factory/FormFactoryTest.php b/src/Component/tests/Symfony/Form/Factory/FormFactoryTest.php new file mode 100644 index 000000000..47dad0a57 --- /dev/null +++ b/src/Component/tests/Symfony/Form/Factory/FormFactoryTest.php @@ -0,0 +1,88 @@ +formFactory = $this->createMock(SymfonyFormFactoryInterface::class); + $this->formFactoryInstance = new FormFactory($this->formFactory); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(FormFactory::class, $this->formFactoryInstance); + } + + public function testItCreatesAForm(): void + { + $operation = $this->createMock(Operation::class); + $form = $this->createMock(FormInterface::class); + + $operation->method('getFormType')->willReturn('App\Form\DummyType'); + $operation->method('getFormOptions')->willReturn(['foo' => 'fighters']); + + $this->formFactory->method('createNamed') + ->with('', 'App\Form\DummyType', null, ['foo' => 'fighters', 'csrf_protection' => false]) + ->willReturn($form); + + $this->assertSame($form, $this->formFactoryInstance->create($operation, new Context())); + } + + public function testItCreatesAFormForHtmlRequest(): void + { + $operation = $this->createMock(Operation::class); + $form = $this->createMock(FormInterface::class); + $request = $this->createMock(Request::class); + + $operation->method('getFormType')->willReturn('App\Form\DummyType'); + $operation->method('getFormOptions')->willReturn(['foo' => 'fighters']); + + $request->method('getRequestFormat')->willReturn('html'); + + $this->formFactory->method('create') + ->with('App\Form\DummyType', null, ['foo' => 'fighters']) + ->willReturn($form); + + $this->assertSame($form, $this->formFactoryInstance->create($operation, new Context(new RequestOption($request)))); + } + + public function testItThrowsAnExceptionWhenOperationHasNoFormType(): void + { + $operation = $this->createMock(Operation::class); + + $operation->method('getFormType')->willReturn(null); + $operation->method('getFormOptions')->willReturn([]); + $operation->method('getName')->willReturn('app_dummy_create'); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Operation "app_dummy_create" has no configured form type.'); + + $this->formFactoryInstance->create($operation, new Context()); + } +} diff --git a/src/Component/tests/Symfony/Request/RepositoryArgumentResolverTest.php b/src/Component/tests/Symfony/Request/RepositoryArgumentResolverTest.php new file mode 100644 index 000000000..f9407899f --- /dev/null +++ b/src/Component/tests/Symfony/Request/RepositoryArgumentResolverTest.php @@ -0,0 +1,122 @@ +repositoryArgumentResolver = new RepositoryArgumentResolver(); + } + + public function testIsInitializable(): void + { + $this->assertInstanceOf(RepositoryArgumentResolver::class, $this->repositoryArgumentResolver); + } + + public function testGetsArgumentsToSendToTheRepository(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $request->attributes = $attributes; + $request->query = new InputBag([]); + $request->request = new InputBag([]); + + $attributes->method('all')->with('_route_params')->willReturn(['id' => 'my_id']); + + $callable = [RepositoryWithCallables::class, 'find']; + $reflector = CallableReflection::from($callable); + + $this->assertSame(['id' => 'my_id'], $this->repositoryArgumentResolver->getArguments($request, $reflector)); + } + + public function testUsesQueryParamsWhenRouteParamsAreNotMatching(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $request->attributes = $attributes; + $request->query = new InputBag(['id' => 'my_id']); + $request->request = new InputBag([]); + + $attributes->method('all')->with('_route_params')->willReturn(['_sylius' => ['resource' => 'app.dummy']]); + + $callable = [RepositoryWithCallables::class, 'find']; + $reflector = CallableReflection::from($callable); + + $this->assertSame(['id' => 'my_id'], $this->repositoryArgumentResolver->getArguments($request, $reflector)); + } + + public function testUsesRequestParamsWhenRouteParamsAreNotMatching(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $request->attributes = $attributes; + $request->query = new InputBag([]); + $request->request = new InputBag(['id' => 'my_id']); + + $attributes->method('all')->with('_route_params')->willReturn(['_sylius' => ['resource' => 'app.dummy']]); + + $callable = [RepositoryWithCallables::class, 'find']; + $reflector = CallableReflection::from($callable); + + $this->assertSame(['id' => 'my_id'], $this->repositoryArgumentResolver->getArguments($request, $reflector)); + } + + public function testEncapsulatesArgumentsWhenTheMethodHasOnlyOneRequiredArrayArgument(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $request->attributes = $attributes; + $request->query = new InputBag([]); + $request->request = new InputBag([]); + + $attributes->method('all')->with('_route_params')->willReturn(['enabled' => 'true', 'author' => 'author@example.com']); + + $callable = [RepositoryWithCallables::class, 'findOneBy']; + $reflector = CallableReflection::from($callable); + + $this->assertSame([['enabled' => 'true', 'author' => 'author@example.com']], $this->repositoryArgumentResolver->getArguments($request, $reflector)); + } + + public function testReturnArrayValuesWhenMethodIsMagic(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $request->attributes = $attributes; + $request->query = new InputBag([]); + $request->request = new InputBag(['ids' => ['first_id', 'second_id']]); + + $attributes->method('all')->with('_route_params')->willReturn(['_sylius' => ['resource' => 'app.dummy']]); + + $callable = [new RepositoryWithCallables(), '__call']; + $reflector = CallableReflection::from($callable); + + $this->assertSame([['first_id', 'second_id']], $this->repositoryArgumentResolver->getArguments($request, $reflector)); + } +} diff --git a/src/Component/tests/Symfony/Request/State/ApiResponderTest.php b/src/Component/tests/Symfony/Request/State/ApiResponderTest.php new file mode 100644 index 000000000..f0dd26248 --- /dev/null +++ b/src/Component/tests/Symfony/Request/State/ApiResponderTest.php @@ -0,0 +1,134 @@ +apiResponder = new ApiResponder(new ApiHeadersInitiator()); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(ApiResponder::class, $this->apiResponder); + } + + public function testItReturnsAResponseWithHttpCreatedForResourceCreate(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $context = new Context(new RequestOption($request)); + + $request->attributes = $attributes; + + $request->method('getRequestFormat')->willReturn('json'); + $request->method('getMimeType')->with('json')->willReturn('application/json'); + + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(true); + $attributes->method('get')->with('form')->willReturn(null); + + $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); + $operation = (new Create())->withResource($resource); + + $response = $this->apiResponder->respond('serialized_data', $operation, $context); + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(Response::HTTP_CREATED, $response->getStatusCode()); + } + + public function testItReturnsAResponseWithHttpNoContentForResourceUpdate(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $context = new Context(new RequestOption($request)); + + $request->attributes = $attributes; + + $request->method('getRequestFormat')->willReturn('json'); + $request->method('getMimeType')->with('json')->willReturn('application/json'); + + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(true); + $attributes->method('get')->with('form')->willReturn(null); + + $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); + $operation = (new Update())->withResource($resource); + + $response = $this->apiResponder->respond('serialized_data', $operation, $context); + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); + } + + public function testItReturnsAResponseWithHttpNoContentForResourceDelete(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $context = new Context(new RequestOption($request)); + + $request->attributes = $attributes; + + $request->method('getRequestFormat')->willReturn('json'); + $request->method('getMimeType')->with('json')->willReturn('application/json'); + + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(true); + $attributes->method('get')->with('form')->willReturn(null); + + $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); + $operation = (new Delete())->withResource($resource); + + $response = $this->apiResponder->respond('serialized_data', $operation, $context); + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); + } + + public function testItReturnsAResponseWithHttpUnprocessableEntityForInvalidResource(): void + { + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + + $context = new Context(new RequestOption($request)); + + $request->attributes = $attributes; + + $request->method('getRequestFormat')->willReturn('json'); + $request->method('getMimeType')->with('json')->willReturn('application/json'); + + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(false); + $attributes->method('get')->with('form')->willReturn(null); + + $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); + $operation = (new Create())->withResource($resource); + + $response = $this->apiResponder->respond('serialized_data', $operation, $context); + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode()); + } +} diff --git a/src/Component/tests/Symfony/Request/State/ProviderTest.php b/src/Component/tests/Symfony/Request/State/ProviderTest.php new file mode 100644 index 000000000..8ceac5baf --- /dev/null +++ b/src/Component/tests/Symfony/Request/State/ProviderTest.php @@ -0,0 +1,181 @@ +locator = $this->createMock(ContainerInterface::class); + $this->argumentParser = $this->createMock(ArgumentParserInterface::class); + $this->provider = new Provider($this->locator, new RepositoryArgumentResolver(), $this->argumentParser); + } + + public function testIsInitializable(): void + { + $this->assertInstanceOf(Provider::class, $this->provider); + } + + public function testCallsRepositoryAsCallable(): void + { + $operation = $this->createMock(Operation::class); + $request = $this->createMock(Request::class); + + $operation->method('getRepository')->willReturn([RepositoryWithCallables::class, 'find']); + $operation->method('getRepositoryArguments')->willReturn(null); + + $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id']]); + $request->query = new InputBag([]); + $request->request = new InputBag(); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertInstanceOf(\stdClass::class, $response); + $this->assertSame('my_id', $response->id); + } + + public function testCallsRepositoryAsString(): void + { + $operation = $this->createMock(Operation::class); + $request = $this->createMock(Request::class); + $repository = $this->createMock(RepositoryInterface::class); + $stdClass = new \stdClass(); + + $operation->method('getRepository')->willReturn('App\Repository'); + $operation->method('getRepositoryMethod')->willReturn(null); + $operation->method('getRepositoryArguments')->willReturn(null); + + $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); + $request->query = new InputBag([]); + $request->request = new InputBag(); + + $this->locator->method('has')->with('App\Repository')->willReturn(true); + $this->locator->method('get')->with('App\Repository')->willReturn($repository); + + $repository->method('findOneBy')->with(['id' => 'my_id'])->willReturn($stdClass); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertSame($stdClass, $response); + } + + public function testCallsCreatePaginatorByDefaultOnCollectionOperations(): void + { + $request = $this->createMock(Request::class); + $operation = new Index(repository: 'App\Repository'); + $repository = $this->createMock(RepositoryInterface::class); + $pagerfanta = $this->createMock(Pagerfanta::class); + + $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); + $request->query = new InputBag([]); + $request->request = new InputBag(); + + $this->locator->method('has')->with('App\Repository')->willReturn(true); + $this->locator->method('get')->with('App\Repository')->willReturn($repository); + + $repository->method('createPaginator')->willReturn($pagerfanta); + $pagerfanta->expects($this->once())->method('setCurrentPage')->with(1)->willReturn($pagerfanta); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertSame($pagerfanta, $response); + } + + public function testSetsCurrentPageFromRequestWhenDataIsAPaginator(): void + { + $request = $this->createMock(Request::class); + $operation = new Index(repository: 'App\Repository'); + $repository = $this->createMock(RepositoryInterface::class); + $pagerfanta = $this->createMock(Pagerfanta::class); + + $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); + $request->query = new InputBag(['page' => 42]); + $request->request = new InputBag(); + + $this->locator->method('has')->with('App\Repository')->willReturn(true); + $this->locator->method('get')->with('App\Repository')->willReturn($repository); + + $repository->method('createPaginator')->willReturn($pagerfanta); + $pagerfanta->expects($this->once())->method('setCurrentPage')->with(42)->willReturn($pagerfanta); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertSame($pagerfanta, $response); + $pagerfanta->method('getCurrentPage')->willReturn(42); + } + + public function testCallsRepositoryAsStringWithSpecificRepositoryMethod(): void + { + $operation = $this->createMock(Operation::class); + $request = $this->createMock(Request::class); + $repository = $this->createMock(RepositoryInterface::class); + $stdClass = new \stdClass(); + + $operation->method('getRepository')->willReturn('App\Repository'); + $operation->method('getRepositoryMethod')->willReturn('find'); + $operation->method('getRepositoryArguments')->willReturn(null); + + $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); + $request->query = new InputBag([]); + $request->request = new InputBag(); + + $this->locator->method('has')->with('App\Repository')->willReturn(true); + $this->locator->method('get')->with('App\Repository')->willReturn($repository); + + $repository->method('find')->with('my_id')->willReturn($stdClass); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertSame($stdClass, $response); + } + + public function testCallsRepositoryAsStringWithSpecificRepositoryMethodAndArguments(): void + { + $operation = $this->createMock(Operation::class); + $request = $this->createMock(Request::class); + $repository = $this->createMock(RepositoryInterface::class); + $stdClass = new \stdClass(); + + $operation->method('getRepository')->willReturn('App\Repository'); + $operation->method('getRepositoryMethod')->willReturn('find'); + $operation->method('getRepositoryArguments')->willReturn(['id' => "request.attributes.get('id')"]); + + $this->argumentParser->method('parseExpression')->with("request.attributes.get('id')")->willReturn('my_id'); + + $this->locator->method('has')->with('App\Repository')->willReturn(true); + $this->locator->method('get')->with('App\Repository')->willReturn($repository); + + $repository->method('find')->with('my_id')->willReturn($stdClass); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertSame($stdClass, $response); + } +} diff --git a/src/Component/tests/Symfony/Request/State/ResponderTest.php b/src/Component/tests/Symfony/Request/State/ResponderTest.php new file mode 100644 index 000000000..e66b345d9 --- /dev/null +++ b/src/Component/tests/Symfony/Request/State/ResponderTest.php @@ -0,0 +1,114 @@ +locator = $this->createMock(ContainerInterface::class); + $this->responder = new Responder($this->locator); + } + + public function testIsInitializable(): void + { + $this->assertInstanceOf(Responder::class, $this->responder); + } + + public function testUsesHtmlResponderOnHtmlFormat(): void + { + $data = new \stdClass(); + $request = $this->createMock(Request::class); + $operation = $this->createMock(HttpOperation::class); + $htmlResponder = $this->createMock(ResponderInterface::class); + $response = $this->createMock(Response::class); + $context = new Context(new RequestOption($request)); + + $request->method('getRequestFormat')->willReturn('html'); + + $this->locator->method('has')->with('sylius.state_responder.html')->willReturn(true); + $this->locator->method('get')->with('sylius.state_responder.html')->willReturn($htmlResponder); + + $htmlResponder->expects($this->once())->method('respond')->with($data, $operation, $context)->willReturn($response); + + $this->responder->respond($data, $operation, $context); + } + + public function testUsesApiResponderOnJsonFormat(): void + { + $data = new \stdClass(); + $request = $this->createMock(Request::class); + $operation = $this->createMock(HttpOperation::class); + $apiResponder = $this->createMock(ResponderInterface::class); + $response = $this->createMock(Response::class); + $context = new Context(new RequestOption($request)); + + $request->method('getRequestFormat')->willReturn('json'); + + $this->locator->method('has')->with('sylius.state_responder.api')->willReturn(true); + $this->locator->method('get')->with('sylius.state_responder.api')->willReturn($apiResponder); + + $apiResponder->expects($this->once())->method('respond')->with($data, $operation, $context)->willReturn($response); + + $this->responder->respond($data, $operation, $context); + } + + public function testThrowExceptionWhenHtmlResponderWasNotFound(): void + { + $data = new \stdClass(); + $request = $this->createMock(Request::class); + $operation = $this->createMock(HttpOperation::class); + $context = new Context(new RequestOption($request)); + + $request->method('getRequestFormat')->willReturn('html'); + + $this->locator->method('has')->with('sylius.state_responder.html')->willReturn(false); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Responder "sylius.state_responder.html" was not found but it should.'); + + $this->responder->respond($data, $operation, $context); + } + + public function testThrowExceptionWhenJsonResponderWasNotFound(): void + { + $data = new \stdClass(); + $request = $this->createMock(Request::class); + $operation = $this->createMock(HttpOperation::class); + $context = new Context(new RequestOption($request)); + + $request->method('getRequestFormat')->willReturn('json'); + + $this->locator->method('has')->with('sylius.state_responder.api')->willReturn(false); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Responder "sylius.state_responder.api" was not found but it should.'); + + $this->responder->respond($data, $operation, $context); + } +} diff --git a/src/Component/tests/Symfony/Request/State/TwigResponderTest.php b/src/Component/tests/Symfony/Request/State/TwigResponderTest.php new file mode 100644 index 000000000..decd6666b --- /dev/null +++ b/src/Component/tests/Symfony/Request/State/TwigResponderTest.php @@ -0,0 +1,141 @@ +redirectHandler = $this->createMock(RedirectHandlerInterface::class); + $this->contextFactory = $this->createMock(ContextFactoryInterface::class); + $this->twig = $this->createMock(Environment::class); + $this->twigResponder = new TwigResponder($this->redirectHandler, $this->contextFactory, $this->twig); + } + + public function testIsInitializable(): void + { + $this->assertInstanceOf(TwigResponder::class, $this->twigResponder); + } + + public function testReturnsAResponseForResourceShow(): void + { + $data = new \stdClass(); + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + $context = new Context(new RequestOption($request)); + + $request->attributes = $attributes; + $request->method('isMethodSafe')->willReturn(true); + + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(false); + $attributes->method('get')->with('form')->willReturn(null); + + $resource = new ResourceMetadata(alias: 'app.book', name: 'book'); + $operation = (new Show(template: 'book/show.html.twig'))->withResource($resource); + + $this->contextFactory->method('create')->with($data, $operation, $context)->willReturn(['book' => $data]); + $this->twig->method('render')->with('book/show.html.twig', ['book' => $data])->willReturn('result'); + + $response = $this->twigResponder->respond($data, $operation, $context); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testReturnsAResponseForResourceIndex(): void + { + $data = new \ArrayObject(); + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + $context = new Context(new RequestOption($request)); + + $request->attributes = $attributes; + $request->method('isMethodSafe')->willReturn(true); + + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(true); + $attributes->method('get')->with('form')->willReturn(null); + + $resource = new ResourceMetadata(alias: 'app.book', pluralName: 'books'); + $operation = (new Index(template: 'book/index.html.twig'))->withResource($resource); + + $this->contextFactory->method('create')->with($data, $operation, $context)->willReturn(['books' => $data]); + $this->twig->method('render')->with('book/index.html.twig', ['books' => $data])->willReturn('result'); + + $response = $this->twigResponder->respond($data, $operation, $context); + $this->assertNotNull($response); + } + + public function testRedirectToRouteAfterCreation(): void + { + $data = new \ArrayObject(); + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + $response = $this->createMock(RedirectResponse::class); + + $data->offsetSet('id', 'xyz'); + $request->attributes = $attributes; + + $request->method('isMethodSafe')->willReturn(false); + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(true); + + $operation = new Create(); + $this->redirectHandler->method('redirectToResource')->with($data, $operation, $request)->willReturn($response); + + $result = $this->twigResponder->respond($data, $operation, new Context(new RequestOption($request))); + $this->assertSame($response, $result); + } + + public function testResponseIsUnprocessableWhenValidationHasFailed(): void + { + $data = new \ArrayObject(); + $request = $this->createMock(Request::class); + $attributes = $this->createMock(ParameterBag::class); + $context = new Context(new RequestOption($request)); + + $data->offsetSet('id', 'xyz'); + $request->attributes = $attributes; + + $request->method('isMethodSafe')->willReturn(false); + $attributes->method('getBoolean')->with('is_valid', true)->willReturn(false); + + $operation = new Create(); + + $this->contextFactory->method('create')->with($data, $operation, $context)->willReturn(['books' => $data]); + $this->twig->method('render')->willReturn('twig_content'); + + $response = $this->twigResponder->respond($data, $operation, new Context(new RequestOption($request))); + $this->assertEquals(422, $response->getStatusCode()); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/OperationRouteFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/OperationRouteFactoryTest.php new file mode 100644 index 000000000..25157cd2b --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/OperationRouteFactoryTest.php @@ -0,0 +1,243 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->operationRouteFactory = new OperationRouteFactory($this->routePathFactory); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(OperationRouteFactory::class, $this->operationRouteFactory); + } + + public function testItGeneratesCreateRoutes(): void + { + $operation = new Create(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies/new'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/new', $route->getPath()); + $this->assertSame(['GET', 'POST'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesIndexRoutes(): void + { + $operation = new Index(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies', $route->getPath()); + $this->assertSame(['GET'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesShowRoutes(): void + { + $operation = new Show(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies/{id}'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/{id}', $route->getPath()); + $this->assertSame(['GET'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesUpdateRoutes(): void + { + $operation = new Update(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies/{id}/edit'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/{id}/edit', $route->getPath()); + $this->assertSame(['GET', 'PUT', 'POST'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesDeleteRoutes(): void + { + $operation = new Delete(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies/{id}'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/{id}', $route->getPath()); + $this->assertSame(['DELETE', 'POST'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesBulkDeleteRoutes(): void + { + $operation = new BulkDelete(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies/bulk_delete'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/bulk_delete', $route->getPath()); + $this->assertSame(['DELETE', 'POST'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesBulkUpdateRoutes(): void + { + $operation = new BulkUpdate(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('dummies/bulk_update'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/bulk_update', $route->getPath()); + $this->assertSame(['PUT', 'PATCH'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesCustomOperationsRoutes(): void + { + $operation = new HttpOperation(methods: ['PATCH'], path: 'dummies/{id}/custom'); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->never()) + ->method('createRoutePath'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata('app.dummy'), $operation); + + $this->assertSame('/dummies/{id}/custom', $route->getPath()); + $this->assertSame(['PATCH'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + ], + ], $route->getDefaults()); + } + + public function testItGeneratesRoutesWithSections(): void + { + $operation = new Show(); + $metadata = Metadata::fromAliasAndConfiguration('app.dummy', ['driver' => 'dummy_driver']); + + $this->routePathFactory->expects($this->once()) + ->method('createRoutePath') + ->with($operation, 'dummies') + ->willReturn('/dummies/{id}'); + + $route = $this->operationRouteFactory->create($metadata, new ResourceMetadata(alias: 'app.dummy', section: 'admin'), $operation); + + $this->assertSame('/dummies/{id}', $route->getPath()); + $this->assertSame(['GET'], $route->getMethods()); + $this->assertSame([ + '_controller' => 'sylius.main_controller', + '_sylius' => [ + 'resource' => 'app.dummy', + 'section' => 'admin', + ], + ], $route->getDefaults()); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RouteName/OperationRouteNameFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RouteName/OperationRouteNameFactoryTest.php new file mode 100644 index 000000000..5f83dfad1 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RouteName/OperationRouteNameFactoryTest.php @@ -0,0 +1,61 @@ +operationRouteNameFactory = new OperationRouteNameFactory(); + } + + public function testIsInitializable(): void + { + $this->assertInstanceOf(OperationRouteNameFactory::class, $this->operationRouteNameFactory); + } + + public function testCreateRouteName(): void + { + $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); + $operation = (new Create())->withResource($resource); + + $this->assertSame('app_book_create', $this->operationRouteNameFactory->createRouteName($operation)); + } + + public function testCreateRouteNameWithASection(): void + { + $resource = new ResourceMetadata(alias: 'app.book', section: 'admin', name: 'book', applicationName: 'app'); + $operation = (new Create())->withResource($resource); + + $this->assertSame('app_admin_book_create', $this->operationRouteNameFactory->createRouteName($operation)); + } + + public function testThrowsExceptionWhenOperationHasNoResource(): void + { + $operation = new Create(); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('No resource was found on the operation "create"'); + + $this->operationRouteNameFactory->createRouteName($operation); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactoryTest.php new file mode 100644 index 000000000..ef93b7e41 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RoutePath/BulkOperationRoutePathFactoryTest.php @@ -0,0 +1,51 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->bulkOperationRoutePathFactory = new BulkOperationRoutePathFactory($this->routePathFactory); + } + + public function testItGeneratesRoutePathForBulkDeleteOperations(): void + { + $operation = new BulkDelete(); + + $result = $this->bulkOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/bulk_delete', $result); + } + + public function testItGeneratesRoutePathForBulkUpdateOperations(): void + { + $operation = new BulkUpdate(); + + $result = $this->bulkOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/bulk_update', $result); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactoryTest.php new file mode 100644 index 000000000..7ed17cac2 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RoutePath/CollectionOperationRoutePathFactoryTest.php @@ -0,0 +1,60 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->collectionOperationRoutePathFactory = new CollectionOperationRoutePathFactory($this->routePathFactory); + } + + public function testItGeneratesRoutePathForIndexOperations(): void + { + $operation = new Index(); + + $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies', $result); + } + + public function testItGeneratesRoutePathForIndexOperationsWithCustomShortName(): void + { + $operation = new Index(shortName: 'list'); + + $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/list', $result); + } + + public function testItGeneratesRoutePathForApiGetCollectionOperations(): void + { + $operation = new Api\GetCollection(); + + $result = $this->collectionOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies', $result); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactoryTest.php new file mode 100644 index 000000000..d1ed14a69 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RoutePath/CreateOperationRoutePathFactoryTest.php @@ -0,0 +1,60 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->createOperationRoutePathFactory = new CreateOperationRoutePathFactory($this->routePathFactory); + } + + public function testItGeneratesRoutePathForCreateOperations(): void + { + $operation = new Create(); + + $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/new', $result); + } + + public function testItGeneratesRoutePathForCreateOperationsWithCustomShortName(): void + { + $operation = new Create(shortName: 'register'); + + $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/register', $result); + } + + public function testItGeneratesRoutePathForApiPostOperations(): void + { + $operation = new Api\Post(); + + $result = $this->createOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies', $result); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactoryTest.php new file mode 100644 index 000000000..913712d89 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RoutePath/DeleteOperationRoutePathFactoryTest.php @@ -0,0 +1,79 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->deleteOperationRoutePathFactory = new DeleteOperationRoutePathFactory($this->routePathFactory); + } + + public function testItGeneratesRoutePathForDeleteOperations(): void + { + $operation = new Delete(); + + $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}/delete', $result); + } + + public function testItGeneratesRoutePathForDeleteOperationsWithCustomIdentifier(): void + { + $operation = (new Delete())->withResource(new ResourceMetadata(identifier: 'code')); + + $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{code}/delete', $result); + } + + public function testItGeneratesRoutePathForUpdateOperationsWithCustomShortName(): void + { + $operation = new Delete(shortName: 'remove'); + + $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}/remove', $result); + } + + public function testItGeneratesRoutePathForApiDeleteOperations(): void + { + $operation = new Api\Delete(); + + $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}', $result); + } + + public function testItGeneratesRoutePathForApiDeleteOperationsWithCustomShortName(): void + { + $operation = new Api\Delete(shortName: 'remove'); + + $result = $this->deleteOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}/remove', $result); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactoryTest.php new file mode 100644 index 000000000..e83fdac02 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RoutePath/ShowOperationRoutePathFactoryTest.php @@ -0,0 +1,70 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->showOperationRoutePathFactory = new ShowOperationRoutePathFactory($this->routePathFactory); + } + + public function testItGeneratesRoutePathForShowOperations(): void + { + $operation = new Show(); + + $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}', $result); + } + + public function testItGeneratesRoutePathForShowOperationsWithCustomIdentifier(): void + { + $operation = (new Show())->withResource(new ResourceMetadata(identifier: 'code')); + + $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{code}', $result); + } + + public function testItGeneratesRoutePathForShowOperationsWithCustomShortName(): void + { + $operation = new Show(shortName: 'details'); + + $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}/details', $result); + } + + public function testItGeneratesRoutePathForApiGetOperations(): void + { + $operation = new Api\Get(); + + $result = $this->showOperationRoutePathFactory->createRoutePath($operation, '/dummies'); + + $this->assertSame('/dummies/{id}', $result); + } +} diff --git a/src/Component/tests/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactoryTest.php b/src/Component/tests/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactoryTest.php new file mode 100644 index 000000000..265129851 --- /dev/null +++ b/src/Component/tests/Symfony/Routing/Factory/RoutePath/UpdateOperationRoutePathFactoryTest.php @@ -0,0 +1,74 @@ +routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); + $this->updateOperationRoutePathFactory = new UpdateOperationRoutePathFactory($this->routePathFactory); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(UpdateOperationRoutePathFactory::class, $this->updateOperationRoutePathFactory); + } + + public function testItGeneratesRoutePathForUpdateOperations(): void + { + $operation = new Update(); + + $this->assertSame('/dummies/{id}/edit', $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies')); + } + + public function testItGeneratesRoutePathForUpdateOperationsWithCustomIdentifier(): void + { + $operation = (new Update())->withResource(new ResourceMetadata(identifier: 'code')); + + $this->assertSame('/dummies/{code}/edit', $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies')); + } + + public function testItGeneratesRoutePathForUpdateOperationsWithCustomShortName(): void + { + $operation = new Update(shortName: 'edition'); + + $this->assertSame('/dummies/{id}/edition', $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies')); + } + + public function testItGeneratesRoutePathForApiPutOperations(): void + { + $operation = new Api\Put(); + + $this->assertSame('/dummies/{id}', $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies')); + } + + public function testItGeneratesRoutePathForApiPatchOperations(): void + { + $operation = new Api\Patch(); + + $this->assertSame('/dummies/{id}', $this->updateOperationRoutePathFactory->createRoutePath($operation, '/dummies')); + } +} diff --git a/src/Component/tests/Symfony/Routing/RedirectHandlerTest.php b/src/Component/tests/Symfony/Routing/RedirectHandlerTest.php new file mode 100644 index 000000000..57082f03d --- /dev/null +++ b/src/Component/tests/Symfony/Routing/RedirectHandlerTest.php @@ -0,0 +1,216 @@ +router = $this->createMock(RouterInterface::class); + $this->argumentParser = $this->createMock(ArgumentParserInterface::class); + $this->operationRouteNameFactory = $this->createMock(OperationRouteNameFactoryInterface::class); + $this->redirectHandler = new RedirectHandler($this->router, $this->argumentParser, $this->operationRouteNameFactory); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(RedirectHandler::class, $this->redirectHandler); + } + + public function testItRedirectsToResourceWithIdArgumentByDefault(): void + { + $data = new \stdClass(); + $data->id = 'xyz'; + $operation = new Create(redirectToRoute: 'app_dummy_index'); + $resource = new ResourceMetadata(alias: 'app.book'); + $operation = $operation->withResource($resource); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_dummy_index', ['id' => 'xyz']) + ->willReturn('/dummies'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToResourceWithCustomIdentifierArgumentByDefault(): void + { + $data = new \stdClass(); + $data->code = 'xyz'; + $operation = new Create(redirectToRoute: 'app_dummy_index'); + $resource = new ResourceMetadata(alias: 'app.ok', identifier: 'code'); + $operation = $operation->withResource($resource); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_dummy_index', ['code' => 'xyz']) + ->willReturn('/dummies'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToResourceWithIdViaPropertyAccess(): void + { + $data = new BoardGameResource('uid'); + $operation = new Create(redirectToRoute: 'app_board_game_index'); + $resource = new ResourceMetadata(alias: 'app.board_game'); + $operation = $operation->withResource($resource); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_board_game_index', ['id' => 'uid']) + ->willReturn('/board-games'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToResourceWithCustomArguments(): void + { + $data = new \stdClass(); + $data->code = 'xyz'; + $operation = new Create(redirectToRoute: 'app_dummy_index', redirectArguments: ['code' => 'resource.code']); + $resource = new ResourceMetadata(alias: 'app.book'); + $operation = $operation->withResource($resource); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_dummy_index', ['code' => 'xyz']) + ->willReturn('/dummies'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToResourceWithIdViaTheGetter(): void + { + $data = new BoardGameResource('uid'); + $operation = new Create(redirectToRoute: 'app_board_game_index', redirectArguments: ['id' => 'resource.id()']); + $resource = new ResourceMetadata(alias: 'app.board_game'); + $operation = $operation->withResource($resource); + + $this->argumentParser->expects($this->once()) + ->method('parseExpression') + ->with('resource.id()', ['resource' => $data]) + ->willReturn('uid'); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_board_game_index', ['id' => 'uid']) + ->willReturn('/board-games'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToResourceWithoutArgumentsAfterDeleteOperationByDefault(): void + { + $data = new \stdClass(); + $data->id = 'xyz'; + $operation = new Delete(redirectToRoute: 'app_dummy_index'); + $resource = new ResourceMetadata(alias: 'app.book'); + $operation = $operation->withResource($resource); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_dummy_index', []) + ->willReturn('/dummies'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToResourceWithoutArgumentsAfterBulkOperationByDefault(): void + { + $data = new \stdClass(); + $data->id = 'xyz'; + $operation = new BulkUpdate(redirectToRoute: 'app_dummy_index'); + $resource = new ResourceMetadata(alias: 'app.book'); + $operation = $operation->withResource($resource); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_dummy_index', []) + ->willReturn('/dummies'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItRedirectsToRoute(): void + { + $data = new \stdClass(); + + $this->router->expects($this->once()) + ->method('generate') + ->with('app_dummy_index', []) + ->willReturn('/dummies'); + + $this->redirectHandler->redirectToRoute($data, 'app_dummy_index'); + } + + public function testItThrowsAnExceptionWhenOperationHasNoResource(): void + { + $data = new \stdClass(); + $operation = new Create(redirectToRoute: 'app_dummy_index', name: 'app_dummy_create'); + + $this->router->expects($this->never()) + ->method('generate'); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Operation "app_dummy_create" has no resource, but it should.'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } + + public function testItThrowsAnExceptionWhenOperationHasNoRouteRedirection(): void + { + $data = new \stdClass(); + $operation = new Create(name: 'app_dummy_create'); + + $this->router->expects($this->never()) + ->method('generate'); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Operation "app_dummy_create" has no redirection route, but it should.'); + + $this->redirectHandler->redirectToResource($data, $operation, new Request()); + } +} + +final class BoardGameResource +{ + public function __construct(private string $id) + { + } + + public function id(): string + { + return $this->id; + } +} diff --git a/src/Component/tests/Symfony/Validator/EventListener/ValidationExceptionListenerSpec.php b/src/Component/tests/Symfony/Validator/EventListener/ValidationExceptionListenerSpec.php new file mode 100644 index 000000000..3b5d7a67d --- /dev/null +++ b/src/Component/tests/Symfony/Validator/EventListener/ValidationExceptionListenerSpec.php @@ -0,0 +1,119 @@ +serializer = $this->createMock(SerializerInterface::class); + $this->listener = new ValidationExceptionListener($this->serializer); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(ValidationExceptionListener::class, $this->listener); + } + + public function testItTransformsValidationExceptionToAResponse(): void + { + $kernel = $this->createMock(KernelInterface::class); + $request = $this->createMock(Request::class); + $violationList = new ConstraintViolationList(); + $exception = new ValidationException($violationList); + + $event = new ExceptionEvent( + $kernel, + $request, + HttpKernelInterface::MAIN_REQUEST, + $exception, + ); + + $request->method('getRequestFormat')->willReturn('json'); + $request->method('getMimeType')->with('json')->willReturn('application/json'); + + $this->serializer->method('serialize')->with($violationList, 'json')->willReturn('serialized_exception'); + + $this->listener->onKernelException($event); + + $response = $event->getResponse(); + + $this->assertInstanceOf(Response::class, $response); + $this->assertEquals('serialized_exception', $response->getContent()); + $this->assertEquals(422, $response->getStatusCode()); + $this->assertEquals(new ResponseHeaderBag([ + 'Content-Type' => 'application/json; charset=utf-8', + 'X-Content-Type-Options' => 'nosniff', + 'X-Frame-Options' => 'deny', + ]), $response->headers); + } + + public function testItDoesNothingOnOtherExceptions(): void + { + $kernel = $this->createMock(KernelInterface::class); + $request = $this->createMock(Request::class); + $exception = $this->createMock(\Throwable::class); + + $event = new ExceptionEvent( + $kernel, + $request, + HttpKernelInterface::MAIN_REQUEST, + $exception, + ); + + $this->serializer->expects($this->never())->method('serialize'); + + $this->listener->onKernelException($event); + + $this->assertNull($event->getResponse()); + } + + public function testItThrowsAnExceptionWhenSerializerIsNotAvailable(): void + { + $this->listener = new ValidationExceptionListener(null); + $kernel = $this->createMock(KernelInterface::class); + $request = $this->createMock(Request::class); + + $violationList = new ConstraintViolationList(); + $exception = new ValidationException($violationList); + + $event = new ExceptionEvent( + $kernel, + $request, + HttpKernelInterface::MAIN_REQUEST, + $exception, + ); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The Symfony Serializer is not available. Try running "composer require symfony/serializer".'); + + $this->listener->onKernelException($event); + } +} diff --git a/src/Component/tests/Symfony/Validator/Exception/ValidationExceptionSpec.php b/src/Component/tests/Symfony/Validator/Exception/ValidationExceptionSpec.php new file mode 100644 index 000000000..9e5eba026 --- /dev/null +++ b/src/Component/tests/Symfony/Validator/Exception/ValidationExceptionSpec.php @@ -0,0 +1,84 @@ +createMock(ConstraintViolationInterface::class); + $secondViolation = $this->createMock(ConstraintViolationInterface::class); + + $violationList = new ConstraintViolationList([ + $firstViolation, + $secondViolation, + ]); + + $this->validationException = new ValidationException($violationList); + } + + public function testItTransformsExceptionIntoAString(): void + { + $firstViolation = $this->createMock(ConstraintViolationInterface::class); + $secondViolation = $this->createMock(ConstraintViolationInterface::class); + + $firstViolation->method('getPropertyPath')->willReturn('name'); + $firstViolation->method('getMessage')->willReturn('This value should not be blank.'); + + $secondViolation->method('getPropertyPath')->willReturn('email'); + $secondViolation->method('getMessage')->willReturn('This value should not be blank.'); + + $violationList = new ConstraintViolationList([ + $firstViolation, + $secondViolation, + ]); + + $this->validationException = new ValidationException($violationList); + + $this->assertEquals( + "name: This value should not be blank.\nemail: This value should not be blank.", + $this->validationException->__toString(), + ); + } + + public function testItCanBeConstructedWithAMessage(): void + { + $this->validationException = new ValidationException(new ConstraintViolationList([]), 'You should not pass!'); + + $this->assertEquals('You should not pass!', $this->validationException->getMessage()); + } + + public function testItCanBeConstructedWithACode(): void + { + $this->validationException = new ValidationException(new ConstraintViolationList([]), '', 42); + + $this->assertEquals(42, $this->validationException->getCode()); + } + + public function testItCanBeConstructedWithAPreviousException(): void + { + $previous = new \Exception(); + + $this->validationException = new ValidationException(new ConstraintViolationList([]), '', 0, $previous); + + $this->assertSame($previous, $this->validationException->getPrevious()); + } +} diff --git a/src/Component/tests/Symfony/Workflow/OperationStateMachineTest.php b/src/Component/tests/Symfony/Workflow/OperationStateMachineTest.php new file mode 100644 index 000000000..2f8b7d07d --- /dev/null +++ b/src/Component/tests/Symfony/Workflow/OperationStateMachineTest.php @@ -0,0 +1,113 @@ +createMock(Registry::class); + $this->operationStateMachine = new OperationStateMachine($registry); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(OperationStateMachine::class, $this->operationStateMachine); + } + + public function testReturnsIfTransitionIsPossible(): void + { + $data = new \stdClass(); + $operation = new Create(stateMachineTransition: 'publish'); + + $registry = $this->createMock(Registry::class); + $workflow = $this->createMock(Workflow::class); + $registry->method('get')->with($data, null)->willReturn($workflow); + $workflow->method('can')->with($data, 'publish')->willReturn(true); + + $this->operationStateMachine = new OperationStateMachine($registry); + + $this->assertTrue($this->operationStateMachine->can($data, $operation, new Context())); + } + + public function testAppliesTransition(): void + { + $data = new \stdClass(); + $operation = new Create(stateMachineTransition: 'publish'); + + $registry = $this->createMock(Registry::class); + $workflow = $this->createMock(Workflow::class); + $marking = $this->createMock(Marking::class); + + $registry->method('get')->with($data, null)->willReturn($workflow); + $workflow->expects($this->once())->method('apply')->with($data, 'publish')->willReturn($marking); + + $this->operationStateMachine = new OperationStateMachine($registry); + + $this->operationStateMachine->apply($data, $operation, new Context()); + } + + public function testThrowsExceptionWhenOperationHasNoDefinedTransition(): void + { + $data = new \stdClass(); + $operation = new Create(name: 'app_dummy_create'); + + $registry = $this->createMock(Registry::class); + $registry->method('get')->with($data, null)->willReturn($this->createMock(Workflow::class)); + + $this->operationStateMachine = new OperationStateMachine($registry); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('No State machine transition was found on operation "app_dummy_create".'); + $this->operationStateMachine->can($data, $operation, new Context()); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('No State machine transition was found on operation "app_dummy_create".'); + $this->operationStateMachine->apply($data, $operation, new Context()); + } + + public function testThrowsExceptionWhenSymfonyWorkflowIsNotAvailable(): void + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('You can not use the "state-machine" if Symfony workflow is not available. Try running "composer require symfony/workflow".'); + + $this->operationStateMachine = new OperationStateMachine(null); + $data = new \stdClass(); + $operation = new Create(stateMachineTransition: 'publish'); + $this->operationStateMachine->can($data, $operation, new Context()); + } + + public function testThrowsExceptionWhenOperationDoesNotImplementAStateMachine(): void + { + $data = new \stdClass(); + $operation = new Index(); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage(sprintf('Expected an instance of %s. Got: %s', StateMachineAwareOperationInterface::class, Index::class)); + + $this->operationStateMachine->can($data, $operation, new Context()); + } +}