Commit | Line | Data |
---|---|---|
6a488035 | 1 | <?php |
0eea664b | 2 | |
aba1cd8b EM |
3 | /** |
4 | * Class CRM_Dedupe_DedupeFinderTest | |
acb109b7 | 5 | * @group headless |
aba1cd8b | 6 | */ |
36741dcf | 7 | class CRM_Dedupe_DedupeFinderTest extends CiviUnitTestCase { |
a2dd33d3 | 8 | |
692e041d | 9 | use CRMTraits_Custom_CustomDataTrait; |
a2dd33d3 | 10 | /** |
11 | * IDs of created contacts. | |
12 | * | |
13 | * @var array | |
14 | */ | |
9099cab3 | 15 | protected $contactIDs = []; |
a2dd33d3 | 16 | |
17 | /** | |
18 | * ID of the group holding the contacts. | |
19 | * | |
20 | * @var int | |
21 | */ | |
22 | protected $groupID; | |
23 | ||
24 | /** | |
25 | * Clean up after the test. | |
2cbe6e87 | 26 | * |
27 | * @throws \CRM_Core_Exception | |
a2dd33d3 | 28 | */ |
29 | public function tearDown() { | |
30 | ||
31 | foreach ($this->contactIDs as $contactId) { | |
32 | $this->contactDelete($contactId); | |
33 | } | |
34 | if ($this->groupID) { | |
9099cab3 | 35 | $this->callAPISuccess('group', 'delete', ['id' => $this->groupID]); |
a2dd33d3 | 36 | } |
692e041d | 37 | $this->quickCleanup(['civicrm_contact'], TRUE); |
38 | CRM_Core_DAO::executeQuery("DELETE r FROM civicrm_dedupe_rule_group rg INNER JOIN civicrm_dedupe_rule r ON rg.id = r.dedupe_rule_group_id WHERE rg.is_reserved = 0 AND used = 'General'"); | |
39 | CRM_Core_DAO::executeQuery("DELETE FROM civicrm_dedupe_rule_group WHERE is_reserved = 0 AND used = 'General'"); | |
40 | ||
a2dd33d3 | 41 | parent::tearDown(); |
42 | } | |
43 | ||
44 | /** | |
45 | * Test the unsupervised dedupe rule against a group. | |
46 | * | |
47 | * @throws \Exception | |
48 | */ | |
49 | public function testUnsupervisedDupes() { | |
367c9a2d | 50 | // make dupe checks based on following contact sets: |
6a488035 TO |
51 | // FIRST - LAST - EMAIL |
52 | // --------------------------------- | |
53 | // robin - hood - robin@example.com | |
54 | // robin - hood - hood@example.com | |
55 | // robin - dale - robin@example.com | |
56 | // little - dale - dale@example.com | |
57 | // will - dale - dale@example.com | |
58 | // will - dale - will@example.com | |
59 | // will - dale - will@example.com | |
a2dd33d3 | 60 | $this->setupForGroupDedupe(); |
6a488035 | 61 | |
9099cab3 | 62 | $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', ['is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Unsupervised']); |
87a56b12 | 63 | |
a2dd33d3 | 64 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); |
65 | $this->assertEquals(count($foundDupes), 3, 'Check Individual-Fuzzy dupe rule for dupesInGroup().'); | |
66 | } | |
67 | ||
367c9a2d JP |
68 | /** |
69 | * Test duplicate contact retrieval with 2 email fields. | |
2cbe6e87 | 70 | * |
71 | * @throws \CRM_Core_Exception | |
367c9a2d JP |
72 | */ |
73 | public function testUnsupervisedWithTwoEmailFields() { | |
74 | $this->setupForGroupDedupe(); | |
75 | $emails = [ | |
76 | ['hood@example.com', ''], | |
77 | ['', 'hood@example.com'], | |
78 | ]; | |
79 | for ($i = 0; $i < 2; $i++) { | |
80 | $fields = [ | |
81 | 'first_name' => 'robin', | |
82 | 'last_name' => 'hood', | |
83 | 'email-1' => $emails[$i][0], | |
84 | 'email-2' => $emails[$i][1], | |
85 | ]; | |
86 | $dedupeParams = CRM_Dedupe_Finder::formatParams($fields, 'Individual'); | |
87 | $dedupeResults = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual'); | |
88 | $this->assertEquals(count($dedupeResults), 1); | |
89 | } | |
90 | } | |
91 | ||
ce217e17 | 92 | /** |
93 | * Test that a rule set to is_reserved = 0 works. | |
94 | * | |
95 | * There is a different search used dependent on this variable. | |
2cbe6e87 | 96 | * |
97 | * @throws \CRM_Core_Exception | |
ce217e17 | 98 | */ |
cf6f63dd AH |
99 | public function testCustomRule() { |
100 | $this->setupForGroupDedupe(); | |
101 | ||
692e041d | 102 | $ruleGroup = $this->createRuleGroup(); |
9099cab3 | 103 | foreach (['birth_date', 'first_name', 'last_name'] as $field) { |
ce217e17 | 104 | $rules[$field] = $this->callAPISuccess('Rule', 'create', [ |
69947f59 | 105 | 'dedupe_rule_group_id' => $ruleGroup['id'], |
106 | 'rule_table' => 'civicrm_contact', | |
107 | 'rule_weight' => 4, | |
108 | 'rule_field' => $field, | |
109 | ]); | |
cf6f63dd AH |
110 | } |
111 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); | |
112 | $this->assertEquals(count($foundDupes), 4); | |
ce217e17 | 113 | CRM_Dedupe_Finder::dupes($ruleGroup['id']); |
114 | ||
cf6f63dd AH |
115 | } |
116 | ||
692e041d | 117 | /** |
118 | * Test that we do not get a fatal error when our rule group is a custom date field. | |
119 | * | |
120 | * @throws \CRM_Core_Exception | |
121 | */ | |
122 | public function testCustomRuleCustomDateField() { | |
123 | ||
124 | $ruleGroup = $this->createRuleGroup(); | |
125 | $this->createCustomGroupWithFieldOfType([], 'date'); | |
126 | $this->callAPISuccess('Rule', 'create', [ | |
127 | 'dedupe_rule_group_id' => $ruleGroup['id'], | |
128 | 'rule_table' => $this->getCustomGroupTable(), | |
129 | 'rule_weight' => 4, | |
130 | 'rule_field' => $this->getCustomFieldColumnName('date'), | |
131 | ]); | |
132 | ||
133 | CRM_Dedupe_Finder::dupes($ruleGroup['id']); | |
134 | } | |
135 | ||
92eadb36 D |
136 | /** |
137 | * Test a custom rule with a non-default field. | |
2cbe6e87 | 138 | * |
139 | * @throws \CRM_Core_Exception | |
92eadb36 D |
140 | */ |
141 | public function testCustomRuleWithAddress() { | |
142 | $this->setupForGroupDedupe(); | |
143 | ||
9099cab3 | 144 | $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [ |
92eadb36 D |
145 | 'contact_type' => 'Individual', |
146 | 'threshold' => 10, | |
147 | 'used' => 'General', | |
148 | 'name' => 'TestRule', | |
149 | 'title' => 'TestRule', | |
150 | 'is_reserved' => 0, | |
9099cab3 | 151 | ]); |
92eadb36 | 152 | $rules = []; |
9099cab3 | 153 | foreach (['postal_code'] as $field) { |
92eadb36 D |
154 | $rules[$field] = $this->callAPISuccess('Rule', 'create', [ |
155 | 'dedupe_rule_group_id' => $ruleGroup['id'], | |
156 | 'rule_table' => 'civicrm_address', | |
157 | 'rule_weight' => 10, | |
158 | 'rule_field' => $field, | |
159 | ]); | |
160 | } | |
161 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); | |
162 | $this->assertEquals(count($foundDupes), 1); | |
163 | CRM_Dedupe_Finder::dupes($ruleGroup['id']); | |
164 | ||
165 | } | |
166 | ||
9742d8f3 SL |
167 | /** |
168 | * Test rule from Richard | |
2cbe6e87 | 169 | * |
170 | * @throws \CRM_Core_Exception | |
9742d8f3 SL |
171 | */ |
172 | public function testRuleThreeContactFieldsEqualWeightWIthThresholdtheTotalSumOfAllWeight() { | |
173 | $this->setupForGroupDedupe(); | |
174 | ||
175 | $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [ | |
176 | 'contact_type' => 'Individual', | |
177 | 'threshold' => 30, | |
178 | 'used' => 'General', | |
179 | 'name' => 'TestRule', | |
180 | 'title' => 'TestRule', | |
181 | 'is_reserved' => 0, | |
182 | ]); | |
183 | ||
184 | foreach (['first_name', 'last_name', 'birth_date'] as $field) { | |
185 | $rules[$field] = $this->callAPISuccess('Rule', 'create', [ | |
186 | 'dedupe_rule_group_id' => $ruleGroup['id'], | |
187 | 'rule_table' => 'civicrm_contact', | |
188 | 'rule_weight' => 10, | |
189 | 'rule_field' => $field, | |
190 | ]); | |
191 | } | |
192 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); | |
2cbe6e87 | 193 | $this->assertCount(1, $foundDupes); |
9742d8f3 SL |
194 | } |
195 | ||
197e72c4 SL |
196 | /** |
197 | * Test a custom rule with a non-default field. | |
2cbe6e87 | 198 | * |
199 | * @throws \CRM_Core_Exception | |
197e72c4 SL |
200 | */ |
201 | public function testInclusiveRule() { | |
202 | $this->setupForGroupDedupe(); | |
203 | ||
692e041d | 204 | $ruleGroup = $this->createRuleGroup(); |
197e72c4 SL |
205 | foreach (['first_name', 'last_name'] as $field) { |
206 | $rules[$field] = $this->callAPISuccess('Rule', 'create', [ | |
207 | 'dedupe_rule_group_id' => $ruleGroup['id'], | |
208 | 'rule_table' => 'civicrm_contact', | |
209 | 'rule_weight' => 4, | |
210 | 'rule_field' => $field, | |
211 | ]); | |
212 | } | |
213 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); | |
2cbe6e87 | 214 | $this->assertCount(4, $foundDupes); |
197e72c4 SL |
215 | CRM_Dedupe_Finder::dupes($ruleGroup['id']); |
216 | } | |
217 | ||
a2dd33d3 | 218 | /** |
219 | * Test the supervised dedupe rule against a group. | |
220 | * | |
221 | * @throws \Exception | |
222 | */ | |
223 | public function testSupervisedDupes() { | |
224 | $this->setupForGroupDedupe(); | |
9099cab3 | 225 | $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', ['is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Supervised']); |
a2dd33d3 | 226 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID); |
227 | // ------------------------------------------------------------------------- | |
228 | // default dedupe rule: threshold = 20 => (First + Last + Email) Matches ( 1 pair ) | |
229 | // -------------------------------------------------------------------------- | |
230 | // will - dale - will@example.com | |
231 | // will - dale - will@example.com | |
232 | // so 1 pair for - first + last + mail | |
233 | $this->assertEquals(count($foundDupes), 1, 'Check Individual-Fuzzy dupe rule for dupesInGroup().'); | |
234 | } | |
235 | ||
236 | /** | |
237 | * Test dupesByParams function. | |
2cbe6e87 | 238 | * |
239 | * @throws \CRM_Core_Exception | |
a2dd33d3 | 240 | */ |
241 | public function testDupesByParams() { | |
242 | // make dupe checks based on based on following contact sets: | |
243 | // FIRST - LAST - EMAIL | |
244 | // --------------------------------- | |
245 | // robin - hood - robin@example.com | |
246 | // robin - hood - hood@example.com | |
247 | // robin - dale - robin@example.com | |
248 | // little - dale - dale@example.com | |
249 | // will - dale - dale@example.com | |
250 | // will - dale - will@example.com | |
251 | // will - dale - will@example.com | |
6a488035 TO |
252 | |
253 | // contact data set | |
254 | // FIXME: move create params to separate function | |
9099cab3 CW |
255 | $params = [ |
256 | [ | |
6a488035 TO |
257 | 'first_name' => 'robin', |
258 | 'last_name' => 'hood', | |
259 | 'email' => 'robin@example.com', | |
260 | 'contact_type' => 'Individual', | |
9099cab3 CW |
261 | ], |
262 | [ | |
6a488035 TO |
263 | 'first_name' => 'robin', |
264 | 'last_name' => 'hood', | |
265 | 'email' => 'hood@example.com', | |
266 | 'contact_type' => 'Individual', | |
9099cab3 CW |
267 | ], |
268 | [ | |
6a488035 TO |
269 | 'first_name' => 'robin', |
270 | 'last_name' => 'dale', | |
271 | 'email' => 'robin@example.com', | |
272 | 'contact_type' => 'Individual', | |
9099cab3 CW |
273 | ], |
274 | [ | |
6a488035 TO |
275 | 'first_name' => 'little', |
276 | 'last_name' => 'dale', | |
277 | 'email' => 'dale@example.com', | |
278 | 'contact_type' => 'Individual', | |
9099cab3 CW |
279 | ], |
280 | [ | |
6a488035 TO |
281 | 'first_name' => 'will', |
282 | 'last_name' => 'dale', | |
283 | 'email' => 'dale@example.com', | |
284 | 'contact_type' => 'Individual', | |
9099cab3 CW |
285 | ], |
286 | [ | |
6a488035 TO |
287 | 'first_name' => 'will', |
288 | 'last_name' => 'dale', | |
289 | 'email' => 'will@example.com', | |
290 | 'contact_type' => 'Individual', | |
9099cab3 CW |
291 | ], |
292 | [ | |
6a488035 TO |
293 | 'first_name' => 'will', |
294 | 'last_name' => 'dale', | |
295 | 'email' => 'will@example.com', | |
296 | 'contact_type' => 'Individual', | |
9099cab3 CW |
297 | ], |
298 | ]; | |
6a488035 | 299 | |
9099cab3 | 300 | $this->hookClass->setHook('civicrm_findDuplicates', [$this, 'hook_civicrm_findDuplicates']); |
b8cb7e46 | 301 | |
6a488035 | 302 | $count = 1; |
a2dd33d3 | 303 | |
6a488035 | 304 | foreach ($params as $param) { |
87a56b12 | 305 | $contact = $this->callAPISuccess('contact', 'create', $param); |
9099cab3 | 306 | $params = [ |
6a488035 | 307 | 'contact_id' => $contact['id'], |
a2dd33d3 | 308 | 'street_address' => 'Ambachtstraat 23', |
309 | 'location_type_id' => 1, | |
9099cab3 | 310 | ]; |
a2dd33d3 | 311 | $this->callAPISuccess('address', 'create', $params); |
312 | $contactIds[$count++] = $contact['id']; | |
6a488035 TO |
313 | } |
314 | ||
315 | // verify that all contacts have been created separately | |
316 | $this->assertEquals(count($contactIds), 7, 'Check for number of contacts.'); | |
317 | ||
9099cab3 | 318 | $fields = [ |
a2dd33d3 | 319 | 'first_name' => 'robin', |
320 | 'last_name' => 'hood', | |
321 | 'email' => 'hood@example.com', | |
322 | 'street_address' => 'Ambachtstraat 23', | |
9099cab3 | 323 | ]; |
b8cb7e46 | 324 | $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($fields, 'Individual', 'General', [], TRUE, NULL, ['event_id' => 1]); |
6a488035 | 325 | |
a2dd33d3 | 326 | // Check with default Individual-General rule |
327 | $this->assertEquals(count($ids), 2, 'Check Individual-General rule for dupesByParams().'); | |
6a488035 | 328 | |
a2dd33d3 | 329 | // delete all created contacts |
6a488035 | 330 | foreach ($contactIds as $contactId) { |
93ac19cd | 331 | $this->contactDelete($contactId); |
6a488035 | 332 | } |
6a488035 TO |
333 | } |
334 | ||
b8cb7e46 MWMC |
335 | /** |
336 | * Implements hook_civicrm_findDuplicates(). | |
337 | * | |
338 | * Locks in expected params | |
339 | * | |
340 | */ | |
341 | public function hook_civicrm_findDuplicates($dedupeParams, &$dedupeResults, $contextParams) { | |
342 | $expectedDedupeParams = [ | |
343 | 'check_permission' => TRUE, | |
344 | 'contact_type' => 'Individual', | |
345 | 'rule' => 'General', | |
346 | 'rule_group_id' => NULL, | |
347 | 'excluded_contact_ids' => [], | |
348 | ]; | |
349 | foreach ($expectedDedupeParams as $key => $value) { | |
350 | $this->assertEquals($value, $dedupeParams[$key]); | |
351 | } | |
352 | $expectedDedupeResults = [ | |
353 | 'ids' => [], | |
354 | 'handled' => FALSE, | |
355 | ]; | |
356 | foreach ($expectedDedupeResults as $key => $value) { | |
357 | $this->assertEquals($value, $dedupeResults[$key]); | |
358 | } | |
359 | ||
360 | $expectedContext = ['event_id' => 1]; | |
361 | foreach ($expectedContext as $key => $value) { | |
362 | $this->assertEquals($value, $contextParams[$key]); | |
363 | } | |
364 | ||
365 | return $dedupeResults; | |
366 | } | |
367 | ||
a2dd33d3 | 368 | /** |
369 | * Set up a group of dedupable contacts. | |
2cbe6e87 | 370 | * |
371 | * @throws \CRM_Core_Exception | |
a2dd33d3 | 372 | */ |
373 | protected function setupForGroupDedupe() { | |
9099cab3 | 374 | $params = [ |
a2dd33d3 | 375 | 'name' => 'Dupe Group', |
376 | 'title' => 'New Test Dupe Group', | |
377 | 'domain_id' => 1, | |
378 | 'is_active' => 1, | |
379 | 'visibility' => 'Public Pages', | |
9099cab3 | 380 | ]; |
a2dd33d3 | 381 | |
382 | $result = $this->callAPISuccess('group', 'create', $params); | |
383 | $this->groupID = $result['id']; | |
6a488035 | 384 | |
9099cab3 CW |
385 | $params = [ |
386 | [ | |
6a488035 TO |
387 | 'first_name' => 'robin', |
388 | 'last_name' => 'hood', | |
389 | 'email' => 'robin@example.com', | |
390 | 'contact_type' => 'Individual', | |
ce217e17 | 391 | 'birth_date' => '2016-01-01', |
197e72c4 | 392 | 'api.Address.create' => ['street_address' => '123 Happy world', 'location_type_id' => 'Billing', 'postal_code' => '99999'], |
9099cab3 CW |
393 | ], |
394 | [ | |
6a488035 TO |
395 | 'first_name' => 'robin', |
396 | 'last_name' => 'hood', | |
397 | 'email' => 'hood@example.com', | |
398 | 'contact_type' => 'Individual', | |
ce217e17 | 399 | 'birth_date' => '2016-01-01', |
197e72c4 | 400 | 'api.Address.create' => ['street_address' => '123 Happy World', 'location_type_id' => 'Billing', 'postal_code' => '99999'], |
9099cab3 CW |
401 | ], |
402 | [ | |
6a488035 TO |
403 | 'first_name' => 'robin', |
404 | 'last_name' => 'dale', | |
405 | 'email' => 'robin@example.com', | |
406 | 'contact_type' => 'Individual', | |
9099cab3 CW |
407 | ], |
408 | [ | |
6a488035 TO |
409 | 'first_name' => 'little', |
410 | 'last_name' => 'dale', | |
411 | 'email' => 'dale@example.com', | |
412 | 'contact_type' => 'Individual', | |
9099cab3 CW |
413 | ], |
414 | [ | |
6a488035 TO |
415 | 'first_name' => 'will', |
416 | 'last_name' => 'dale', | |
417 | 'email' => 'dale@example.com', | |
418 | 'contact_type' => 'Individual', | |
9099cab3 CW |
419 | ], |
420 | [ | |
6a488035 TO |
421 | 'first_name' => 'will', |
422 | 'last_name' => 'dale', | |
423 | 'email' => 'will@example.com', | |
424 | 'contact_type' => 'Individual', | |
9099cab3 CW |
425 | ], |
426 | [ | |
6a488035 TO |
427 | 'first_name' => 'will', |
428 | 'last_name' => 'dale', | |
429 | 'email' => 'will@example.com', | |
430 | 'contact_type' => 'Individual', | |
9099cab3 CW |
431 | ], |
432 | ]; | |
6a488035 TO |
433 | |
434 | $count = 1; | |
6a488035 | 435 | foreach ($params as $param) { |
87a56b12 | 436 | $contact = $this->callAPISuccess('contact', 'create', $param); |
a2dd33d3 | 437 | $this->contactIDs[$count++] = $contact['id']; |
438 | ||
9099cab3 | 439 | $grpParams = [ |
6a488035 | 440 | 'contact_id' => $contact['id'], |
a2dd33d3 | 441 | 'group_id' => $this->groupID, |
9099cab3 | 442 | ]; |
a2dd33d3 | 443 | $this->callAPISuccess('group_contact', 'create', $grpParams); |
6a488035 TO |
444 | } |
445 | ||
446 | // verify that all contacts have been created separately | |
a2dd33d3 | 447 | $this->assertEquals(count($this->contactIDs), 7, 'Check for number of contacts.'); |
6a488035 | 448 | } |
96025800 | 449 | |
6a488035 | 450 | } |