[NFC] [Test] Intial testing on event payment forms.
authoreileen <emcnaughton@wikimedia.org>
Mon, 10 Aug 2020 04:12:02 +0000 (16:12 +1200)
committereileen <emcnaughton@wikimedia.org>
Mon, 10 Aug 2020 05:13:54 +0000 (17:13 +1200)
I'm trying to work on improving our testing of payment forms. I started with 3 event forms but there were
enough challenges that for this commit I'm just adding tests on one form. As noted in the code comments
it makes sense for this test to be in the extension but I want to work through the challenges on the
other forms before finalising any helper functions to the point where they are available to extensions.

This is actually a bit of a break through as it's the first time we have testing on a form flow - ie
submitting the first form and then the second. It was quite painful

tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php [new file with mode: 0644]
tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php [new file with mode: 0644]
tests/phpunit/CiviTest/CiviUnitTestCase.php

diff --git a/tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php b/tests/phpunit/CRM/Core/Payment/AuthorizeNetTrait.php
new file mode 100644 (file)
index 0000000..87ff897
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Class CRM_Core_Payment_AuthorizeNetTest
+ * @group headless
+ */
+trait CRM_Core_Payment_AuthorizeNetTrait {
+  use \Civi\Test\GuzzleTestTrait;
+
+  /**
+   * @var \CRM_Core_Payment_AuthorizeNet
+   */
+  protected $processor;
+
+  /**
+   * Is this a recurring transaction.
+   *
+   * @var bool
+   */
+  protected $isRecur = FALSE;
+
+  /**
+   * Get the expected response from Authorize.net.
+   *
+   * @return string
+   */
+  public function getExpectedSinglePaymentResponse() {
+    return '"1","1","1","(TESTMODE) This transaction has been approved.","000000","P","0","","","5.24","CC","auth_capture","","John","O&#39;Connor","","","","","","","","","","","","","","","","","","","","","","","",""';
+  }
+
+  /**
+   *  Get the expected request from Authorize.net.
+   *
+   * @return string
+   */
+  public function getExpectedSinglePaymentRequest() {
+    return 'x_login=4y5BfuW7jm&x_tran_key=4cAmW927n8uLf5J8&x_email_customer=&x_first_name=John&x_last_name=O%27Connor&x_address=&x_city=&x_state=&x_zip=&x_country=&x_customer_ip=&x_email=&x_invoice_num=&x_amount=5.24&x_currency_code=&x_description=&x_cust_id=&x_relay_response=FALSE&x_delim_data=TRUE&x_delim_char=%2C&x_encap_char=%22&x_card_num=4444333322221111&x_card_code=123&x_exp_date=10%2F2022&x_test_request=TRUE';
+  }
+
+  /**
+   * Add a mock handler to the authorize.net processor for testing.
+   *
+   * @param int|null $id
+   *
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected function setupMockHandler($id = NULL) {
+    if ($id) {
+      $this->processor = Civi\Payment\System::singleton()->getById($id);
+    }
+    $response = $this->isRecur ? $this->getExpectedRecurResponse() : $this->getExpectedSinglePaymentResponse();
+    $this->createMockHandler([$response]);
+    $this->setUpClientWithHistoryContainer();
+    $this->processor->setGuzzleClient($this->getGuzzleClient());
+  }
+
+  /**
+   * Get a successful response to setting up a recurring.
+   *
+   * @return string
+   */
+  public function getExpectedRecurResponse() {
+    return '<?xml version="1.0" encoding="utf-8"?><ARBCreateSubscriptionResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"><refId>8d468ca1b1dd5c2b56c7</refId><messages><resultCode>Ok</resultCode><message><code>I00001</code><text>Successful.</text></message></messages><subscriptionId>6632052</subscriptionId><profile><customerProfileId>1512023280</customerProfileId><customerPaymentProfileId>1512027350</customerPaymentProfileId></profile></ARBCreateSubscriptionResponse>';
+  }
+
+}
diff --git a/tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php b/tests/phpunit/CRM/Financial/Form/PaymentFormsTest.php
new file mode 100644 (file)
index 0000000..29b271c
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *  Test various payment forms.
+ *
+ * This class is intended to be a place to build out testing of various forms - with the
+ * hope being to ensure all payment forms are consistently tested and to refine
+ * helper functions into a trait that could be available to
+ * extensions for testing - notably the eventcart which ideally should interact with core
+ * through approved interfaces - ideally even in tests.
+ *
+ * An approved interface would sit in the Civi directory and would at minimum support some functions
+ * to support using our processors in tests so we are testing a broader swath than just Dummy.
+ * Currently Authorize.net is also testable (uses Guzzle). At some point PaypalPro & Std should also be testable
+ * - allowing us to easily check payment forms work with the core processors which cover a reasonable amount of the
+ * expectations held by non-core processors .
+ *
+ * Note that this tests eventcart but is not in eventcart because I want to be sure about whether the
+ * traits supporting it make sense before making them available to extensions.
+ */
+class CRM_Financial_Form_PaymentFormsTest extends CiviUnitTestCase {
+
+  use CRM_Core_Payment_AuthorizeNetTrait;
+
+  /**
+   * Generic test on event payment forms to make sure they submit without error with payment processing.
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function testEventPaymentForms() {
+    $processors = [$this->paymentProcessorAuthorizeNetCreate(['is_test' => FALSE])];
+    $this->setupMockHandler($processors[0]);
+    $eventID = $this->eventCreatePaid([
+      'end_date' => '+ 1 month',
+      'registration_end_date' => '+ 1 month',
+      'payment_processor' => $processors,
+    ])['id'];
+    $this->createLoggedInUser();
+
+    $forms = [
+      'CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices' => [
+        'forms' => ['CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices', 'CRM_Event_Cart_Form_Checkout_Payment'],
+        'controller' => [],
+        'submitValues' => [
+          'event' => [$eventID => ['participant' => [1 => ['email' => 'bob@example.com']]]],
+          'event_' . $eventID . '_price_' . $this->_ids['price_field'][0] => $this->_ids['price_field_value'][0],
+        ],
+        'REQUEST' => [],
+      ],
+    ];
+    $genericParams = [
+      'credit_card_number' => 4111111111111111,
+      'processor_id' => $processors[0],
+      'cvv2' => '123',
+      'credit_card_exp_date' => [
+        'M' => '1',
+        'Y' => date('Y') + 1,
+      ],
+      'credit_card_type' => 'Visa',
+      'billing_contact_email' => 'bobby@example.com',
+      'billing_first_name' => 'John',
+      'billing_middle_name' => '',
+      'billing_last_name' => "O'Connor",
+      'billing_street_address-5' => '8 Hobbitton Road',
+      'billing_city-5' => 'The Shire',
+      'billing_state_province_id-5' => 1012,
+      'billing_postal_code-5' => 5010,
+      'billing_country_id-5' => 1228,
+    ];
+
+    $cart = CRM_Event_Cart_BAO_Cart::find_or_create_for_current_session();
+    $cart->add_event($eventID);
+
+    foreach ($forms as $values) {
+      $_REQUEST = $values['REQUEST'];
+      $qfKey = NULL;
+      foreach ($values['forms'] as $formName) {
+        $formValues = array_merge($genericParams, $values['submitValues'], ['qfKey' => $qfKey]);
+        $form = $this->getFormObject($formName, $formValues);
+        $form->preProcess();
+        $form->buildQuickForm();
+        $form->postProcess();
+        $qfKey = $form->controller->_key;
+      }
+      $this->callAPISuccessGetSingle('Participant', ['participant_status_id' => 'Registered']);
+      $request = explode('&', $this->getRequestBodies()[0]);
+      // This is stand in for now just to check a request happened. We can improve later.
+      $this->assertContains('x_card_num=4111111111111111', $request);
+    }
+  }
+
+}
index 34bba898b12081837f4ce15e08683c3003211c87..7d05958487b299cf091a58342da6186d23e51e29 100644 (file)
@@ -3234,9 +3234,22 @@ VALUES
    * @throws \CRM_Core_Exception
    */
   public function getFormObject($class, $formValues = [], $pageName = '') {
+    $_POST = $formValues;
     $form = new $class();
     $_SERVER['REQUEST_METHOD'] = 'GET';
-    $form->controller = new CRM_Core_Controller();
+    switch ($class) {
+      case 'CRM_Event_Cart_Form_Checkout_Payment':
+      case 'CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices':
+        $form->controller = new CRM_Event_Cart_Controller_Checkout();
+        break;
+
+      default:
+        $form->controller = new CRM_Core_Controller();
+    }
+    if (!$pageName) {
+      $formParts = explode('_', $class);
+      $pageName = array_pop($formParts);
+    }
     $form->controller->setStateMachine(new CRM_Core_StateMachine($form->controller));
     $_SESSION['_' . $form->controller->_name . '_container']['values'][$pageName] = $formValues;
     return $form;