Merge pull request #11387 from civicrm/4.7.28-rc
[civicrm-core.git] / tests / phpunit / CRM / Dedupe / MergerTest.php
CommitLineData
0622d221 1<?php
0622d221 2
3/**
4 * Class CRM_Dedupe_DedupeMergerTest
acb109b7 5 * @group headless
0622d221 6 */
7class CRM_Dedupe_MergerTest extends CiviUnitTestCase {
8
9 protected $_groupId;
10 protected $_contactIds = array();
11
3308aac0 12 public function tearDown() {
13 $this->quickCleanup(array('civicrm_contact', 'civicrm_group_contact', 'civicrm_group'));
14 parent::tearDown();
15 }
16
0622d221 17 public function createDupeContacts() {
18 // create a group to hold contacts, so that dupe checks don't consider any other contacts in the DB
19 $params = array(
20 'name' => 'Test Dupe Merger Group',
21 'title' => 'Test Dupe Merger Group',
22 'domain_id' => 1,
23 'is_active' => 1,
24 'visibility' => 'Public Pages',
0622d221 25 );
87a56b12 26
27 $result = $this->callAPISuccess('group', 'create', $params);
0622d221 28 $this->_groupId = $result['id'];
29
30 // contact data set
31
32 // make dupe checks based on based on following contact sets:
33 // FIRST - LAST - EMAIL
34 // ---------------------------------
35 // robin - hood - robin@example.com
36 // robin - hood - robin@example.com
37 // robin - hood - hood@example.com
38 // robin - dale - robin@example.com
39 // little - dale - dale@example.com
40 // little - dale - dale@example.com
41 // will - dale - dale@example.com
42 // will - dale - will@example.com
43 // will - dale - will@example.com
44 $params = array(
45 array(
46 'first_name' => 'robin',
47 'last_name' => 'hood',
48 'email' => 'robin@example.com',
49 'contact_type' => 'Individual',
50 ),
51 array(
52 'first_name' => 'robin',
53 'last_name' => 'hood',
54 'email' => 'robin@example.com',
55 'contact_type' => 'Individual',
56 ),
57 array(
58 'first_name' => 'robin',
59 'last_name' => 'hood',
60 'email' => 'hood@example.com',
61 'contact_type' => 'Individual',
62 ),
63 array(
64 'first_name' => 'robin',
65 'last_name' => 'dale',
66 'email' => 'robin@example.com',
67 'contact_type' => 'Individual',
68 ),
69 array(
70 'first_name' => 'little',
71 'last_name' => 'dale',
72 'email' => 'dale@example.com',
73 'contact_type' => 'Individual',
74 ),
75 array(
76 'first_name' => 'little',
77 'last_name' => 'dale',
78 'email' => 'dale@example.com',
79 'contact_type' => 'Individual',
80 ),
81 array(
82 'first_name' => 'will',
83 'last_name' => 'dale',
84 'email' => 'dale@example.com',
85 'contact_type' => 'Individual',
86 ),
87 array(
88 'first_name' => 'will',
89 'last_name' => 'dale',
90 'email' => 'will@example.com',
91 'contact_type' => 'Individual',
92 ),
93 array(
94 'first_name' => 'will',
95 'last_name' => 'dale',
96 'email' => 'will@example.com',
97 'contact_type' => 'Individual',
98 ),
99 );
100
101 $count = 1;
102 foreach ($params as $param) {
103 $param['version'] = 3;
104 $contact = civicrm_api('contact', 'create', $param);
105 $this->_contactIds[$count++] = $contact['id'];
106
107 $grpParams = array(
108 'contact_id' => $contact['id'],
109 'group_id' => $this->_groupId,
110 'version' => 3,
111 );
87a56b12 112 $this->callAPISuccess('group_contact', 'create', $grpParams);
0622d221 113 }
114 }
115
93ac19cd 116 /**
117 * Delete all created contacts.
118 */
0622d221 119 public function deleteDupeContacts() {
0622d221 120 foreach ($this->_contactIds as $contactId) {
93ac19cd 121 $this->contactDelete($contactId);
0622d221 122 }
87a56b12 123 $this->groupDelete($this->_groupId);
0622d221 124 }
125
c8ec0753 126 /**
a354251e 127 * Test the batch merge.
c8ec0753 128 */
0622d221 129 public function testBatchMergeSelectedDuplicates() {
130 $this->createDupeContacts();
131
132 // verify that all contacts have been created separately
133 $this->assertEquals(count($this->_contactIds), 9, 'Check for number of contacts.');
134
135 $dao = new CRM_Dedupe_DAO_RuleGroup();
136 $dao->contact_type = 'Individual';
137 $dao->name = 'IndividualSupervised';
138 $dao->is_default = 1;
139 $dao->find(TRUE);
140
141 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($dao->id, $this->_groupId);
142
143 // -------------------------------------------------------------------------
144 // Name and Email (reserved) Matches ( 3 pairs )
145 // --------------------------------------------------------------------------
146 // robin - hood - robin@example.com
147 // robin - hood - robin@example.com
148 // little - dale - dale@example.com
149 // little - dale - dale@example.com
150 // will - dale - will@example.com
151 // will - dale - will@example.com
152 // so 3 pairs for - first + last + mail
153 $this->assertEquals(count($foundDupes), 3, 'Check Individual-Supervised dupe rule for dupesInGroup().');
154
155 // Run dedupe finder as the browser would
156 $_SERVER['REQUEST_METHOD'] = 'GET'; //avoid invalid key error
157 $object = new CRM_Contact_Page_DedupeFind();
158 $object->set('gid', $this->_groupId);
159 $object->set('rgid', $dao->id);
160 $object->set('action', CRM_Core_Action::UPDATE);
64fe2fe0 161 $object->setEmbedded(TRUE);
0622d221 162 @$object->run();
163
164 // Retrieve pairs from prev next cache table
165 $select = array('pn.is_selected' => 'is_selected');
b1679439 166 $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($dao->id, $this->_groupId);
0622d221 167 $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
168
169 $this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.');
170
171 // mark first two pairs as selected
172 CRM_Core_DAO::singleValueQuery("UPDATE civicrm_prevnext_cache SET is_selected = 1 WHERE id IN ({$pnDupePairs[0]['prevnext_id']}, {$pnDupePairs[1]['prevnext_id']})");
173
174 $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
175 $this->assertEquals($pnDupePairs[0]['is_selected'], 1, 'Check if first record in dupe pairs is marked as selected.');
176 $this->assertEquals($pnDupePairs[0]['is_selected'], 1, 'Check if second record in dupe pairs is marked as selected.');
177
178 // batch merge selected dupes
d13a105a 179 $result = CRM_Dedupe_Merger::batchMerge($dao->id, $this->_groupId, 'safe', 5, 1);
0622d221 180 $this->assertEquals(count($result['merged']), 2, 'Check number of merged pairs.');
181
182 // retrieve pairs from prev next cache table
183 $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
184 $this->assertEquals(count($pnDupePairs), 1, 'Check number of remaining dupe pairs in prev next cache.');
185
186 $this->deleteDupeContacts();
187 }
188
c8ec0753 189 /**
a354251e 190 * Test the batch merge.
c8ec0753 191 */
0622d221 192 public function testBatchMergeAllDuplicates() {
193 $this->createDupeContacts();
194
195 // verify that all contacts have been created separately
196 $this->assertEquals(count($this->_contactIds), 9, 'Check for number of contacts.');
197
198 $dao = new CRM_Dedupe_DAO_RuleGroup();
199 $dao->contact_type = 'Individual';
200 $dao->name = 'IndividualSupervised';
201 $dao->is_default = 1;
202 $dao->find(TRUE);
203
204 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($dao->id, $this->_groupId);
205
206 // -------------------------------------------------------------------------
207 // Name and Email (reserved) Matches ( 3 pairs )
208 // --------------------------------------------------------------------------
209 // robin - hood - robin@example.com
210 // robin - hood - robin@example.com
211 // little - dale - dale@example.com
212 // little - dale - dale@example.com
213 // will - dale - will@example.com
214 // will - dale - will@example.com
215 // so 3 pairs for - first + last + mail
216 $this->assertEquals(count($foundDupes), 3, 'Check Individual-Supervised dupe rule for dupesInGroup().');
217
218 // Run dedupe finder as the browser would
219 $_SERVER['REQUEST_METHOD'] = 'GET'; //avoid invalid key error
220 $object = new CRM_Contact_Page_DedupeFind();
221 $object->set('gid', $this->_groupId);
222 $object->set('rgid', $dao->id);
223 $object->set('action', CRM_Core_Action::UPDATE);
64fe2fe0 224 $object->setEmbedded(TRUE);
0622d221 225 @$object->run();
226
227 // Retrieve pairs from prev next cache table
228 $select = array('pn.is_selected' => 'is_selected');
b1679439 229 $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($dao->id, $this->_groupId);
0622d221 230 $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
231
232 $this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.');
233
234 // batch merge all dupes
d13a105a 235 $result = CRM_Dedupe_Merger::batchMerge($dao->id, $this->_groupId, 'safe', 5, 2);
0622d221 236 $this->assertEquals(count($result['merged']), 3, 'Check number of merged pairs.');
237
238 // retrieve pairs from prev next cache table
239 $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
240 $this->assertEquals(count($pnDupePairs), 0, 'Check number of remaining dupe pairs in prev next cache.');
241
242 $this->deleteDupeContacts();
243 }
244
bf17fa88 245 /**
246 * The goal of this function is to test that all required tables are returned.
247 */
248 public function testGetCidRefs() {
249 $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, 'Contacts');
250 $this->assertEquals(array_merge($this->getStaticCIDRefs(), $this->getHackedInCIDRef()), CRM_Dedupe_Merger::cidRefs());
251 $this->assertEquals(array_merge($this->getCalculatedCIDRefs(), $this->getHackedInCIDRef()), CRM_Dedupe_Merger::cidRefs());
252 }
253
254 /**
255 * Get the list of not-really-cid-refs that are currently hacked in.
256 *
257 * This is hacked into getCIDs function.
258 *
259 * @return array
260 */
261 public function getHackedInCIDRef() {
262 return array(
263 'civicrm_entity_tag' => array(
264 0 => 'entity_id',
265 ),
266 );
267 }
268
2988f5c7 269 /**
270 * Test function that gets duplicate pairs.
271 *
272 * It turns out there are 2 code paths retrieving this data so my initial focus is on ensuring
273 * they match.
274 */
275 public function testGetMatches() {
276 $this->setupMatchData();
277 $pairs = CRM_Dedupe_Merger::getDuplicatePairs(
278 1,
279 NULL,
280 TRUE,
281 25,
282 FALSE
283 );
284
285 $this->assertEquals(array(
286 0 => array(
08cde01f 287 'srcID' => $this->contacts[1]['id'],
2988f5c7 288 'srcName' => 'Mr. Mickey Mouse II',
08cde01f 289 'dstID' => $this->contacts[0]['id'],
2988f5c7 290 'dstName' => 'Mr. Mickey Mouse II',
291 'weight' => 20,
292 'canMerge' => TRUE,
293 ),
3308aac0 294 1 => array(
08cde01f 295 'srcID' => $this->contacts[3]['id'],
3308aac0 296 'srcName' => 'Mr. Minnie Mouse II',
08cde01f 297 'dstID' => $this->contacts[2]['id'],
3308aac0 298 'dstName' => 'Mr. Minnie Mouse II',
299 'weight' => 20,
300 'canMerge' => TRUE,
301 ),
2988f5c7 302 ), $pairs);
303 }
304
bc0f3965 305 /**
306 * Test function that gets organization pairs.
307 *
308 * Note the rule will match on organization_name OR email - hence lots of matches.
309 */
310 public function testGetOrganizationMatches() {
311 $this->setupMatchData();
312 $ruleGroups = $this->callAPISuccessGetSingle('RuleGroup', array('contact_type' => 'Organization', 'used' => 'Supervised'));
313
314 $pairs = CRM_Dedupe_Merger::getDuplicatePairs(
315 $ruleGroups['id'],
316 NULL,
317 TRUE,
318 25,
319 FALSE
320 );
321
72475b30 322 $expectedPairs = array(
bc0f3965 323 0 => array(
324 'srcID' => $this->contacts[5]['id'],
325 'srcName' => 'Walt Disney Ltd',
326 'dstID' => $this->contacts[4]['id'],
327 'dstName' => 'Walt Disney Ltd',
328 'weight' => 20,
329 'canMerge' => TRUE,
330 ),
331 1 => array(
332 'srcID' => $this->contacts[7]['id'],
333 'srcName' => 'Walt Disney',
334 'dstID' => $this->contacts[6]['id'],
335 'dstName' => 'Walt Disney',
336 'weight' => 10,
337 'canMerge' => TRUE,
338 ),
339 2 => array(
340 'srcID' => $this->contacts[6]['id'],
341 'srcName' => 'Walt Disney',
342 'dstID' => $this->contacts[4]['id'],
343 'dstName' => 'Walt Disney Ltd',
344 'weight' => 10,
345 'canMerge' => TRUE,
346 ),
347 3 => array(
348 'srcID' => $this->contacts[6]['id'],
349 'srcName' => 'Walt Disney',
350 'dstID' => $this->contacts[5]['id'],
351 'dstName' => 'Walt Disney Ltd',
352 'weight' => 10,
353 'canMerge' => TRUE,
354 ),
72475b30
TO
355 );
356 usort($pairs, array(__CLASS__, 'compareDupes'));
357 usort($expectedPairs, array(__CLASS__, 'compareDupes'));
358 $this->assertEquals($expectedPairs, $pairs);
359 }
360
361 /**
362 * Function to sort $duplicate records in a stable way.
363 *
364 * @param array $a
365 * @param array $b
366 * @return int
367 */
368 public static function compareDupes($a, $b) {
369 foreach (array('srcName', 'dstName', 'srcID', 'dstID') as $field) {
370 if ($a[$field] != $b[$field]) {
371 return ($a[$field] < $b[$field]) ? 1 : -1;
372 }
373 }
374 return 0;
bc0f3965 375 }
376
377 /**
378 * Test function that gets organization duplicate pairs.
379 */
380 public function testGetOrganizationMatchesInGroup() {
381 $this->setupMatchData();
382 $ruleGroups = $this->callAPISuccessGetSingle('RuleGroup', array('contact_type' => 'Organization', 'used' => 'Supervised'));
383
384 $groupID = $this->groupCreate(array('title' => 'she-mice'));
385
386 $this->callAPISuccess('GroupContact', 'create', array('group_id' => $groupID, 'contact_id' => $this->contacts[4]['id']));
387
388 $pairs = CRM_Dedupe_Merger::getDuplicatePairs(
389 $ruleGroups['id'],
390 $groupID,
391 TRUE,
392 25,
393 FALSE
394 );
395
396 $this->assertEquals(array(
397 0 => array(
398 'srcID' => $this->contacts[5]['id'],
399 'srcName' => 'Walt Disney Ltd',
400 'dstID' => $this->contacts[4]['id'],
401 'dstName' => 'Walt Disney Ltd',
402 'weight' => 20,
403 'canMerge' => TRUE,
404 ),
405 1 => array(
406 'srcID' => $this->contacts[6]['id'],
407 'srcName' => 'Walt Disney',
408 'dstID' => $this->contacts[4]['id'],
409 'dstName' => 'Walt Disney Ltd',
410 'weight' => 10,
411 'canMerge' => TRUE,
412 ),
413 ), $pairs);
be61083d 414
415 $this->callAPISuccess('GroupContact', 'create', array('group_id' => $groupID, 'contact_id' => $this->contacts[5]['id']));
416 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_prevnext_cache");
417 $pairs = CRM_Dedupe_Merger::getDuplicatePairs(
418 $ruleGroups['id'],
419 $groupID,
420 TRUE,
421 25,
422 FALSE
423 );
424
425 $this->assertEquals(array(
426 0 => array(
427 'srcID' => $this->contacts[5]['id'],
428 'srcName' => 'Walt Disney Ltd',
429 'dstID' => $this->contacts[4]['id'],
430 'dstName' => 'Walt Disney Ltd',
431 'weight' => 20,
432 'canMerge' => TRUE,
433 ),
434 1 => array(
435 'srcID' => $this->contacts[6]['id'],
436 'srcName' => 'Walt Disney',
437 'dstID' => $this->contacts[4]['id'],
438 'dstName' => 'Walt Disney Ltd',
439 'weight' => 10,
440 'canMerge' => TRUE,
441 ),
442 2 => array(
443 'srcID' => $this->contacts[6]['id'],
444 'srcName' => 'Walt Disney',
445 'dstID' => $this->contacts[5]['id'],
446 'dstName' => 'Walt Disney Ltd',
447 'weight' => 10,
448 'canMerge' => TRUE,
449 ),
450 ), $pairs);
bc0f3965 451 }
452
3308aac0 453 /**
454 * Test function that gets duplicate pairs.
455 *
456 * It turns out there are 2 code paths retrieving this data so my initial focus is on ensuring
457 * they match.
458 */
459 public function testGetMatchesInGroup() {
460 $this->setupMatchData();
461
462 $groupID = $this->groupCreate(array('title' => 'she-mice'));
463
464 $this->callAPISuccess('GroupContact', 'create', array('group_id' => $groupID, 'contact_id' => $this->contacts[3]['id']));
465
466 $pairs = CRM_Dedupe_Merger::getDuplicatePairs(
467 1,
468 $groupID,
469 TRUE,
470 25,
471 FALSE
472 );
473
474 $this->assertEquals(array(
475 0 => array(
476 'srcID' => $this->contacts[3]['id'],
477 'srcName' => 'Mr. Minnie Mouse II',
478 'dstID' => $this->contacts[2]['id'],
479 'dstName' => 'Mr. Minnie Mouse II',
480 'weight' => 20,
481 'canMerge' => TRUE,
482 ),
483 ), $pairs);
484 }
485
0946ab3f 486 /**
487 * CRM-19653 : Test that custom field data should/shouldn't be overriden on
488 * selecting/not selecting option to migrate data respectively
489 */
490 public function testCustomDataOverwrite() {
dba77ae8
CR
491 // Create Custom Field
492 $createGroup = $this->setupCustomGroupForIndividual();
493 $createField = $this->setupCustomField('Graduation', $createGroup);
494 $customFieldName = "custom_" . $createField['id'];
495
496 // Contacts setup
0946ab3f 497 $this->setupMatchData();
498
499 $originalContactID = $this->contacts[0]['id'];
500 $duplicateContactID1 = $this->contacts[1]['id']; // used as duplicate contact in 1st use-case
501 $duplicateContactID2 = $this->contacts[2]['id']; // used as duplicate contact in 2nd use-case
502
0946ab3f 503 // update the text custom field for original contact with value 'abc'
504 $this->callAPISuccess('Contact', 'create', array(
505 'id' => $originalContactID,
dba77ae8 506 "{$customFieldName}" => 'abc',
0946ab3f 507 ));
dba77ae8
CR
508 $this->assertCustomFieldValue($originalContactID, 'abc', $customFieldName);
509
0946ab3f 510 // update the text custom field for duplicate contact 1 with value 'def'
511 $this->callAPISuccess('Contact', 'create', array(
512 'id' => $duplicateContactID1,
dba77ae8 513 "{$customFieldName}" => 'def',
0946ab3f 514 ));
dba77ae8
CR
515 $this->assertCustomFieldValue($duplicateContactID1, 'def', $customFieldName);
516
0946ab3f 517 // update the text custom field for duplicate contact 2 with value 'ghi'
518 $this->callAPISuccess('Contact', 'create', array(
519 'id' => $duplicateContactID2,
dba77ae8 520 "{$customFieldName}" => 'ghi',
0946ab3f 521 ));
dba77ae8 522 $this->assertCustomFieldValue($duplicateContactID2, 'ghi', $customFieldName);
0946ab3f 523
524 /*** USE-CASE 1: DO NOT OVERWRITE CUSTOM FIELD VALUE **/
dba77ae8 525 $this->mergeContacts($originalContactID, $duplicateContactID1, array(
ee3b1d86 526 "move_{$customFieldName}" => NULL,
0946ab3f 527 ));
dba77ae8 528 $this->assertCustomFieldValue($originalContactID, 'abc', $customFieldName);
0946ab3f 529
530 /*** USE-CASE 2: OVERWRITE CUSTOM FIELD VALUE **/
dba77ae8
CR
531 $this->mergeContacts($originalContactID, $duplicateContactID2, array(
532 "move_{$customFieldName}" => 'ghi',
533 ));
534 $this->assertCustomFieldValue($originalContactID, 'ghi', $customFieldName);
535
536 // cleanup created custom set
537 $this->callAPISuccess('CustomField', 'delete', array('id' => $createField['id']));
538 $this->callAPISuccess('CustomGroup', 'delete', array('id' => $createGroup['id']));
539 }
540
541 /**
542 * Verifies that when a contact with a custom field value is merged into a
543 * contact without a record int its corresponding custom group table, and none
544 * of the custom fields of that custom table are selected, the value is not
545 * merged in.
546 */
547 public function testMigrationOfUnselectedCustomDataOnEmptyCustomRecord() {
548 // Create Custom Fields
549 $createGroup = $this->setupCustomGroupForIndividual();
550 $customField1 = $this->setupCustomField('TestField', $createGroup);
551
552 // Contacts setup
553 $this->setupMatchData();
554 $originalContactID = $this->contacts[0]['id'];
555 $duplicateContactID = $this->contacts[1]['id'];
556
557 // Update the text custom fields for duplicate contact
558 $this->callAPISuccess('Contact', 'create', array(
559 'id' => $duplicateContactID,
560 "custom_{$customField1['id']}" => 'abc',
561 ));
562 $this->assertCustomFieldValue($duplicateContactID, 'abc', "custom_{$customField1['id']}");
563
564 $this->mergeContacts($originalContactID, $duplicateContactID, array(
ee3b1d86 565 "move_custom_{$customField1['id']}" => NULL,
dba77ae8
CR
566 ));
567 $this->assertCustomFieldValue($originalContactID, '', "custom_{$customField1['id']}");
568
569 // cleanup created custom set
570 $this->callAPISuccess('CustomField', 'delete', array('id' => $customField1['id']));
571 $this->callAPISuccess('CustomGroup', 'delete', array('id' => $createGroup['id']));
572 }
573
574 /**
575 * Tests that if only part of the custom fields of a custom group are selected
576 * for a merge, only those values are merged, while all other fields of the
577 * custom group retain their original value, specifically for a contact with
578 * no records on the custom group table.
579 */
580 public function testMigrationOfSomeCustomDataOnEmptyCustomRecord() {
581 // Create Custom Fields
582 $createGroup = $this->setupCustomGroupForIndividual();
583 $customField1 = $this->setupCustomField('Test1', $createGroup);
584 $customField2 = $this->setupCustomField('Test2', $createGroup);
585
586 // Contacts setup
587 $this->setupMatchData();
588 $originalContactID = $this->contacts[0]['id'];
589 $duplicateContactID = $this->contacts[1]['id'];
590
591 // Update the text custom fields for duplicate contact
592 $this->callAPISuccess('Contact', 'create', array(
593 'id' => $duplicateContactID,
594 "custom_{$customField1['id']}" => 'abc',
595 "custom_{$customField2['id']}" => 'def',
596 ));
597 $this->assertCustomFieldValue($duplicateContactID, 'abc', "custom_{$customField1['id']}");
598 $this->assertCustomFieldValue($duplicateContactID, 'def', "custom_{$customField2['id']}");
599
600 // Perform merge
601 $this->mergeContacts($originalContactID, $duplicateContactID, array(
ee3b1d86 602 "move_custom_{$customField1['id']}" => NULL,
dba77ae8
CR
603 "move_custom_{$customField2['id']}" => 'def',
604 ));
605 $this->assertCustomFieldValue($originalContactID, '', "custom_{$customField1['id']}");
606 $this->assertCustomFieldValue($originalContactID, 'def', "custom_{$customField2['id']}");
607
608 // cleanup created custom set
609 $this->callAPISuccess('CustomField', 'delete', array('id' => $customField1['id']));
610 $this->callAPISuccess('CustomField', 'delete', array('id' => $customField2['id']));
611 $this->callAPISuccess('CustomGroup', 'delete', array('id' => $createGroup['id']));
612 }
613
614 /**
615 * Calls merge method on given contacts, with values given in $params array.
616 *
617 * @param $originalContactID
618 * ID of target contact
619 * @param $duplicateContactID
620 * ID of contact to be merged
621 * @param $params
622 * Array of fields to be merged from source into target contact, of the form
623 * ['move_<fieldName>' => <fieldValue>]
624 */
625 private function mergeContacts($originalContactID, $duplicateContactID, $params) {
626 $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($originalContactID, $duplicateContactID);
627
0946ab3f 628 $migrationData = array(
629 'main_details' => $rowsElementsAndInfo['main_details'],
630 'other_details' => $rowsElementsAndInfo['other_details'],
0946ab3f 631 );
dba77ae8
CR
632
633 // Migrate data of duplicate contact
634 CRM_Dedupe_Merger::moveAllBelongings($originalContactID, $duplicateContactID, array_merge($migrationData, $params));
635 }
636
637 /**
638 * Checks if the expected value for the given field corresponds to what is
639 * stored in the database for the given contact ID.
640 *
641 * @param $contactID
642 * @param $expectedValue
643 * @param $customFieldName
644 */
645 private function assertCustomFieldValue($contactID, $expectedValue, $customFieldName) {
0946ab3f 646 $data = $this->callAPISuccess('Contact', 'getsingle', array(
dba77ae8 647 'id' => $contactID,
0946ab3f 648 'return' => array($customFieldName),
649 ));
0946ab3f 650
dba77ae8
CR
651 $this->assertEquals($expectedValue, $data[$customFieldName], "Custom field value was supposed to be '{$expectedValue}', '{$data[$customFieldName]}' found.");
652 }
653
654 /**
655 * Creates a custom group to run tests on contacts that are individuals.
656 *
657 * @return array
658 * Data for the created custom group record
659 */
660 private function setupCustomGroupForIndividual() {
661 $customGroup = $this->callAPISuccess('custom_group', 'get', array(
662 'name' => 'test_group',
663 ));
664
665 if ($customGroup['count'] > 0) {
666 $this->callAPISuccess('CustomGroup', 'delete', array('id' => $customGroup['id']));
667 }
668
669 $customGroup = $this->callAPISuccess('custom_group', 'create', array(
670 'title' => 'Test_Group',
671 'name' => 'test_group',
672 'extends' => array('Individual'),
673 'style' => 'Inline',
674 'is_multiple' => FALSE,
675 'is_active' => 1,
676 ));
677
678 return $customGroup;
679 }
680
681 /**
682 * Creates a custom field on the provided custom group with the given field
683 * label.
684 *
685 * @param $fieldLabel
686 * @param $createGroup
687 *
688 * @return array
689 * Data for the created custom field record
690 */
691 private function setupCustomField($fieldLabel, $createGroup) {
692 return $this->callAPISuccess('custom_field', 'create', array(
693 'label' => $fieldLabel,
694 'data_type' => 'Alphanumeric',
695 'html_type' => 'Text',
696 'custom_group_id' => $createGroup['id'],
697 ));
0946ab3f 698 }
699
3308aac0 700 /**
701 * Set up some contacts for our matching.
702 */
2988f5c7 703 public function setupMatchData() {
704 $fixtures = array(
705 array(
706 'first_name' => 'Mickey',
707 'last_name' => 'Mouse',
708 'email' => 'mickey@mouse.com',
709 ),
710 array(
711 'first_name' => 'Mickey',
712 'last_name' => 'Mouse',
713 'email' => 'mickey@mouse.com',
714 ),
715 array(
716 'first_name' => 'Minnie',
717 'last_name' => 'Mouse',
718 'email' => 'mickey@mouse.com',
719 ),
3308aac0 720 array(
721 'first_name' => 'Minnie',
722 'last_name' => 'Mouse',
723 'email' => 'mickey@mouse.com',
724 ),
2988f5c7 725 );
726 foreach ($fixtures as $fixture) {
727 $contactID = $this->individualCreate($fixture);
728 $this->contacts[] = array_merge($fixture, array('id' => $contactID));
bc0f3965 729 }
730 $organizationFixtures = array(
731 array(
732 'organization_name' => 'Walt Disney Ltd',
733 'email' => 'walt@disney.com',
734 ),
735 array(
736 'organization_name' => 'Walt Disney Ltd',
737 'email' => 'walt@disney.com',
738 ),
739 array(
740 'organization_name' => 'Walt Disney',
741 'email' => 'walt@disney.com',
742 ),
743 array(
744 'organization_name' => 'Walt Disney',
745 'email' => 'walter@disney.com',
746 ),
747 );
748 foreach ($organizationFixtures as $fixture) {
749 $contactID = $this->organizationCreate($fixture);
750 $this->contacts[] = array_merge($fixture, array('id' => $contactID));
2988f5c7 751 }
752 }
753
754
bf17fa88 755 /**
756 * Get the list of tables that refer to the CID.
757 *
758 * This is a statically maintained (in this test list).
759 *
760 * There is also a check against an automated list but having both seems to add extra stability to me. They do
761 * not change often.
762 */
763 public function getStaticCIDRefs() {
764 return array(
765 'civicrm_acl_cache' => array(
766 0 => 'contact_id',
767 ),
768 'civicrm_acl_contact_cache' => array(
769 0 => 'user_id',
770 1 => 'contact_id',
771 ),
772 'civicrm_action_log' => array(
773 0 => 'contact_id',
774 ),
775 'civicrm_activity_contact' => array(
776 0 => 'contact_id',
777 ),
778 'civicrm_address' => array(
779 0 => 'contact_id',
780 ),
781 'civicrm_batch' => array(
782 0 => 'created_id',
783 1 => 'modified_id',
784 ),
785 'civicrm_campaign' => array(
786 0 => 'created_id',
787 1 => 'last_modified_id',
788 ),
789 'civicrm_case_contact' => array(
790 0 => 'contact_id',
791 ),
792 'civicrm_contact' => array(
793 0 => 'primary_contact_id',
794 1 => 'employer_id',
795 ),
796 'civicrm_contribution' => array(
797 0 => 'contact_id',
798 ),
799 'civicrm_contribution_page' => array(
800 0 => 'created_id',
801 ),
802 'civicrm_contribution_recur' => array(
803 0 => 'contact_id',
804 ),
805 'civicrm_contribution_soft' => array(
806 0 => 'contact_id',
807 ),
808 'civicrm_custom_group' => array(
809 0 => 'created_id',
810 ),
811 'civicrm_dashboard_contact' => array(
812 0 => 'contact_id',
813 ),
814 'civicrm_dedupe_exception' => array(
815 0 => 'contact_id1',
816 1 => 'contact_id2',
817 ),
818 'civicrm_domain' => array(
819 0 => 'contact_id',
820 ),
821 'civicrm_email' => array(
822 0 => 'contact_id',
823 ),
824 'civicrm_event' => array(
825 0 => 'created_id',
826 ),
827 'civicrm_event_carts' => array(
828 0 => 'user_id',
829 ),
830 'civicrm_financial_account' => array(
831 0 => 'contact_id',
832 ),
833 'civicrm_financial_item' => array(
834 0 => 'contact_id',
835 ),
836 'civicrm_grant' => array(
837 0 => 'contact_id',
838 ),
839 'civicrm_group' => array(
840 0 => 'created_id',
841 1 => 'modified_id',
842 ),
843 'civicrm_group_contact' => array(
844 0 => 'contact_id',
845 ),
846 'civicrm_group_contact_cache' => array(
847 0 => 'contact_id',
848 ),
849 'civicrm_group_organization' => array(
850 0 => 'organization_id',
851 ),
852 'civicrm_im' => array(
853 0 => 'contact_id',
854 ),
855 'civicrm_log' => array(
856 0 => 'modified_id',
857 ),
858 'civicrm_mailing' => array(
859 0 => 'created_id',
860 1 => 'scheduled_id',
861 2 => 'approver_id',
862 ),
863 'civicrm_mailing_abtest' => array(
864 0 => 'created_id',
865 ),
866 'civicrm_mailing_event_queue' => array(
867 0 => 'contact_id',
868 ),
869 'civicrm_mailing_event_subscribe' => array(
870 0 => 'contact_id',
871 ),
872 'civicrm_mailing_recipients' => array(
873 0 => 'contact_id',
874 ),
875 'civicrm_membership' => array(
876 0 => 'contact_id',
877 ),
878 'civicrm_membership_log' => array(
879 0 => 'modified_id',
880 ),
881 'civicrm_membership_type' => array(
882 0 => 'member_of_contact_id',
883 ),
884 'civicrm_note' => array(
885 0 => 'contact_id',
886 ),
887 'civicrm_openid' => array(
888 0 => 'contact_id',
889 ),
890 'civicrm_participant' => array(
891 0 => 'contact_id',
101d0fef 892 1 => 'transferred_to_contact_id', //CRM-16761
bf17fa88 893 ),
894 'civicrm_payment_token' => array(
895 0 => 'contact_id',
896 1 => 'created_id',
897 ),
898 'civicrm_pcp' => array(
899 0 => 'contact_id',
900 ),
901 'civicrm_phone' => array(
902 0 => 'contact_id',
903 ),
904 'civicrm_pledge' => array(
905 0 => 'contact_id',
906 ),
907 'civicrm_print_label' => array(
908 0 => 'created_id',
909 ),
910 'civicrm_relationship' => array(
911 0 => 'contact_id_a',
912 1 => 'contact_id_b',
913 ),
914 'civicrm_report_instance' => array(
915 0 => 'created_id',
916 1 => 'owner_id',
917 ),
918 'civicrm_setting' => array(
919 0 => 'contact_id',
920 1 => 'created_id',
921 ),
922 'civicrm_subscription_history' => array(
923 0 => 'contact_id',
924 ),
925 'civicrm_survey' => array(
926 0 => 'created_id',
927 1 => 'last_modified_id',
928 ),
929 'civicrm_tag' => array(
930 0 => 'created_id',
931 ),
932 'civicrm_uf_group' => array(
933 0 => 'created_id',
934 ),
935 'civicrm_uf_match' => array(
936 0 => 'contact_id',
937 ),
938 'civicrm_value_testgetcidref_1' => array(
939 0 => 'entity_id',
940 ),
941 'civicrm_website' => array(
942 0 => 'contact_id',
943 ),
944 );
945 }
946
947 /**
948 * Get a list of CIDs that is calculated off the schema.
949 *
950 * Note this is an expensive and table locking query. Should be safe in tests though.
951 */
952 public function getCalculatedCIDRefs() {
953 $cidRefs = array();
954 $sql = "
955SELECT
956 table_name,
957 column_name
958FROM information_schema.key_column_usage
959WHERE
960 referenced_table_schema = database() AND
961 referenced_table_name = 'civicrm_contact' AND
962 referenced_column_name = 'id';
963 ";
964 $dao = CRM_Core_DAO::executeQuery($sql);
965 while ($dao->fetch()) {
966 $cidRefs[$dao->table_name][] = $dao->column_name;
967 }
968 // Do specific re-ordering changes to make this the same as the ref validated one.
969 // The above query orders by FK alphabetically.
970 // There might be cleverer ways to do this but it shouldn't change much.
971 $cidRefs['civicrm_contact'][0] = 'primary_contact_id';
972 $cidRefs['civicrm_contact'][1] = 'employer_id';
973 $cidRefs['civicrm_acl_contact_cache'][0] = 'user_id';
974 $cidRefs['civicrm_acl_contact_cache'][1] = 'contact_id';
975 $cidRefs['civicrm_mailing'][0] = 'created_id';
976 $cidRefs['civicrm_mailing'][1] = 'scheduled_id';
977 $cidRefs['civicrm_mailing'][2] = 'approver_id';
978 return $cidRefs;
979 }
980
0622d221 981}