From dd83e43ff9dde85f02f76f7ebad8031ae4e1ab10 Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 16 Oct 2020 19:11:41 +1300 Subject: [PATCH] Add initial test to the new extension I made a small change to support the test - causing Membership.create not to set the stattus_id if it is specified. --- .../phpunit.xml.dist | 18 ++ .../tests/phpunit/IPNCancelTest.php | 204 ++++++++++++++++++ .../tests/phpunit/bootstrap.php | 63 ++++++ 3 files changed, 285 insertions(+) create mode 100644 ext/contributioncancelactions/phpunit.xml.dist create mode 100644 ext/contributioncancelactions/tests/phpunit/IPNCancelTest.php create mode 100644 ext/contributioncancelactions/tests/phpunit/bootstrap.php diff --git a/ext/contributioncancelactions/phpunit.xml.dist b/ext/contributioncancelactions/phpunit.xml.dist new file mode 100644 index 0000000000..fc8f870b72 --- /dev/null +++ b/ext/contributioncancelactions/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + ./tests/phpunit + + + + + ./ + + + + + + + + diff --git a/ext/contributioncancelactions/tests/phpunit/IPNCancelTest.php b/ext/contributioncancelactions/tests/phpunit/IPNCancelTest.php new file mode 100644 index 0000000000..9f808765e8 --- /dev/null +++ b/ext/contributioncancelactions/tests/phpunit/IPNCancelTest.php @@ -0,0 +1,204 @@ +installMe(__DIR__) + ->apply(); + } + + /** + * Test that a cancel from paypal pro results in an order being cancelled. + * + * @throws \API_Exception + * @throws \CRM_Core_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + public function testPaypalProCancel() { + $this->createMembershipOrder(); + $membership = $this->callAPISuccessGetSingle('Membership', []); + $membership['owner_membership_id'] = $membership['id']; + $membership['contact_id'] = Contact::create()->setValues(['first_name' => 'Bugs', 'last_name' => 'Bunny'])->execute()->first()['id']; + unset($membership['id']); + $this->callAPISuccess('Membership', 'create', $membership); + + $ipn = new CRM_Core_Payment_PayPalProIPN([ + 'rp_invoice_id' => http_build_query([ + 'b' => $this->ids['Contribution'][0], + 'm' => 'contribute', + 'i' => 'zyx', + 'c' => $this->ids['contact'][0], + ]), + 'mc_gross' => 200, + 'payment_status' => 'Refunded', + 'processor_id' => $this->createPaymentProcessor(), + ]); + $ipn->main(); + $this->callAPISuccessGetSingle('Contribution', ['contribution_status_id' => 'Cancelled']); + $this->callAPISuccessGetCount('Membership', ['status_id' => 'Cancelled'], 2); + } + + /** + * Create an order with more than one membership. + * + * @throws \API_Exception + * @throws \CRM_Core_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected function createMembershipOrder() { + $this->ids['contact'][0] = Civi\Api4\Contact::create()->setValues(['first_name' => 'Brer', 'last_name' => 'Rabbit'])->execute()->first()['id']; + $this->createMembershipType(); + $priceFieldID = $this->callAPISuccessGetValue('price_field', [ + 'return' => 'id', + 'label' => 'Membership Amount', + 'options' => ['limit' => 1, 'sort' => 'id DESC'], + ]); + $generalPriceFieldValueID = $this->callAPISuccessGetValue('price_field_value', [ + 'return' => 'id', + 'label' => 'General', + 'options' => ['limit' => 1, 'sort' => 'id DESC'], + ]); + + $orderID = $this->callAPISuccess('Order', 'create', [ + 'financial_type_id' => 'Member Dues', + 'contact_id' => $this->ids['contact'][0], + 'is_test' => 0, + 'payment_instrument_id' => 'Credit card', + 'receive_date' => '2019-07-25 07:34:23', + 'invoice_id' => 'zyx', + 'line_items' => [ + [ + 'params' => [ + 'contact_id' => $this->ids['contact'][0], + 'source' => 'Payment', + 'membership_type_id' => 'General', + // This is interim needed while we improve the BAO - if the test passes without it it can go! + 'skipStatusCal' => TRUE, + ], + 'line_item' => [ + [ + 'label' => 'General', + 'qty' => 1, + 'unit_price' => 200, + 'line_total' => 200, + 'financial_type_id' => 1, + 'entity_table' => 'civicrm_membership', + 'price_field_id' => $priceFieldID, + 'price_field_value_id' => $generalPriceFieldValueID, + ], + ], + ], + ], + ])['id']; + $this->ids['Contribution'][0] = $orderID; + } + + /** + * Create the general membership type. + * + * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected function createMembershipType(): void { + MembershipType::create()->setValues([ + 'name' => 'General', + 'duration_unit' => 'year', + 'duration_interval' => 1, + 'period_type' => 'rolling', + 'member_of_contact_id' => 1, + 'domain_id' => 1, + 'financial_type_id' => 2, + 'is_active' => 1, + 'sequential' => 1, + 'visibility' => 'Public', + ])->execute(); + } + + /** + * Create a payment processor. + * + * @param array $params + * + * @return int + * @throws \CRM_Core_Exception + */ + public function createPaymentProcessor($params = []) { + $params = array_merge([ + 'name' => 'demo', + 'domain_id' => CRM_Core_Config::domainID(), + 'payment_processor_type_id' => 'PayPal', + 'is_active' => 1, + 'is_default' => 0, + 'is_test' => 1, + 'user_name' => 'sunil._1183377782_biz_api1.webaccess.co.in', + 'password' => '1183377788', + 'signature' => 'APixCoQ-Zsaj-u3IH7mD5Do-7HUqA9loGnLSzsZga9Zr-aNmaJa3WGPH', + 'url_site' => 'https://www.sandbox.paypal.com/', + 'url_api' => 'https://api-3t.sandbox.paypal.com/', + 'url_button' => 'https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif', + 'class_name' => 'Payment_PayPalImpl', + 'billing_mode' => 3, + 'financial_type_id' => 1, + 'financial_account_id' => 12, + // Credit card = 1 so can pass 'by accident'. + 'payment_instrument_id' => 'Debit Card', + ], $params); + if (!is_numeric($params['payment_processor_type_id'])) { + // really the api should handle this through getoptions but it's not exactly api call so lets just sort it + //here + $params['payment_processor_type_id'] = $this->callAPISuccess('payment_processor_type', 'getvalue', [ + 'name' => $params['payment_processor_type_id'], + 'return' => 'id', + ], 'integer'); + } + $result = $this->callAPISuccess('payment_processor', 'create', $params); + return (int) $result['id']; + } + +} diff --git a/ext/contributioncancelactions/tests/phpunit/bootstrap.php b/ext/contributioncancelactions/tests/phpunit/bootstrap.php new file mode 100644 index 0000000000..a5b49253c8 --- /dev/null +++ b/ext/contributioncancelactions/tests/phpunit/bootstrap.php @@ -0,0 +1,63 @@ +add('CRM_', __DIR__); +$loader->add('Civi\\', __DIR__); +$loader->add('api_', __DIR__); +$loader->add('api\\', __DIR__); +$loader->register(); + +/** + * Call the "cv" command. + * + * @param string $cmd + * The rest of the command to send. + * @param string $decode + * Ex: 'json' or 'phpcode'. + * @return string + * Response output (if the command executed normally). + * @throws \RuntimeException + * If the command terminates abnormally. + */ +function cv($cmd, $decode = 'json') { + $cmd = 'cv ' . $cmd; + $descriptorSpec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => STDERR); + $oldOutput = getenv('CV_OUTPUT'); + putenv("CV_OUTPUT=json"); + + // Execute `cv` in the original folder. This is a work-around for + // phpunit/codeception, which seem to manipulate PWD. + $cmd = sprintf('cd %s; %s', escapeshellarg(getenv('PWD')), $cmd); + + $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__); + putenv("CV_OUTPUT=$oldOutput"); + fclose($pipes[0]); + $result = stream_get_contents($pipes[1]); + fclose($pipes[1]); + if (proc_close($process) !== 0) { + throw new RuntimeException("Command failed ($cmd):\n$result"); + } + switch ($decode) { + case 'raw': + return $result; + + case 'phpcode': + // If the last output is /*PHPCODE*/, then we managed to complete execution. + if (substr(trim($result), 0, 12) !== "/*BEGINPHP*/" || substr(trim($result), -10) !== "/*ENDPHP*/") { + throw new \RuntimeException("Command failed ($cmd):\n$result"); + } + return $result; + + case 'json': + return json_decode($result, 1); + + default: + throw new RuntimeException("Bad decoder format ($decode)"); + } +} -- 2.25.1