CRM-19170 preliminary tidy up on top donor report for testability
authoreileen <emcnaughton@wikimedia.org>
Thu, 4 Aug 2016 08:27:17 +0000 (20:27 +1200)
committereileen <emcnaughton@wikimedia.org>
Tue, 16 Aug 2016 04:09:48 +0000 (16:09 +1200)
CRM/Report/Form/Contribute/TopDonor.php
tests/phpunit/api/v3/ReportTemplateTest.php

index 2481484ea6334362515edb3d8c48be932b2c801d..72d074efd04fba1027149d5aeab78534a4c9b0b9 100644 (file)
@@ -277,7 +277,7 @@ class CRM_Report_Form_Contribute_TopDonor extends CRM_Report_Form {
     }
     $this->_selectClauses = $select;
 
-    $this->_select = " SELECT * FROM ( SELECT " . implode(', ', $select) . " ";
+    $this->_select = " SELECT " . implode(', ', $select) . " ";
   }
 
   /**
@@ -391,7 +391,7 @@ class CRM_Report_Form_Contribute_TopDonor extends CRM_Report_Form {
     $setVariable = " SET @rows:=0, @rank=0 ";
     CRM_Core_DAO::singleValueQuery($setVariable);
 
-    $sql = " {$this->_select} {$this->_from}  {$this->_where} {$this->_groupBy}
+    $sql = "SELECT * FROM ( {$this->_select} {$this->_from}  {$this->_where} {$this->_groupBy}
                      ORDER BY civicrm_contribution_total_amount_sum DESC
                  ) as abc {$this->_outerCluase} $this->_limit
                ";
index ed3e3af0a987c8b385d41c5249e6b1dd1e8a527c..2e26a2710d33a3ffbfc7fd869392764f0ebc58d3 100644 (file)
 class api_v3_ReportTemplateTest extends CiviUnitTestCase {
   protected $_apiversion = 3;
 
-  public function setUp() {
-    parent::setUp();
-    $this->useTransaction(TRUE);
+  /**
+   * Our group reports use an alter so transaction cleanup won't work.
+   *
+   * @throws \Exception
+   */
+  public function tearDown() {
+    $this->quickCleanUpFinancialEntities();
+    $this->quickCleanup(array('civicrm_group', 'civicrm_saved_search', 'civicrm_group_contact'));
+    parent::tearDown();
   }
 
   public function testReportTemplate() {
@@ -203,6 +209,15 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
     return $reportTemplates;
   }
 
+  /**
+   * Get contribution templates that work with basic filter tests.
+   *
+   * These templates require minimal data config.
+   */
+  public static function getContributionReportTemplates() {
+    return array(array('contribute/summary'), array('contribute/detail'), array('contribute/repeat'), array('contribute/topDonor'));
+  }
+
   /**
    * Test Lybunt report to check basic inclusion of a contact who gave in the year before the chosen year.
    */
@@ -271,4 +286,250 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
     $this->assertEquals(2, $rows['count'], "Report failed - the sql used to generate the results was " . print_r($rows['metadata']['sql'], TRUE));
   }
 
+  /**
+   * Test the group filter works on the contribution summary (with a smart group).
+   */
+  public function testContributionSummaryWithSmartGroupFilter() {
+    $groupID = $this->setUpPopulatedSmartGroup();
+    $rows = $this->callAPISuccess('report_template', 'getrows', array(
+      'report_id' => 'contribute/summary',
+      'gid_value' => $groupID,
+      'gid_op' => 'in',
+      'options' => array('metadata' => array('sql')),
+    ));
+    $this->assertEquals(3, $rows['values'][0]['civicrm_contribution_total_amount_count']);
+
+  }
+
+  /**
+   * Test the group filter works on the contribution summary (with a smart group).
+   */
+  public function testContributionSummaryWithNotINSmartGroupFilter() {
+    $groupID = $this->setUpPopulatedSmartGroup();
+    $rows = $this->callAPISuccess('report_template', 'getrows', array(
+      'report_id' => 'contribute/summary',
+      'gid_value' => $groupID,
+      'gid_op' => 'not in',
+      'options' => array('metadata' => array('sql')),
+    ));
+    $this->assertEquals(2, $rows['values'][0]['civicrm_contribution_total_amount_count']);
+
+  }
+
+  /**
+   * Test the group filter works on the contribution summary (with a smart group).
+   *
+   * @dataProvider getContributionReportTemplates
+   *
+   * @param string $template
+   *   Report template unique identifier.
+   */
+  public function testContributionSummaryWithNonSmartGroupFilter($template) {
+    $groupID = $this->setUpPopulatedGroup();
+    $rows = $this->callAPISuccess('report_template', 'getrows', array(
+      'report_id' => $template,
+      'gid_value' => array($groupID),
+      'gid_op' => 'in',
+      'options' => array('metadata' => array('sql')),
+    ));
+    $this->assertNumberOfContactsInResult(1, $rows, $template);
+  }
+
+  /**
+   * Assert the included results match the expected.
+   *
+   * There may or may not be a group by in play so the assertion varies a little.
+   *
+   * @param int $numberExpected
+   * @param array $rows
+   *   Rows returned from the report.
+   * @param string $template
+   */
+  protected function assertNumberOfContactsInResult($numberExpected, $rows, $template) {
+    if (isset($rows['values'][0]['civicrm_contribution_total_amount_count'])) {
+      $this->assertEquals($numberExpected, $rows['values'][0]['civicrm_contribution_total_amount_count'], 'wrong row count in ' . $template);
+    }
+    else {
+      $this->assertEquals($numberExpected, count($rows['values']), 'wrong row count in ' . $template);
+    }
+  }
+
+  /**
+   * Test the group filter works on the contribution summary when 2 groups are involved.
+   */
+  public function testContributionSummaryWithTwoGroups() {
+    $groupID = $this->setUpPopulatedGroup();
+    $groupID2 = $this->setUpPopulatedSmartGroup();
+    $rows = $this->callAPISuccess('report_template', 'getrows', array(
+      'report_id' => 'contribute/summary',
+      'gid_value' => array($groupID, $groupID2),
+      'gid_op' => 'in',
+      'options' => array('metadata' => array('sql')),
+    ));
+    $this->assertEquals(4, $rows['values'][0]['civicrm_contribution_total_amount_count']);
+  }
+
+  /**
+   * Test the group filter works on the contribution summary when 2 groups are involved.
+   */
+  public function testContributionSummaryWithTwoGroupsWithIntersection() {
+    $groups = $this->setUpIntersectingGroups();
+
+    $rows = $this->callAPISuccess('report_template', 'getrows', array(
+      'report_id' => 'contribute/summary',
+      'gid_value' => $groups,
+      'gid_op' => 'in',
+      'options' => array('metadata' => array('sql')),
+    ));
+    $this->assertEquals(7, $rows['values'][0]['civicrm_contribution_total_amount_count']);
+  }
+
+  /**
+   * Set up a smart group for testing.
+   *
+   * The smart group includes all Households by filter. In addition an individual
+   * is created and hard-added and an individual is created that is not added.
+   *
+   * One household is hard-added as well as being in the filter.
+   *
+   * This gives us a range of scenarios for testing contacts are included only once
+   * whenever they are hard-added or in the criteria.
+   *
+   * @return int
+   */
+  public function setUpPopulatedSmartGroup() {
+    $household1ID = $this->householdCreate();
+    $individual1ID = $this->individualCreate();
+    $householdID = $this->householdCreate();
+    $individualID = $this->individualCreate();
+    $individualIDRemoved = $this->individualCreate();
+    $groupID = $this->smartGroupCreate(array(), array('name' => uniqid(), 'title' => uniqid()));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $individualIDRemoved,
+      'status' => 'Removed',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $individualID,
+      'status' => 'Added',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $householdID,
+      'status' => 'Added',
+    ));
+    foreach (array($household1ID, $individual1ID, $householdID, $individualID, $individualIDRemoved) as $contactID) {
+      $this->contributionCreate(array('contact_id' => $contactID, 'invoice_id' => '', 'trxn_id' => ''));
+    }
+
+    // Refresh the cache for test purposes. It would be better to alter to alter the GroupContact add function to add contacts to the cache.
+    CRM_Contact_BAO_GroupContactCache::remove($groupID, FALSE);
+    return $groupID;
+  }
+
+  /**
+   * Set up a smart group for testing.
+   *
+   * The smart group includes all Households by filter. In addition an individual
+   * is created and hard-added and an individual is created that is not added.
+   *
+   * One household is hard-added as well as being in the filter.
+   *
+   * This gives us a range of scenarios for testing contacts are included only once
+   * whenever they are hard-added or in the criteria.
+   *
+   * @return int
+   */
+  public function setUpPopulatedGroup() {
+    $individual1ID = $this->individualCreate();
+    $individualID = $this->individualCreate();
+    $individualIDRemoved = $this->individualCreate();
+    $groupID = $this->groupCreate(array('name' => uniqid(), 'title' => uniqid()));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $individualIDRemoved,
+      'status' => 'Removed',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $individualID,
+      'status' => 'Added',
+    ));
+
+    foreach (array($individual1ID, $individualID, $individualIDRemoved) as $contactID) {
+      $this->contributionCreate(array('contact_id' => $contactID, 'invoice_id' => '', 'trxn_id' => ''));
+    }
+
+    // Refresh the cache for test purposes. It would be better to alter to alter the GroupContact add function to add contacts to the cache.
+    CRM_Contact_BAO_GroupContactCache::remove($groupID, FALSE);
+    return $groupID;
+  }
+
+  /**
+   * @return array
+   */
+  public function setUpIntersectingGroups() {
+    $groupID = $this->setUpPopulatedGroup();
+    $groupID2 = $this->setUpPopulatedSmartGroup();
+    $addedToBothIndividualID = $this->individualCreate();
+    $removedFromBothIndividualID = $this->individualCreate();
+    $addedToSmartGroupRemovedFromOtherIndividualID = $this->individualCreate();
+    $removedFromSmartGroupAddedToOtherIndividualID = $this->individualCreate();
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $addedToBothIndividualID,
+      'status' => 'Added',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID2,
+      'contact_id' => $addedToBothIndividualID,
+      'status' => 'Added',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $removedFromBothIndividualID,
+      'status' => 'Removed',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID2,
+      'contact_id' => $removedFromBothIndividualID,
+      'status' => 'Removed',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID2,
+      'contact_id' => $addedToSmartGroupRemovedFromOtherIndividualID,
+      'status' => 'Added',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $addedToSmartGroupRemovedFromOtherIndividualID,
+      'status' => 'Removed',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID,
+      'contact_id' => $removedFromSmartGroupAddedToOtherIndividualID,
+      'status' => 'Added',
+    ));
+    $this->callAPISuccess('GroupContact', 'create', array(
+      'group_id' => $groupID2,
+      'contact_id' => $removedFromSmartGroupAddedToOtherIndividualID,
+      'status' => 'Removed',
+    ));
+
+    foreach (array(
+               $addedToBothIndividualID,
+               $removedFromBothIndividualID,
+               $addedToSmartGroupRemovedFromOtherIndividualID,
+               $removedFromSmartGroupAddedToOtherIndividualID,
+             ) as $contactID) {
+      $this->contributionCreate(array(
+        'contact_id' => $contactID,
+        'invoice_id' => '',
+        'trxn_id' => '',
+      ));
+    }
+    return array($groupID, $groupID2);
+  }
+
 }