From c3f12392bbceda386c64b297e78bc81c77e53e01 Mon Sep 17 00:00:00 2001 From: Lennart Tinkloh Date: Fri, 29 Nov 2024 12:06:29 +0100 Subject: [PATCH] BRAIN-43 - application tests --- .env.test | 8 + .github/workflows/php.yml | 30 ++++ composer.json | 6 +- config/services.php | 4 + config/services/braintree_test.xml | 27 ++++ infection.json5 | 7 +- phpunit.xml.dist | 4 + src/Tests/ApplicationBootstrapTrait.php | 56 +++++++ .../Gateway/BraintreeTestGatewayFactory.php | 31 ++++ src/Tests/Contract/IntegrationHelperTrait.php | 5 +- src/Tests/Contract/OrderHelperTrait.php | 137 ++++++++++++++++++ .../Contract/OrderTransactionHelperTrait.php | 8 +- .../Contract/PaymentPayActionHelperTrait.php | 18 +++ .../Braintree/BraintreeReachabilityTest.php | 19 +++ .../Payment/BraintreePaymentServiceTest.php | 37 +++++ .../Payment/BraintreePaymentServiceTest.php | 4 +- .../Payment/OrderInformationServiceTest.php | 2 +- 17 files changed, 390 insertions(+), 13 deletions(-) create mode 100644 config/services/braintree_test.xml create mode 100644 src/Tests/ApplicationBootstrapTrait.php create mode 100644 src/Tests/Braintree/Gateway/BraintreeTestGatewayFactory.php create mode 100644 tests/application/Braintree/BraintreeReachabilityTest.php create mode 100644 tests/application/Braintree/Payment/BraintreePaymentServiceTest.php diff --git a/.env.test b/.env.test index a92e5ea..ae3a6b8 100644 --- a/.env.test +++ b/.env.test @@ -2,3 +2,11 @@ KERNEL_CLASS='Swag\Braintree\Kernel' APP_SECRET='$ecretf0rt3st' SYMFONY_DEPRECATIONS_HELPER=999999 + +###> braintree/gateway ### +BRAINTREE_TEST_MERCHANT_ACCOUNT_ID='your_merchant_account_id' +BRAINTREE_TEST_ENVIRONMENT='sandbox' +BRAINTREE_TEST_MERCHANT_ID='your_merchant_id' +BRAINTREE_TEST_PUBLIC_KEY='your_public_key' +BRAINTREE_TEST_PRIVATE_KEY='your_private_key' +###< braintree/gateway ### \ No newline at end of file diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index baf4668..d3550a0 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -81,6 +81,36 @@ jobs: with: files: ./coverage.xml + phpunit-application: + runs-on: ubuntu-latest + services: + mariadb: + image: mariadb:10.5 + env: + MARIADB_ROOT_PASSWORD: swagbraintree + MYSQL_DATABASE: swagbraintree_test + ports: [ '3306:3306' ] + env: + DATABASE_URL: mysql://root:swagbraintree@127.0.0.1:3306/swagbraintree + BRAINTREE_TEST_ENVIRONMENT: sandbox + BRAINTREE_TEST_MERCHANT_ID: ${{ secrets.BRAINTREE_TEST_MERCHANT_ID }} + BRAINTREE_TEST_MERCHANT_ACCOUNT_ID: ${{ secrets.BRAINTREE_TEST_MERCHANT_ACCOUNT_ID }} + BRAINTREE_TEST_PUBLIC_KEY: ${{ secrets.BRAINTREE_TEST_PUBLIC_KEY }} + BRAINTREE_TEST_PRIVATE_KEY: ${{ secrets.BRAINTREE_TEST_PRIVATE_KEY }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-php + - name: Setup database + run: composer setup:test + - name: Run PHPUnit + continue-on-error: true + run: composer phpunit:application -- --coverage-clover=coverage.xml + - name: Codecov + if: steps.phpunit-application.outcome == 'success' && steps.phpunit-application.conclusion == 'success' + uses: codecov/codecov-action@v4 + with: + files: ./coverage.xml + infection: runs-on: ubuntu-latest services: diff --git a/composer.json b/composer.json index d82343d..f94d4d2 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,8 @@ "autoload-dev": { "psr-4": { "Swag\\Braintree\\Tests\\Unit\\": "tests/unit/", - "Swag\\Braintree\\Tests\\Integration\\": "tests/integration/" + "Swag\\Braintree\\Tests\\Integration\\": "tests/integration/", + "Swag\\Braintree\\Tests\\Application\\": "tests/application/" } }, "replace": { @@ -93,7 +94,8 @@ "bin/console doctrine:migrations:migrate --env=test -n" ], "phpunit:unit": "@phpunit --testsuite=SwagBraintreeUnitTest", - "phpunit:integration": "@phpunit --testsuite=SwagBraintreeIntegrationTest" + "phpunit:integration": "@phpunit --testsuite=SwagBraintreeIntegrationTest", + "phpunit:application": "@phpunit --testsuite=SwagBraintreeApplicationTest" }, "conflict": { "symfony/symfony": "*" diff --git a/config/services.php b/config/services.php index 1d856be..2fdb255 100644 --- a/config/services.php +++ b/config/services.php @@ -13,4 +13,8 @@ ->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'); $container->import(__DIR__ . '/services/braintree.xml', 'xml'); + + if ($container->env() === 'test') { + $container->import(__DIR__ . '/services/braintree_test.xml', 'xml'); + } }; diff --git a/config/services/braintree_test.xml b/config/services/braintree_test.xml new file mode 100644 index 0000000..9e4f4dd --- /dev/null +++ b/config/services/braintree_test.xml @@ -0,0 +1,27 @@ + + + + %env(BRAINTREE_TEST_MERCHANT_ACCOUNT_ID)% + %env(BRAINTREE_TEST_MERCHANT_ID)% + %env(BRAINTREE_TEST_ENVIRONMENT)% + %env(BRAINTREE_TEST_PUBLIC_KEY)% + %env(BRAINTREE_TEST_PRIVATE_KEY)% + + + + + %env(BRAINTREE_TEST_ENVIRONMENT)% + %env(BRAINTREE_TEST_MERCHANT_ID)% + %env(BRAINTREE_TEST_PUBLIC_KEY)% + %env(BRAINTREE_TEST_PRIVATE_KEY)% + + + + + + + diff --git a/infection.json5 b/infection.json5 index 4d4be61..0cad787 100644 --- a/infection.json5 +++ b/infection.json5 @@ -21,6 +21,7 @@ }, "tmpDir": "var/cache", "testFramework":"phpunit", + "testFrameworkOptions": "--testsuite=SwagBraintreeUnitTest,SwagBraintreeIntegrationTest", "mutators": { "@default": true, "LessThan": { @@ -30,17 +31,17 @@ }, "PlusEqual": { "ignore": [ - "Swag\\Braintree\\Braintree\\Payment\\OrderInformationService::extractDiscountAmount", + "Swag\\Braintree\\Braintree\\Payment\\OrderInformationService::extractDiscountAmount" ] }, "GreaterThan": { "ignore": [ - "Swag\\Braintree\\Braintree\\Payment\\OrderInformationService::extractDiscountAmount", + "Swag\\Braintree\\Braintree\\Payment\\OrderInformationService::extractDiscountAmount" ] }, "ArrayItem": { "ignore": [ - "Swag\\Braintree\\Braintree\\Payment\\BraintreePaymentService::handleTransaction", + "Swag\\Braintree\\Braintree\\Payment\\BraintreePaymentService::handleTransaction" ] } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 023d384..2cb784b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -42,6 +42,10 @@ tests/integration + + + tests/application + diff --git a/src/Tests/ApplicationBootstrapTrait.php b/src/Tests/ApplicationBootstrapTrait.php new file mode 100644 index 0000000..d8014a5 --- /dev/null +++ b/src/Tests/ApplicationBootstrapTrait.php @@ -0,0 +1,56 @@ +get(EntityManagerInterface::class); + + $shop = new ShopEntity(Ids::get('shop-id'), 'https://platform.dev.localhost', 'this-is-shop-secret'); + $shop->setBraintreeMerchantId($container->getParameter('BRAINTREE_TEST_MERCHANT_ID')); + $shop->setBraintreePublicKey($container->getParameter('BRAINTREE_TEST_PUBLIC_KEY')); + $shop->setBraintreePrivateKey($container->getParameter('BRAINTREE_TEST_PRIVATE_KEY')); + $shop->setShopActive(true); + $shop->setShopApiCredentials('this-is-the-client-id', 'this-is-the-client-secret'); + $shop->setBraintreeSandbox(true); + + $em->persist($shop); + $em->flush(); + + static::getContainer() + ->get(ConfigRepository::class) + ->upsert([[ + 'salesChannelId' => null, + 'threeDSecureEnforced' => true, + 'shipsFromPostalCode' => '48268', + ]], $shop); + + $currencyMapping = new CurrencyMappingEntity(); + $currencyMapping->setCurrencyId('EUR'); + $currencyMapping->setMerchantAccountId($container->getParameter('BRAINTREE_TEST_MERCHANT_ACCOUNT_ID')); + $currencyMapping->setShop($shop); + + static::getContainer() + ->get(CurrencyMappingRepository::class) + ->upsert([[ + 'salesChannelId' => null, + 'currencyId' => Ids::get('currency-id'), + 'currencyIso' => 'EUR', + 'merchantAccountId' => $container->getParameter('BRAINTREE_TEST_MERCHANT_ACCOUNT_ID'), + ]], $shop); + + return $shop; + } +} diff --git a/src/Tests/Braintree/Gateway/BraintreeTestGatewayFactory.php b/src/Tests/Braintree/Gateway/BraintreeTestGatewayFactory.php new file mode 100644 index 0000000..379846b --- /dev/null +++ b/src/Tests/Braintree/Gateway/BraintreeTestGatewayFactory.php @@ -0,0 +1,31 @@ + $this->braintreeEnv, + 'merchantId' => $this->braintreeMerchantId, + 'publicKey' => $this->braintreePublicKey, + 'privateKey' => $this->braintreePrivateKey, + ] + ); + + return new Gateway($configuration); + } +} diff --git a/src/Tests/Contract/IntegrationHelperTrait.php b/src/Tests/Contract/IntegrationHelperTrait.php index 51a0f6b..d705653 100644 --- a/src/Tests/Contract/IntegrationHelperTrait.php +++ b/src/Tests/Contract/IntegrationHelperTrait.php @@ -4,13 +4,16 @@ use Doctrine\ORM\EntityManagerInterface; use Swag\Braintree\Entity\ShopEntity; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * @infection-ignore-all */ trait IntegrationHelperTrait { - use IntegrationHelperTrait; + use ShopHelperTrait; + + abstract protected static function getContainer(): ContainerInterface; protected static function getEntityManager(): EntityManagerInterface { diff --git a/src/Tests/Contract/OrderHelperTrait.php b/src/Tests/Contract/OrderHelperTrait.php index 4891086..4394746 100644 --- a/src/Tests/Contract/OrderHelperTrait.php +++ b/src/Tests/Contract/OrderHelperTrait.php @@ -416,4 +416,141 @@ protected static function createOrderWithDiscount(): Order 'id' => Ids::get('order-discount-id'), ]); } + + private function createOrderForApplication(): Order + { + return new Order([ + 'orderNumber' => '10068', + 'salesChannelId' => Ids::get('order-sales-channel-id'), + 'price' => [ + 'netPrice' => 100, + 'totalPrice' => 119, + 'calculatedTaxes' => [['tax' => 19, 'taxRate' => 19, 'price' => 100]], + 'taxRules' => [['taxRate' => 19, 'percentage' => 100]], + 'positionPrice' => 119, + 'taxStatus' => 'gross', + 'rawTotal' => 100, + ], + 'amountTotal' => 119, + 'amountNet' => 100, + 'positionPrice' => 100, + 'taxStatus' => 'gross', + 'shippingTotal' => 0, + 'shippingCosts' => [ + 'unitPrice' => 0, + 'quantity' => 0, + 'totalPrice' => 0, + 'calculatedTaxes' => [['tax' => 19, 'taxRate' => 19, 'price' => 100]], + 'taxRules' => [['taxRate' => 19, 'percentage' => 100]], + ], + 'orderCustomer' => [ + 'email' => 'test@example.com', + 'orderId' => Ids::get('order-id'), + 'firstName' => 'Application', + 'lastName' => 'Tester', + 'title' => null, + 'company' => 'shopware AG', + 'customerNumber' => '1337', + 'customerId' => Ids::get('order-customer-id'), + 'id' => Ids::get('order-order-customer-id'), + ], + 'currency' => [ + 'isoCode' => 'EUR', + 'symbol' => '€', + 'shortName' => 'EUR', + 'name' => 'Euro', + 'itemRounding' => ['decimals' => 2, 'interval' => 0.01, 'roundForNet' => true], + 'totalRounding' => ['decimals' => 2, 'interval' => 0.01, 'roundForNet' => true], + 'id' => Ids::get('currency-id'), + ], + 'billingAddress' => [ + 'firstName' => 'Application', + 'lastName' => 'Tester', + 'street' => 'Bahnhofstraße 27', + 'zipcode' => '10332', + 'city' => 'Berlin', + 'company' => null, + 'title' => null, + 'additionalAddressLine1' => null, + 'additionalAddressLine2' => null, + 'country' => [ + 'name' => 'Haiti', + 'iso' => 'HT', + 'iso3' => 'HTI', + 'id' => Ids::get('order-country-id'), + ], + 'id' => Ids::get('order-billing-address-id'), + ], + 'deliveries' => [[ + 'shippingCosts' => [ + 'unitPrice' => 0, + 'quantity' => 0, + 'totalPrice' => 0, + 'calculatedTaxes' => [[ + 'tax' => 0, + 'taxRate' => 0, + 'price' => 0, + ]], + ], + 'shippingOrderAddress' => [ + 'firstName' => 'Application', + 'lastName' => 'Tester', + 'street' => 'Ebbinghoff 10', + 'zipcode' => '1234567890', + 'city' => 'Schöppingen', + 'company' => 'company', + 'title' => null, + 'additionalAddressLine1' => 'additionalAddressLine1', + 'additionalAddressLine2' => null, + 'country' => [ + 'name' => 'Hungary', + 'iso' => 'HU', + 'iso3' => 'HUN', + 'id' => Ids::get('order-country-id'), + ], + 'countryState' => [ + 'name' => 'countryState', + ], + 'id' => Ids::get('order-shipping-address-id'), + ], + 'id' => Ids::get('order-delivery-id'), + ]], + 'lineItems' => [[ + 'quantity' => 1, + 'unitPrice' => 100, + 'totalPrice' => 100, + 'label' => 'Product 1 - Application test', + 'description' => 'Product description 1 from application test', + 'good' => true, + 'type' => 'product', + 'referencedId' => 'product-1', + 'payload' => [ + 'customFields' => [ + OrderInformationService::LINE_ITEM_COMMODITY_CODE_CUSTOM_FIELD => '1234567890', + ], + ], + 'price' => [ + 'unitPrice' => 100, + 'quantity' => 1, + 'totalPrice' => 100, + 'calculatedTaxes' => [['tax' => 19, 'taxRate' => 19, 'price' => 100]], + 'taxRules' => [['taxRate' => 19, 'percentage' => 100]], + ], + 'id' => Ids::get('order-line-item-id'), + ]], + 'transactions' => [[ + 'amount' => [ + 'unitPrice' => 100, + 'quantity' => 1, + 'totalPrice' => 100, + 'calculatedTaxes' => [['tax' => 19, 'taxRate' => 10, 'price' => 100]], + 'taxRules' => [['taxRate' => 19, 'percentage' => 100]], + ], + 'id' => Ids::get('order-discount-transaction-id'), + ]], + 'itemRounding' => ['decimals' => 2, 'interval' => 0.01, 'roundForNet' => true], + 'totalRounding' => ['decimals' => 2, 'interval' => 0.01, 'roundForNet' => true], + 'id' => Ids::get('order-discount-id'), + ]); + } } diff --git a/src/Tests/Contract/OrderTransactionHelperTrait.php b/src/Tests/Contract/OrderTransactionHelperTrait.php index 337ada0..b2db38c 100644 --- a/src/Tests/Contract/OrderTransactionHelperTrait.php +++ b/src/Tests/Contract/OrderTransactionHelperTrait.php @@ -14,11 +14,11 @@ protected static function createOrderTransaction(): OrderTransaction { return new OrderTransaction([ 'amount' => [ - 'unitPrice' => 200, + 'unitPrice' => 119, 'quantity' => 1, - 'totalPrice' => 200, - 'calculatedTaxes' => [['tax' => 20.456, 'taxRate' => 10, 'price' => 200]], - 'taxRules' => [['taxRate' => 10, 'percentage' => 100]], + 'totalPrice' => 119, + 'calculatedTaxes' => [['tax' => 19, 'taxRate' => 19, 'price' => 100]], + 'taxRules' => [['taxRate' => 19, 'percentage' => 100]], ], 'id' => Ids::get('order-transaction-id'), ]); diff --git a/src/Tests/Contract/PaymentPayActionHelperTrait.php b/src/Tests/Contract/PaymentPayActionHelperTrait.php index 8332203..ed02385 100644 --- a/src/Tests/Contract/PaymentPayActionHelperTrait.php +++ b/src/Tests/Contract/PaymentPayActionHelperTrait.php @@ -28,4 +28,22 @@ protected static function createPaymentPayAction(ShopInterface $shop, array $req $requestData, ); } + + /** + * @param array $requestData + */ + private function createPaymentPayActionForApplication(ShopInterface $shop, array $requestData = []): PaymentPayAction + { + $actionSource = new ActionSource('this-is-url', 'this-is-app-version'); + + return new PaymentPayAction( + $shop, + $actionSource, + $this->createOrderForApplication(), + $this->createOrderTransaction(), + null, + null, + $requestData, + ); + } } diff --git a/tests/application/Braintree/BraintreeReachabilityTest.php b/tests/application/Braintree/BraintreeReachabilityTest.php new file mode 100644 index 0000000..43cc678 --- /dev/null +++ b/tests/application/Braintree/BraintreeReachabilityTest.php @@ -0,0 +1,19 @@ +getContainer()->get(Gateway::class); + $merchant = $gateway->merchantAccount()->find($this->getContainer()->getParameter('BRAINTREE_TEST_MERCHANT_ACCOUNT_ID')); + + static::assertSame(MerchantAccount::STATUS_ACTIVE, $merchant->status); + static::assertTrue($merchant->default); + } +} diff --git a/tests/application/Braintree/Payment/BraintreePaymentServiceTest.php b/tests/application/Braintree/Payment/BraintreePaymentServiceTest.php new file mode 100644 index 0000000..a9d75cc --- /dev/null +++ b/tests/application/Braintree/Payment/BraintreePaymentServiceTest.php @@ -0,0 +1,37 @@ +registerShop(); + $action = $this->createPaymentPayActionForApplication( + $shop, + [BraintreePaymentService::BRAINTREE_NONCE => 'fake-three-d-secure-visa-full-authentication-nonce'] + ); + + $service = static::getContainer()->get(BraintreePaymentService::class); + $transaction = $service->handleTransaction($action); + + static::assertSame(Transaction::SUBMITTED_FOR_SETTLEMENT, $transaction->status); + static::assertSame('119.00', $transaction->amount); + static::assertSame('EUR', $transaction->currencyIsoCode); + + static::assertTrue($transaction->threeDSecureInfo->liabilityShifted); + static::assertTrue($transaction->threeDSecureInfo->liabilityShiftPossible); + static::assertSame(ThreeDSecure::STATUS_AUTHENTICATE_SUCCESSFUL, $transaction->threeDSecureInfo->status); + static::assertSame(ThreeDSecure::ENROLLMENT_STATUS_YES, $transaction->threeDSecureInfo->enrolled); + } +} diff --git a/tests/unit/Braintree/Payment/BraintreePaymentServiceTest.php b/tests/unit/Braintree/Payment/BraintreePaymentServiceTest.php index 00d3f5a..543648b 100644 --- a/tests/unit/Braintree/Payment/BraintreePaymentServiceTest.php +++ b/tests/unit/Braintree/Payment/BraintreePaymentServiceTest.php @@ -104,11 +104,11 @@ public function testHandleTransaction(): void ->method('sale') ->with(static::callback(function (array $sale) { static::assertEquals('this-is-merchant-id', $sale['merchantAccountId']); - static::assertEquals(200, $sale['amount']); + static::assertEquals(119.0, $sale['amount']); static::assertEquals(5, $sale['shippingAmount']); static::assertEquals(10068, $sale['purchaseOrderNumber']); static::assertEquals('this-is-nonce', $sale['paymentMethodNonce']); - static::assertEquals(20.46, $sale['taxAmount']); + static::assertEquals(19, $sale['taxAmount']); static::assertEquals(['submitForSettlement' => true], $sale['options']); static::assertEquals('this-is-device-data', $sale['deviceData']); static::assertEquals('this-is-nonce', $sale['paymentMethodNonce']); diff --git a/tests/unit/Braintree/Payment/OrderInformationServiceTest.php b/tests/unit/Braintree/Payment/OrderInformationServiceTest.php index 150169f..02e5fce 100644 --- a/tests/unit/Braintree/Payment/OrderInformationServiceTest.php +++ b/tests/unit/Braintree/Payment/OrderInformationServiceTest.php @@ -42,7 +42,7 @@ protected function setUp(): void public function testExtractTaxAmount(): void { $taxAmount = $this->orderInformationService->extractTaxAmount($this->paymentPayAction); - static::assertEquals(20.46, $taxAmount); + static::assertEquals(19.0, $taxAmount); } public function testExtractShippingAddress(): void