core#1590: Don't send reminders to add'l recipients on deleted/inactive/template...
[civicrm-core.git] / tests / phpunit / api / v3 / JobTest.php
index 869bf470e2b10c1dc45ae60ee93247898b12562c..08a0188f6ef67f76e67005411114e208c4cb2578 100644 (file)
@@ -29,8 +29,6 @@ class api_v3_JobTest extends CiviUnitTestCase {
 
   public $_entity = 'Job';
 
-  public $_params = [];
-
   /**
    * Created membership type.
    *
@@ -59,11 +57,17 @@ class api_v3_JobTest extends CiviUnitTestCase {
     ];
   }
 
+  /**
+   * Cleanup after test.
+   *
+   * @throws \CRM_Core_Exception
+   */
   public function tearDown() {
     parent::tearDown();
     // The membershipType create breaks transactions so this extra cleanup is needed.
     $this->membershipTypeDelete(['id' => $this->membershipTypeID]);
     $this->cleanUpSetUpIDs();
+    $this->quickCleanUpFinancialEntities();
     $this->quickCleanup(['civicrm_contact', 'civicrm_address', 'civicrm_email', 'civicrm_website', 'civicrm_phone'], TRUE);
     parent::tearDown();
   }
@@ -112,6 +116,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
 
   /**
    * Clone job
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testClone() {
     $createResult = $this->callAPISuccess('job', 'create', $this->_params);
@@ -161,18 +167,11 @@ class api_v3_JobTest extends CiviUnitTestCase {
   }
 
   /**
+   * Test greeting update job.
    *
-   * public function testCallUpdateGreetingMissingParams() {
-   * $result = $this->callAPISuccess($this->_entity, 'update_greeting', array('gt' => 1));
-   * $this->assertEquals('Mandatory key(s) missing from params array: ct', $result['error_message']);
-   * }
-   *
-   * public function testCallUpdateGreetingIncorrectParams() {
-   * $result = $this->callAPISuccess($this->_entity, 'update_greeting', array('gt' => 1, 'ct' => 'djkfhdskjfhds'));
-   * $this->assertEquals('ct `djkfhdskjfhds` is not valid.', $result['error_message']);
-   * }
-   * /*
    * Note that this test is about tesing the metadata / calling of the function & doesn't test the success of the called function
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testCallUpdateGreetingSuccess() {
     $this->callAPISuccess($this->_entity, 'update_greeting', [
@@ -181,6 +180,11 @@ class api_v3_JobTest extends CiviUnitTestCase {
     ]);
   }
 
+  /**
+   * Test greeting update handles comma separated params.
+   *
+   * @throws \CRM_Core_Exception
+   */
   public function testCallUpdateGreetingCommaSeparatedParamsSuccess() {
     $gt = 'postal_greeting,email_greeting,addressee';
     $ct = 'Individual,Household';
@@ -196,6 +200,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Also note that this is testing a 'job' api so is in this class rather than scheduled_reminder - which
    * seems a cleaner place to build up a collection of scheduled reminder testing functions. However, it seems
    * that the api itself would need to be moved to the scheduled_reminder fn to do that  with the job wrapper being respected for legacy functions
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testCallSendReminderSuccessMoreThanDefaultLimit() {
     $membershipTypeID = $this->membershipTypeCreate();
@@ -232,55 +238,15 @@ class api_v3_JobTest extends CiviUnitTestCase {
    *
    * We create 3 contacts - 1 is in our group, 1 has our membership & the chosen one has both
    * & check that only the chosen one got the reminder
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public function testCallSendReminderLimitToSMS() {
-    $membershipTypeID = $this->membershipTypeCreate();
-    $this->membershipStatusCreate();
-    $createTotal = 3;
-    $groupID = $this->groupCreate(['name' => 'Texan drawlers', 'title' => 'a...']);
-    for ($i = 1; $i <= $createTotal; $i++) {
-      $contactID = $this->individualCreate();
-      $this->callAPISuccess('Phone', 'create', [
-        'contact_id' => $contactID,
-        'phone' => '555 123 1234',
-        'phone_type_id' => 'Mobile',
-        'location_type_id' => 'Billing',
-      ]);
-      if ($i == 2) {
-        $theChosenOneID = $contactID;
-      }
-      if ($i < 3) {
-        $this->callAPISuccess('group_contact', 'create', [
-          'contact_id' => $contactID,
-          'status' => 'Added',
-          'group_id' => $groupID,
-        ]);
-      }
-      if ($i > 1) {
-        $this->callAPISuccess('membership', 'create', [
-          'contact_id' => $contactID,
-          'membership_type_id' => $membershipTypeID,
-          'join_date' => 'now',
-          'start_date' => '+ 1 day',
-        ]);
-      }
-    }
-    $this->setupForSmsTests();
-    $provider = civicrm_api3('SmsProvider', 'create', [
-      'name' => "CiviTestSMSProvider",
-      'api_type' => "1",
-      "username" => "1",
-      "password" => "1",
-      "api_type" => "1",
-      "api_url" => "1",
-      "api_params" => "a=1",
-      "is_default" => "1",
-      "is_active" => "1",
-      "domain_id" => "1",
-    ]);
+    list($membershipTypeID, $groupID, $theChosenOneID, $provider) = $this->setUpMembershipSMSReminders();
     $this->callAPISuccess('action_schedule', 'create', [
-      'title' => " remind all Texans",
-      'subject' => "drawling renewal",
+      'title' => ' remind all Texans',
+      'subject' => 'drawling renewal',
       'entity_value' => $membershipTypeID,
       'mapping_id' => 4,
       'start_action_date' => 'membership_start_date',
@@ -301,6 +267,11 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->setupForSmsTests(TRUE);
   }
 
+  /**
+   * Test disabling expired relationships.
+   *
+   * @throws \CRM_Core_Exception
+   */
   public function testCallDisableExpiredRelationships() {
     $individualID = $this->individualCreate();
     $orgID = $this->organizationCreate();
@@ -327,6 +298,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
 
   /**
    * Event templates should not send reminders to additional contacts.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testTemplateRemindAddlContacts() {
     $contactId = $this->individualCreate();
@@ -338,9 +311,9 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $event = $this->eventCreate(['is_template' => 1, 'template_title' => "I'm a template", 'title' => NULL]);
     $eventId = $event['id'];
 
-    $actionSchedule = $this->callAPISuccess('action_schedule', 'create', [
-      'title' => "Do not send me",
-      'subject' => "I am a reminder attached to a template.",
+    $this->callAPISuccess('action_schedule', 'create', [
+      'title' => 'Do not send me',
+      'subject' => 'I am a reminder attached to a template.',
       'entity_value' => $eventId,
       'mapping_id' => 5,
       'start_action_date' => 'start_date',
@@ -353,7 +326,42 @@ class api_v3_JobTest extends CiviUnitTestCase {
     ]);
 
     $this->callAPISuccess('job', 'send_reminder', []);
-    $successfulCronCount = CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM civicrm_action_log");
+    $successfulCronCount = CRM_Core_DAO::singleValueQuery('SELECT count(*) FROM civicrm_action_log');
+    $this->assertEquals(0, $successfulCronCount);
+  }
+
+  /**
+   * Deleted events should not send reminders to additional contacts.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function testDeletedEventRemindAddlContacts() {
+    $contactId = $this->individualCreate();
+    $groupId = $this->groupCreate(['name' => 'Additional Contacts', 'title' => 'Additional Contacts']);
+    $this->callAPISuccess('GroupContact', 'create', [
+      'contact_id' => $contactId,
+      'group_id' => $groupId,
+    ]);
+    $event = $this->eventCreate(['title' => 'delete this event']);
+    $eventId = $event['id'];
+
+    $this->callAPISuccess('action_schedule', 'create', [
+      'title' => 'Do not send me',
+      'subject' => 'I am a reminder attached to a (soon to be) deleted event.',
+      'entity_value' => $eventId,
+      'mapping_id' => CRM_Event_ActionMapping::EVENT_NAME_MAPPING_ID,
+      'start_action_date' => 'start_date',
+      'start_action_offset' => 1,
+      'start_action_condition' => 'before',
+      'start_action_unit' => 'day',
+      'group_id' => $groupId,
+      'limit_to' => FALSE,
+      'mode' => 'Email',
+    ]);
+    $this->callAPISuccess('event', 'delete', ['id' => $eventId]);
+
+    $this->callAPISuccess('job', 'send_reminder', []);
+    $successfulCronCount = CRM_Core_DAO::singleValueQuery('SELECT count(*) FROM civicrm_action_log');
     $this->assertEquals(0, $successfulCronCount);
   }
 
@@ -364,55 +372,15 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * & check that only the chosen one got the reminder
    *
    * Also check no hard fail on cron job with running a reminder that has a deleted SMS provider
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
-  public function testCallSendReminderLimitToSMSWithDeletedProviderr() {
-    $membershipTypeID = $this->membershipTypeCreate();
-    $this->membershipStatusCreate();
-    $createTotal = 3;
-    $groupID = $this->groupCreate(['name' => 'Texan drawlers', 'title' => 'a...']);
-    for ($i = 1; $i <= $createTotal; $i++) {
-      $contactID = $this->individualCreate();
-      $this->callAPISuccess('Phone', 'create', [
-        'contact_id' => $contactID,
-        'phone' => '555 123 1234',
-        'phone_type_id' => 'Mobile',
-        'location_type_id' => 'Billing',
-      ]);
-      if ($i == 2) {
-        $theChosenOneID = $contactID;
-      }
-      if ($i < 3) {
-        $this->callAPISuccess('group_contact', 'create', [
-          'contact_id' => $contactID,
-          'status' => 'Added',
-          'group_id' => $groupID,
-        ]);
-      }
-      if ($i > 1) {
-        $this->callAPISuccess('membership', 'create', [
-          'contact_id' => $contactID,
-          'membership_type_id' => $membershipTypeID,
-          'join_date' => 'now',
-          'start_date' => '+ 1 day',
-        ]);
-      }
-    }
-    $this->setupForSmsTests();
-    $provider = civicrm_api3('SmsProvider', 'create', [
-      'name' => "CiviTestSMSProvider",
-      'api_type' => "1",
-      "username" => "1",
-      "password" => "1",
-      "api_type" => "1",
-      "api_url" => "1",
-      "api_params" => "a=1",
-      "is_default" => "1",
-      "is_active" => "1",
-      "domain_id" => "1",
-    ]);
+  public function testCallSendReminderLimitToSMSWithDeletedProvider() {
+    list($membershipTypeID, $groupID, $theChosenOneID, $provider) = $this->setUpMembershipSMSReminders();
     $this->callAPISuccess('action_schedule', 'create', [
-      'title' => " remind all Texans",
-      'subject' => "drawling renewal",
+      'title' => ' remind all Texans',
+      'subject' => 'drawling renewal',
       'entity_value' => $membershipTypeID,
       'mapping_id' => 4,
       'start_action_date' => 'membership_start_date',
@@ -440,6 +408,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Test the batch merge function.
    *
    * We are just checking it returns without error here.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMerge() {
     $this->callAPISuccess('Job', 'process_batch_merge', []);
@@ -451,6 +421,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * @dataProvider getMergeSets
    *
    * @param $dataSet
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeWorks($dataSet) {
     foreach ($dataSet['contacts'] as $params) {
@@ -458,8 +430,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
     }
 
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => $dataSet['mode']]);
-    $this->assertEquals($dataSet['skipped'], count($result['values']['skipped']), 'Failed to skip the right number:' . $dataSet['skipped']);
-    $this->assertEquals($dataSet['merged'], count($result['values']['merged']));
+    $this->assertCount($dataSet['skipped'], $result['values']['skipped'], 'Failed to skip the right number:' . $dataSet['skipped']);
+    $this->assertCount($dataSet['merged'], $result['values']['merged']);
     $result = $this->callAPISuccess('Contact', 'get', [
       'contact_sub_type' => 'Student',
       'sequential' => 1,
@@ -469,7 +441,7 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->assertEquals(count($dataSet['expected']), $result['count']);
     foreach ($dataSet['expected'] as $index => $contact) {
       foreach ($contact as $key => $value) {
-        if ($key == 'gender_id') {
+        if ($key === 'gender_id') {
           $key = 'gender';
         }
         $this->assertEquals($value, $result['values'][$index][$key]);
@@ -481,6 +453,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Check that the merge carries across various related entities.
    *
    * Note the group combinations & expected results:
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeWithAssets() {
     $contactID = $this->individualCreate();
@@ -497,8 +471,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->entityTagAdd(['contact_id' => $contact2ID, 'tag_id' => 'Short']);
     $this->entityTagAdd(['contact_id' => $contact2ID, 'tag_id' => 'Tall']);
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => 'safe']);
-    $this->assertEquals(0, count($result['values']['skipped']));
-    $this->assertEquals(1, count($result['values']['merged']));
+    $this->assertCount(0, $result['values']['skipped']);
+    $this->assertCount(1, $result['values']['merged']);
     $this->callAPISuccessGetCount('Contribution', ['contact_id' => $contactID], 2);
     $this->callAPISuccessGetCount('Contribution', ['contact_id' => $contact2ID], 0);
     $this->callAPISuccessGetCount('FinancialItem', ['contact_id' => $contactID], 2);
@@ -515,6 +489,27 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->callAPISuccessGetCount('ActivityContact', ['contact_id' => $contact2ID], 2);
   }
 
+  /**
+   * Test that non-contact entity tags are untouched in merge.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function testContributionEntityTag() {
+    $this->callAPISuccess('OptionValue', 'create', ['option_group_id' => 'tag_used_for', 'value' => 'civicrm_contribution', 'label' => 'Contribution']);
+    $tagID = $this->tagCreate(['name' => 'Big', 'used_for' => 'civicrm_contribution'])['id'];
+    $contact1 = (int) $this->individualCreate();
+    $contact2 = (int) $this->individualCreate();
+    $contributionID = NULL;
+    while ($contributionID !== $contact2) {
+      $contributionID = (int) $this->callAPISuccess('Contribution', 'create', ['contact_id' => $contact1, 'total_amount' => 5, 'financial_type_id' => 'Donation'])['id'];
+    }
+    $entityTagParams = ['entity_id' => $contributionID, 'entity_table' => 'civicrm_contribution', 'tag_id' => $tagID];
+    $this->callAPISuccess('EntityTag', 'create', $entityTagParams);
+    $this->callAPISuccessGetSingle('EntityTag', $entityTagParams);
+    $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => 'safe']);
+    $this->callAPISuccessGetSingle('EntityTag', $entityTagParams);
+  }
+
   /**
    * Check that the merge carries across various related entities.
    *
@@ -530,6 +525,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Group 7  null  Removed  **** null
    *
    * The ones with **** are the ones where I think a case could be made to change the behaviour.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeMergesGroups() {
     $contactID = $this->individualCreate();
@@ -597,8 +594,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
       'status' => 'Removed',
     ]);
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => 'safe']);
-    $this->assertEquals(0, count($result['values']['skipped']));
-    $this->assertEquals(1, count($result['values']['merged']));
+    $this->assertCount(0, $result['values']['skipped']);
+    $this->assertCount(1, $result['values']['merged']);
     $groupResult = $this->callAPISuccess('GroupContact', 'get', []);
     $this->assertEquals(5, $groupResult['count']);
     $expectedGroups = [
@@ -645,6 +642,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    *    - result primary kept with the lowest ID. Other address retained too (to preserve location type info).
    *
    * @param array $dataSet
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergesAddresses($dataSet) {
     $contactID1 = $this->individualCreate();
@@ -657,13 +656,13 @@ class api_v3_JobTest extends CiviUnitTestCase {
     }
 
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => 'safe']);
-    $this->assertEquals(1, count($result['values']['merged']));
+    $this->assertCount(1, $result['values']['merged']);
     $addresses = $this->callAPISuccess($dataSet['entity'], 'get', ['contact_id' => $contactID1, 'sequential' => 1]);
-    $this->assertEquals(count($dataSet['expected']), $addresses['count'], "Did not get the expected result for " . $dataSet['entity'] . (!empty($dataSet['description']) ? " on dataset {$dataSet['description']}" : ''));
+    $this->assertEquals(count($dataSet['expected']), $addresses['count'], 'Did not get the expected result for ' . $dataSet['entity'] . (!empty($dataSet['description']) ? " on dataset {$dataSet['description']}" : ''));
     $locationTypes = $this->callAPISuccess($dataSet['entity'], 'getoptions', ['field' => 'location_type_id']);
     foreach ($dataSet['expected'] as $index => $expectedAddress) {
       foreach ($expectedAddress as $key => $value) {
-        if ($key == 'location_type_id') {
+        if ($key === 'location_type_id') {
           $this->assertEquals($locationTypes['values'][$addresses['values'][$index][$key]], $value);
         }
         else {
@@ -679,6 +678,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * @dataProvider getMergeLocationData
    *
    * @param array $dataSet
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergesAddressesHook($dataSet) {
     $contactID1 = $this->individualCreate();
@@ -694,13 +695,13 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->hookClass->setHook('civicrm_alterLocationMergeData', [$this, 'hookMostRecentDonor']);
 
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => 'safe']);
-    $this->assertEquals(1, count($result['values']['merged']));
+    $this->assertCount(1, $result['values']['merged']);
     $addresses = $this->callAPISuccess($dataSet['entity'], 'get', ['contact_id' => $contactID1, 'sequential' => 1]);
     $this->assertEquals(count($dataSet['expected_hook']), $addresses['count']);
     $locationTypes = $this->callAPISuccess($dataSet['entity'], 'getoptions', ['field' => 'location_type_id']);
     foreach ($dataSet['expected_hook'] as $index => $expectedAddress) {
       foreach ($expectedAddress as $key => $value) {
-        if ($key == 'location_type_id') {
+        if ($key === 'location_type_id') {
           $this->assertEquals($locationTypes['values'][$addresses['values'][$index][$key]], $value);
         }
         else {
@@ -712,6 +713,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
 
   /**
    * Test the organization will not be matched to an individual.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeWillNotMergeOrganizationToIndividual() {
     $individual = $this->callAPISuccess('Contact', 'create', [
@@ -725,8 +728,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
       'email' => 'anonymous@hacker.com',
     ]);
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['mode' => 'aggressive']);
-    $this->assertEquals(0, count($result['values']['skipped']));
-    $this->assertEquals(0, count($result['values']['merged']));
+    $this->assertCount(0, $result['values']['skipped']);
+    $this->assertCount(0, $result['values']['merged']);
     $this->callAPISuccessGetSingle('Contact', ['id' => $individual['id']]);
     $this->callAPISuccessGetSingle('Contact', ['id' => $organization['id']]);
 
@@ -754,6 +757,7 @@ class api_v3_JobTest extends CiviUnitTestCase {
    *   Calculated migration info, informational only.
    *
    * @return mixed
+   * @throws \CRM_Core_Exception
    */
   public function hookMostRecentDonor(&$blocksDAO, $mainId, $otherId, $migrationInfo) {
 
@@ -848,13 +852,15 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Test the batch merge does not create duplicate emails.
    *
    * Test CRM-18546, a 4.7 regression whereby a merged contact gets duplicate emails.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeEmailHandling() {
     for ($x = 0; $x <= 4; $x++) {
       $id = $this->individualCreate(['email' => 'batman@gotham.met']);
     }
     $result = $this->callAPISuccess('Job', 'process_batch_merge', []);
-    $this->assertEquals(4, count($result['values']['merged']));
+    $this->assertCount(4, $result['values']['merged']);
     $this->callAPISuccessGetCount('Contact', ['email' => 'batman@gotham.met'], 1);
     $contacts = $this->callAPISuccess('Contact', 'get', ['is_deleted' => 0]);
     $deletedContacts = $this->callAPISuccess('Contact', 'get', ['is_deleted' => 1]);
@@ -919,13 +925,12 @@ class api_v3_JobTest extends CiviUnitTestCase {
    */
   public function getOnHoldSets() {
     // Each row specifies: contact 1 on_hold, contact 2 on_hold, merge? (0 or 1),
-    $sets = [
+    return [
       [0, 0, 1, NULL],
       [0, 1, 0, "Email 2 (Work): 'batman@gotham.met' vs. 'batman@gotham.met\n(On Hold)'"],
       [1, 0, 0, "Email 2 (Work): 'batman@gotham.met\n(On Hold)' vs. 'batman@gotham.met'"],
       [1, 1, 1, NULL],
     ];
-    return $sets;
   }
 
   /**
@@ -938,6 +943,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * @param string $name
    * @param bool $isReserved
    * @param int $threshold
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeEmptyRule($contactType, $used, $name, $isReserved, $threshold) {
     $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
@@ -974,6 +981,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Test the batch merge does not create duplicate emails.
    *
    * Test CRM-18546, a 4.7 regression whereby a merged contact gets duplicate emails.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeMatchingAddress() {
     for ($x = 0; $x <= 2; $x++) {
@@ -1026,6 +1035,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * Test the batch merge by id range.
    *
    * We have 2 sets of 5 matches & set the merge only to merge the lower set.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeIDRange() {
     for ($x = 0; $x <= 4; $x++) {
@@ -1057,13 +1068,15 @@ class api_v3_JobTest extends CiviUnitTestCase {
 
   /**
    * Test the batch merge copes with view only custom data field.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeCustomDataViewOnlyField() {
     CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'edit my contact'];
     $mouseParams = ['first_name' => 'Mickey', 'last_name' => 'Mouse', 'email' => 'tha_mouse@mouse.com'];
     $this->individualCreate($mouseParams);
 
-    $customGroup = $this->CustomGroupCreate();
+    $customGroup = $this->customGroupCreate();
     $customField = $this->customFieldCreate(['custom_group_id' => $customGroup['id'], 'is_view' => 1]);
     $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField['id'] => 'blah']));
 
@@ -1094,7 +1107,7 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField['id'] => 0]));
 
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['check_permissions' => 0, 'mode' => 'safe']);
-    $this->assertEquals(1, count($result['values']['merged']));
+    $this->assertCount(1, $result['values']['merged']);
     $mouseParams['return'] = 'custom_' . $customField['id'];
     $mouse = $this->callAPISuccess('Contact', 'getsingle', $mouseParams);
     $this->assertEquals(0, $mouse['custom_' . $customField['id']]);
@@ -1124,7 +1137,7 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $mouse2 = $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField['id'] => 1]));
 
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['check_permissions' => 0, 'mode' => 'safe']);
-    $this->assertEquals(0, count($result['values']['merged']));
+    $this->assertCount(0, $result['values']['merged']);
 
     // Reverse which mouse has the zero to test we still get a conflict.
     $this->individualCreate(array_merge($mouseParams, ['id' => $mouse1, 'custom_' . $customField['id'] => 1]));
@@ -1141,7 +1154,9 @@ class api_v3_JobTest extends CiviUnitTestCase {
    *
    * @dataProvider getMergeSets
    *
-   * @param $dataSet
+   * @param array $dataSet
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeWorksCheckPermissionsTrue($dataSet) {
     CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'administer CiviCRM', 'merge duplicate contacts', 'force merge duplicate contacts'];
@@ -1150,8 +1165,8 @@ class api_v3_JobTest extends CiviUnitTestCase {
     }
 
     $result = $this->callAPISuccess('Job', 'process_batch_merge', ['check_permissions' => 1, 'mode' => $dataSet['mode']]);
-    $this->assertEquals(0, count($result['values']['merged']), 'User does not have permission to any contacts, so no merging');
-    $this->assertEquals(0, count($result['values']['skipped']), 'User does not have permission to any contacts, so no skip visibility');
+    $this->assertCount(0, $result['values']['merged'], 'User does not have permission to any contacts, so no merging');
+    $this->assertCount(0, $result['values']['skipped'], 'User does not have permission to any contacts, so no skip visibility');
   }
 
   /**
@@ -1159,7 +1174,9 @@ class api_v3_JobTest extends CiviUnitTestCase {
    *
    * @dataProvider getMergeSets
    *
-   * @param $dataSet
+   * @param array $dataSet
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testBatchMergeWorksCheckPermissionsFalse($dataSet) {
     CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'edit my contact'];
@@ -1518,7 +1535,9 @@ class api_v3_JobTest extends CiviUnitTestCase {
   }
 
   /**
-   * @param $op
+   * Implements pre hook on relationships.
+   *
+   * @param string $op
    * @param string $objectName
    * @param int $id
    * @param array $params
@@ -1546,7 +1565,7 @@ class api_v3_JobTest extends CiviUnitTestCase {
    * @return array
    */
   public function getMergeLocations($locationParams1, $locationParams2, $entity, $additionalExpected = []) {
-    $data = [
+    return [
       [
         'matching_primary' => [
           'entity' => $entity,
@@ -1932,11 +1951,12 @@ class api_v3_JobTest extends CiviUnitTestCase {
         ],
       ],
     ];
-    return $data;
   }
 
   /**
    * Test processing membership for deceased contacts.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function testProcessMembershipDeceased() {
     $this->callAPISuccess('Job', 'process_membership', []);
@@ -1978,7 +1998,7 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $this->ids['MembershipType'] = $this->membershipTypeCreate();
 
     // Create admin-only membership status and get all statuses.
-    $membershipStatusIdAdmin = $this->callAPISuccess('membership_status', 'create', ['name' => 'Admin', 'is_admin' => 1])['id'];
+    $this->callAPISuccess('membership_status', 'create', ['name' => 'Admin', 'is_admin' => 1])['id'];
 
     // Create membership with incorrect statuses for the given dates and also some (pending, cancelled, admin override) which should not be updated.
     $memberships = [
@@ -2138,10 +2158,10 @@ class api_v3_JobTest extends CiviUnitTestCase {
     $resultCurrent = $this->callAPISuccess('Membership', 'create', $params);
     // Ensure that is_override is set to 0 by doing through DB given API not seem to accept id
     CRM_Core_DAO::executeQuery("Update civicrm_membership SET is_override = 0 WHERE id = %1", [1 => [$resultCurrent['id'], 'Positive']]);
-    $this->assertEquals(array_search('New', $memStatus), $resultCurrent['values'][0]['status_id']);
+    $this->assertEquals(array_search('New', $memStatus, TRUE), $resultCurrent['values'][0]['status_id']);
     $jobResult = $this->callAPISuccess('Job', 'process_membership', []);
     $this->assertEquals('Processed 1 membership records. Updated 1 records.', $jobResult['values']);
-    $this->assertEquals(array_search('Current', $memStatus), $this->callAPISuccess('Membership', 'get', ['id' => $resultCurrent['id']])['values'][$resultCurrent['id']]['status_id']);
+    $this->assertEquals(array_search('Current', $memStatus, TRUE), $this->callAPISuccess('Membership', 'get', ['id' => $resultCurrent['id']])['values'][$resultCurrent['id']]['status_id']);
   }
 
   /**
@@ -2187,4 +2207,59 @@ class api_v3_JobTest extends CiviUnitTestCase {
     return (int) $resultNew['id'];
   }
 
+  /**
+   * Shared set up for SMS reminder tests.
+   *
+   * @return array
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected function setUpMembershipSMSReminders(): array {
+    $membershipTypeID = $this->membershipTypeCreate();
+    $this->membershipStatusCreate();
+    $createTotal = 3;
+    $groupID = $this->groupCreate(['name' => 'Texan drawlers', 'title' => 'a...']);
+    for ($i = 1; $i <= $createTotal; $i++) {
+      $contactID = $this->individualCreate();
+      $this->callAPISuccess('Phone', 'create', [
+        'contact_id' => $contactID,
+        'phone' => '555 123 1234',
+        'phone_type_id' => 'Mobile',
+        'location_type_id' => 'Billing',
+      ]);
+      if ($i === 2) {
+        $theChosenOneID = $contactID;
+      }
+      if ($i < 3) {
+        $this->callAPISuccess('group_contact', 'create', [
+          'contact_id' => $contactID,
+          'status' => 'Added',
+          'group_id' => $groupID,
+        ]);
+      }
+      if ($i > 1) {
+        $this->callAPISuccess('membership', 'create', [
+          'contact_id' => $contactID,
+          'membership_type_id' => $membershipTypeID,
+          'join_date' => 'now',
+          'start_date' => '+ 1 day',
+        ]);
+      }
+    }
+    $this->setupForSmsTests();
+    $provider = civicrm_api3('SmsProvider', 'create', [
+      'name' => 'CiviTestSMSProvider',
+      'api_type' => '1',
+      'username' => '1',
+      'password' => '1',
+      'api_url' => '1',
+      'api_params' => 'a=1',
+      'is_default' => '1',
+      'is_active' => '1',
+      'domain_id' => '1',
+    ]);
+    return [$membershipTypeID, $groupID, $theChosenOneID, $provider];
+  }
+
 }