From b94f121d4cf65155d664d79e57c504cdbaed9757 Mon Sep 17 00:00:00 2001 From: mamazu Date: Mon, 1 Jun 2020 20:58:26 +0200 Subject: [PATCH] Adding tests --- .../PaymentMethodAvailableValidatorSpec.php | 94 ++++++++++++++ .../Cart/PaymentMethodExistsValidatorSpec.php | 56 ++++++++ .../Checkout/ChoosePaymentMethodAction.php | 16 ++- .../config/services/actions/checkout.xml | 1 + .../config/services/validators/cart.xml | 9 +- .../ChoosePaymentMethodRequest.xml} | 13 +- src/Resources/translations/validators.en.yml | 2 + ...hp => PaymentMethodAvailableValidator.php} | 18 ++- .../Cart/PaymentMethodExistsValidator.php | 33 +++++ ...tMethod.php => PaymentMethodAvailable.php} | 4 +- .../Constraints/PaymentMethodExists.php | 19 +++ .../Checkout/ChoosePaymentMethodApiTest.php | 122 +++++++++++++++--- tests/DataFixtures/ORM/payment.yml | 12 ++ .../checkout/non_existing_payment_method.json | 10 ++ .../payment_method_not_available.json | 9 ++ 15 files changed, 386 insertions(+), 32 deletions(-) create mode 100644 spec/Validator/Cart/PaymentMethodAvailableValidatorSpec.php create mode 100644 spec/Validator/Cart/PaymentMethodExistsValidatorSpec.php rename src/Resources/config/validation/{cart/CorrectPaymentMethodSelected.xml => checkout/ChoosePaymentMethodRequest.xml} (61%) rename src/Validator/Cart/{CorrectPaymentMethodValidator.php => PaymentMethodAvailableValidator.php} (70%) create mode 100644 src/Validator/Cart/PaymentMethodExistsValidator.php rename src/Validator/Constraints/{CorrectPaymentMethod.php => PaymentMethodAvailable.php} (76%) create mode 100644 src/Validator/Constraints/PaymentMethodExists.php create mode 100644 tests/Responses/Expected/checkout/non_existing_payment_method.json create mode 100644 tests/Responses/Expected/checkout/payment_method_not_available.json diff --git a/spec/Validator/Cart/PaymentMethodAvailableValidatorSpec.php b/spec/Validator/Cart/PaymentMethodAvailableValidatorSpec.php new file mode 100644 index 000000000..186f1219f --- /dev/null +++ b/spec/Validator/Cart/PaymentMethodAvailableValidatorSpec.php @@ -0,0 +1,94 @@ +beConstructedWith($orderRepository, $paymentMethodsResolver); + $this->initialize($context); + + $request->getOrderToken()->willReturn('NON_EXISTING'); + $request->getPaymentId()->willReturn(0); + $request->getMethod()->willReturn('COD'); + } + + function it_does_not_validate_carts_if_the_order_does_not_exist( + OrderRepositoryInterface $orderRepository, + PaymentMethodsResolverInterface $paymentMethodsResolver, + ExecutionContextInterface $context, + ChoosePaymentMethodRequest $request + ): void { + $orderRepository->findOneBy(['tokenValue' => 'NON_EXISTING', 'state' => 'cart'])->willReturn(null); + $paymentMethodsResolver->getSupportedMethods(Argument::any())->shouldNotBeCalled(); + $context->buildViolation(Argument::any())->shouldNotBeCalled(); + + $this->validate($request, new PaymentMethodAvailable()); + } + + function it_adds_a_violation_if_payment_method_is_not_available( + OrderRepositoryInterface $orderRepository, + OrderInterface $cart, + PaymentInterface $payment, + ConstraintViolationBuilderInterface $violationBuilder, + PaymentMethodsResolverInterface $paymentMethodsResolver, + PaymentMethodInterface $paymentMethod, + ExecutionContextInterface $context, + ChoosePaymentMethodRequest $request + ): void { + $orderRepository->findOneBy(['tokenValue' => 'NON_EXISTING', 'state' => 'cart'])->willReturn($cart); + $cart->getPayments()->willReturn(new ArrayCollection([$payment->getWrappedObject()])); + + $paymentMethodsResolver->getSupportedMethods($payment)->shouldBeCalled()->willReturn([$paymentMethod]); + $paymentMethod->getCode()->willReturn('paypal'); + + $context->buildViolation('sylius.shop_api.checkout.payment_method_not_available')->willReturn( + $violationBuilder + ) + ; + $violationBuilder->atPath('method')->willReturn($violationBuilder); + $violationBuilder->addViolation()->shouldBeCalled(); + + $this->validate($request, new PaymentMethodAvailable()); + } + + function it_adds_no_violation_if_payment_method_is_available( + OrderRepositoryInterface $orderRepository, + OrderInterface $cart, + PaymentInterface $payment, + PaymentMethodsResolverInterface $paymentMethodsResolver, + PaymentMethodInterface $paymentMethod, + ExecutionContextInterface $context, + ChoosePaymentMethodRequest $request + ): void { + $orderRepository->findOneBy(['tokenValue' => 'NON_EXISTING', 'state' => 'cart'])->willReturn($cart); + $cart->getPayments()->willReturn(new ArrayCollection([$payment->getWrappedObject()])); + + $paymentMethodsResolver->getSupportedMethods($payment)->shouldBeCalled()->willReturn([$paymentMethod]); + $paymentMethod->getCode()->willReturn('COD'); + + $context->buildViolation(Argument::any())->shouldNotBeCalled(); + + $this->validate($request, new PaymentMethodAvailable()); + } +} diff --git a/spec/Validator/Cart/PaymentMethodExistsValidatorSpec.php b/spec/Validator/Cart/PaymentMethodExistsValidatorSpec.php new file mode 100644 index 000000000..ccc940e76 --- /dev/null +++ b/spec/Validator/Cart/PaymentMethodExistsValidatorSpec.php @@ -0,0 +1,56 @@ +beConstructedWith($paymentMethodRepository); + + $this->initialize($executionContext); + } + + function it_does_not_add_constraint_if_payment_method_exists( + OrderInterface $order, + PaymentMethodRepositoryInterface $paymentMethodRepository, + ExecutionContextInterface $executionContext + ): void { + $paymentMethodRepository->findOneBy(['code' => 'paypal'])->willReturn($order); + + $executionContext->addViolation(Argument::any(), Argument::any())->shouldNotBeCalled(); + + $this->validate('paypal', new PaymentMethodExists()); + } + + function it_adds_constraint_if_payment_method_does_not_exists( + PaymentMethodRepositoryInterface $paymentMethodRepository, + ExecutionContextInterface $executionContext + ): void { + $paymentMethodRepository->findOneBy(['code' => 'paypal'])->willReturn(null); + + $executionContext->addViolation('sylius.shop_api.checkout.payment_method_does_not_exist')->shouldBeCalled(); + + $this->validate('paypal', new PaymentMethodExists()); + } + + function it_throws_an_exception_if_constraint_is_not_payment_exists(): void + { + $this + ->shouldThrow(\InvalidArgumentException::class) + ->during('validate', ['paypal', new CartExists()]) + ; + } +} diff --git a/src/Controller/Checkout/ChoosePaymentMethodAction.php b/src/Controller/Checkout/ChoosePaymentMethodAction.php index 02bd174b8..7f10f0336 100644 --- a/src/Controller/Checkout/ChoosePaymentMethodAction.php +++ b/src/Controller/Checkout/ChoosePaymentMethodAction.php @@ -7,6 +7,7 @@ use FOS\RestBundle\View\View; use FOS\RestBundle\View\ViewHandlerInterface; use Sylius\ShopApiPlugin\CommandProvider\CommandProviderInterface; +use Sylius\ShopApiPlugin\Factory\ValidationErrorViewFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\MessageBusInterface; @@ -22,18 +23,31 @@ final class ChoosePaymentMethodAction /** @var CommandProviderInterface */ private $choosePaymentMethodCommandProvider; + /** @var ValidationErrorViewFactoryInterface */ + private $validationErrorViewFactory; + public function __construct( ViewHandlerInterface $viewHandler, MessageBusInterface $bus, - CommandProviderInterface $choosePaymentMethodCommandProvider + CommandProviderInterface $choosePaymentMethodCommandProvider, + ValidationErrorViewFactoryInterface $validationErrorViewFactory ) { $this->viewHandler = $viewHandler; $this->bus = $bus; $this->choosePaymentMethodCommandProvider = $choosePaymentMethodCommandProvider; + $this->validationErrorViewFactory = $validationErrorViewFactory; } public function __invoke(Request $request): Response { + $validationResults = $this->choosePaymentMethodCommandProvider->validate($request); + if (0 !== count($validationResults)) { + return $this->viewHandler->handle(View::create( + $this->validationErrorViewFactory->create($validationResults), + Response::HTTP_BAD_REQUEST + )); + } + $this->bus->dispatch($this->choosePaymentMethodCommandProvider->getCommand($request)); return $this->viewHandler->handle(View::create(null, Response::HTTP_NO_CONTENT)); diff --git a/src/Resources/config/services/actions/checkout.xml b/src/Resources/config/services/actions/checkout.xml index 6a2bd8096..50a082d5f 100644 --- a/src/Resources/config/services/actions/checkout.xml +++ b/src/Resources/config/services/actions/checkout.xml @@ -47,6 +47,7 @@ + + class="Sylius\ShopApiPlugin\Validator\Cart\PaymentMethodAvailableValidator"> + + + + + + diff --git a/src/Resources/config/validation/cart/CorrectPaymentMethodSelected.xml b/src/Resources/config/validation/checkout/ChoosePaymentMethodRequest.xml similarity index 61% rename from src/Resources/config/validation/cart/CorrectPaymentMethodSelected.xml rename to src/Resources/config/validation/checkout/ChoosePaymentMethodRequest.xml index 49bc5ea18..0ca272067 100644 --- a/src/Resources/config/validation/cart/CorrectPaymentMethodSelected.xml +++ b/src/Resources/config/validation/checkout/ChoosePaymentMethodRequest.xml @@ -15,6 +15,17 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd"> - + + + + + + + + + + + + diff --git a/src/Resources/translations/validators.en.yml b/src/Resources/translations/validators.en.yml index d97754b51..4da377471 100644 --- a/src/Resources/translations/validators.en.yml +++ b/src/Resources/translations/validators.en.yml @@ -10,6 +10,8 @@ sylius: address_required: Please provide the cart with a shipping and billing address shipping_required: Please select a shipping method payment_required: Please select a payment method + payment_method_does_not_exist: The selected payment method does not exist. + payment_method_not_available: The selected payment method is not available for this cart. cart_item: not_exists: Cart item does not exist. email: diff --git a/src/Validator/Cart/CorrectPaymentMethodValidator.php b/src/Validator/Cart/PaymentMethodAvailableValidator.php similarity index 70% rename from src/Validator/Cart/CorrectPaymentMethodValidator.php rename to src/Validator/Cart/PaymentMethodAvailableValidator.php index a6a774c9f..61094cefb 100644 --- a/src/Validator/Cart/CorrectPaymentMethodValidator.php +++ b/src/Validator/Cart/PaymentMethodAvailableValidator.php @@ -6,14 +6,15 @@ use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Core\OrderCheckoutStates; use Sylius\Component\Core\Repository\OrderRepositoryInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; use Sylius\ShopApiPlugin\Request\Checkout\ChoosePaymentMethodRequest; -use Sylius\ShopApiPlugin\Validator\Constraints\CorrectPaymentMethod; +use Sylius\ShopApiPlugin\Validator\Constraints\PaymentMethodAvailable; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -final class CorrectPaymentMethodValidator extends ConstraintValidator +final class PaymentMethodAvailableValidator extends ConstraintValidator { /** @var OrderRepositoryInterface */ private $orderRepository; @@ -34,7 +35,9 @@ public function validate($value, Constraint $constraint): void /** @var ChoosePaymentMethodRequest $value */ /** @var OrderInterface|null $order */ - $order = $this->orderRepository->findOneByTokenValue($value->getOrderToken()); + $order = $this->orderRepository->findOneBy( + ['tokenValue' => $value->getOrderToken(), 'state' => OrderCheckoutStates::STATE_CART] + ); if ($order === null) { return; } @@ -48,9 +51,12 @@ static function (PaymentMethodInterface $paymentMethod) { $this->paymentMethodsResolver->getSupportedMethods($payment) ); - if (!in_array($value->getMethod(), $paymentMethodCodes)) { - /** @var CorrectPaymentMethod $constraint */ - $this->context->addViolation($constraint->message); + if (!in_array($value->getMethod(), $paymentMethodCodes, true)) { + /** @var PaymentMethodAvailable $constraint */ + $this->context + ->buildViolation($constraint->message) + ->atPath('method') + ->addViolation(); } } } diff --git a/src/Validator/Cart/PaymentMethodExistsValidator.php b/src/Validator/Cart/PaymentMethodExistsValidator.php new file mode 100644 index 000000000..39d7a0142 --- /dev/null +++ b/src/Validator/Cart/PaymentMethodExistsValidator.php @@ -0,0 +1,33 @@ +paymentMethodRepository = $paymentMethodRepository; + } + + /** {@inheritdoc} */ + public function validate($value, Constraint $constraint): void + { + Assert::isInstanceOf($constraint, PaymentMethodExists::class); + + if ($this->paymentMethodRepository->findOneBy(['code' => $value]) === null) { + /** @var PaymentMethodExists $constraint */ + $this->context->addViolation($constraint->message); + } + } +} diff --git a/src/Validator/Constraints/CorrectPaymentMethod.php b/src/Validator/Constraints/PaymentMethodAvailable.php similarity index 76% rename from src/Validator/Constraints/CorrectPaymentMethod.php rename to src/Validator/Constraints/PaymentMethodAvailable.php index fc120236c..cbb482b49 100644 --- a/src/Validator/Constraints/CorrectPaymentMethod.php +++ b/src/Validator/Constraints/PaymentMethodAvailable.php @@ -6,10 +6,10 @@ use Symfony\Component\Validator\Constraint; -final class CorrectPaymentMethod extends Constraint +final class PaymentMethodAvailable extends Constraint { /** @var string */ - public $message = 'sylius.shop_api.cart.payment_method.valid'; + public $message = 'sylius.shop_api.checkout.payment_method_not_available'; /** {@inheritdoc} */ public function getTargets(): string diff --git a/src/Validator/Constraints/PaymentMethodExists.php b/src/Validator/Constraints/PaymentMethodExists.php new file mode 100644 index 000000000..e15020ecd --- /dev/null +++ b/src/Validator/Constraints/PaymentMethodExists.php @@ -0,0 +1,19 @@ +loadFixturesFromFiles(['shop.yml', 'country.yml', 'shipping.yml', 'payment.yml']); @@ -28,26 +28,16 @@ public function it_allows_to_choose_payment_method(): void $bus = $this->get('sylius_shop_api_plugin.command_bus'); $bus->dispatch(new PickupCart($token, 'WEB_GB')); $bus->dispatch(new PutSimpleItemToCart($token, 'LOGAN_MUG_CODE', 5)); - $bus->dispatch(new AddressOrder( - $token, - Address::createFromArray([ - 'firstName' => 'Sherlock', - 'lastName' => 'Holmes', - 'city' => 'London', - 'street' => 'Baker Street 221b', - 'countryCode' => 'GB', - 'postcode' => 'NWB', - 'provinceName' => 'Greater London', - ]), Address::createFromArray([ - 'firstName' => 'Sherlock', - 'lastName' => 'Holmes', - 'city' => 'London', - 'street' => 'Baker Street 221b', - 'countryCode' => 'GB', - 'postcode' => 'NWB', - 'provinceName' => 'Greater London', - ]) - )); + $sampleAddress = Address::createFromArray([ + 'firstName' => 'Sherlock', + 'lastName' => 'Holmes', + 'city' => 'London', + 'street' => 'Baker Street 221b', + 'countryCode' => 'GB', + 'postcode' => 'NWB', + 'provinceName' => 'Greater London', + ]); + $bus->dispatch(new AddressOrder($token, $sampleAddress, $sampleAddress)); $bus->dispatch(new ChooseShippingMethod($token, 0, 'DHL')); $data = @@ -69,4 +59,94 @@ public function it_allows_to_choose_payment_method(): void $response = $this->client->getResponse(); $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); } + + /** + * @test + */ + public function it_does_not_allow_to_select_a_payment_method_that_does_not_exist(): void + { + $this->loadFixturesFromFiles(['shop.yml', 'country.yml', 'shipping.yml', 'payment.yml']); + + $token = 'SDAOSLEFNWU35H3QLI5325'; + + /** @var MessageBusInterface $bus */ + $bus = $this->get('sylius_shop_api_plugin.command_bus'); + $bus->dispatch(new PickupCart($token, 'WEB_GB')); + $bus->dispatch(new PutSimpleItemToCart($token, 'LOGAN_MUG_CODE', 5)); + $sampleAddress = Address::createFromArray([ + 'firstName' => 'Sherlock', + 'lastName' => 'Holmes', + 'city' => 'London', + 'street' => 'Baker Street 221b', + 'countryCode' => 'GB', + 'postcode' => 'NWB', + 'provinceName' => 'Greater London', + ]); + $bus->dispatch(new AddressOrder($token, $sampleAddress, $sampleAddress)); + $bus->dispatch(new ChooseShippingMethod($token, 0, 'DHL')); + + $data = + <<client->request( + 'PUT', + sprintf('/shop-api/checkout/%s/payment/0', $token), + [], + [], + self::CONTENT_TYPE_HEADER, + $data + ); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'checkout/non_existing_payment_method', Response::HTTP_BAD_REQUEST); + } + + /** + * @test + */ + public function it_does_not_allow_to_select_a_payment_method_that_not_available_for_order(): void + { + $this->loadFixturesFromFiles(['shop.yml', 'country.yml', 'shipping.yml', 'payment.yml']); + + $token = 'SDAOSLEFNWU35H3QLI5325'; + + /** @var MessageBusInterface $bus */ + $bus = $this->get('sylius_shop_api_plugin.command_bus'); + $bus->dispatch(new PickupCart($token, 'WEB_GB')); + $bus->dispatch(new PutSimpleItemToCart($token, 'LOGAN_MUG_CODE', 5)); + $sampleAddress = Address::createFromArray([ + 'firstName' => 'Sherlock', + 'lastName' => 'Holmes', + 'city' => 'London', + 'street' => 'Baker Street 221b', + 'countryCode' => 'GB', + 'postcode' => 'NWB', + 'provinceName' => 'Greater London', + ]); + $bus->dispatch(new AddressOrder($token, $sampleAddress, $sampleAddress)); + $bus->dispatch(new ChooseShippingMethod($token, 0, 'DHL')); + + $data = + <<client->request( + 'PUT', + sprintf('/shop-api/checkout/%s/payment/0', $token), + [], + [], + self::CONTENT_TYPE_HEADER, + $data + ); + + $response = $this->client->getResponse(); + $this->assertResponseCode($response, Response::HTTP_BAD_REQUEST); + } } diff --git a/tests/DataFixtures/ORM/payment.yml b/tests/DataFixtures/ORM/payment.yml index 9f347b2dd..c3429e162 100644 --- a/tests/DataFixtures/ORM/payment.yml +++ b/tests/DataFixtures/ORM/payment.yml @@ -11,6 +11,12 @@ Sylius\Component\Core\Model\PaymentMethod: gatewayConfig: '@offline' currentLocale: "en_GB" channels: ["@gb_web_channel"] + not_available: + code: "NOT_AVAILABLE" + enabled: true + gatewayConfig: '@offline' + currentLocale: "en_GB" + channels: [] Sylius\Bundle\PayumBundle\Model\GatewayConfig: offline: @@ -31,3 +37,9 @@ Sylius\Component\Payment\Model\PaymentMethodTranslation: description: instructions: "Please put the money in the bag!" translatable: "@pay_by_check" + not_available_translation: + name: 'Not available' + locale: 'en_GB' + description: + instructions: "Please put the money in the bag!" + translatable: "@not_available" diff --git a/tests/Responses/Expected/checkout/non_existing_payment_method.json b/tests/Responses/Expected/checkout/non_existing_payment_method.json new file mode 100644 index 000000000..adf2bc3b6 --- /dev/null +++ b/tests/Responses/Expected/checkout/non_existing_payment_method.json @@ -0,0 +1,10 @@ +{ + "code": 400, + "message": "Validation failed", + "errors": { + "method": [ + "The selected payment method is not available for this cart.", + "The selected payment method does not exist." + ] + } +} diff --git a/tests/Responses/Expected/checkout/payment_method_not_available.json b/tests/Responses/Expected/checkout/payment_method_not_available.json new file mode 100644 index 000000000..fe95c22c9 --- /dev/null +++ b/tests/Responses/Expected/checkout/payment_method_not_available.json @@ -0,0 +1,9 @@ +{ + "code": 400, + "message": "Validation failed", + "errors": { + "method": [ + "The selected payment method is not available for this cart." + ] + } +}