4 * Class CRM_Dedupe_DedupeFinderTest
7 class CRM_Dedupe_DedupeFinderTest
extends CiviUnitTestCase
{
10 * IDs of created contacts.
14 protected $contactIDs = [];
17 * ID of the group holding the contacts.
24 * Clean up after the test.
26 * @throws \CRM_Core_Exception
28 public function tearDown() {
30 foreach ($this->contactIDs
as $contactId) {
31 $this->contactDelete($contactId);
34 $this->callAPISuccess('group', 'delete', ['id' => $this->groupID
]);
40 * Test the unsupervised dedupe rule against a group.
44 public function testUnsupervisedDupes() {
45 // make dupe checks based on following contact sets:
46 // FIRST - LAST - EMAIL
47 // ---------------------------------
48 // robin - hood - robin@example.com
49 // robin - hood - hood@example.com
50 // robin - dale - robin@example.com
51 // little - dale - dale@example.com
52 // will - dale - dale@example.com
53 // will - dale - will@example.com
54 // will - dale - will@example.com
55 $this->setupForGroupDedupe();
57 $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', ['is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Unsupervised']);
59 $foundDupes = CRM_Dedupe_Finder
::dupesInGroup($ruleGroup['id'], $this->groupID
);
60 $this->assertEquals(count($foundDupes), 3, 'Check Individual-Fuzzy dupe rule for dupesInGroup().');
64 * Test duplicate contact retrieval with 2 email fields.
66 * @throws \CRM_Core_Exception
68 public function testUnsupervisedWithTwoEmailFields() {
69 $this->setupForGroupDedupe();
71 ['hood@example.com', ''],
72 ['', 'hood@example.com'],
74 for ($i = 0; $i < 2; $i++
) {
76 'first_name' => 'robin',
77 'last_name' => 'hood',
78 'email-1' => $emails[$i][0],
79 'email-2' => $emails[$i][1],
81 $dedupeParams = CRM_Dedupe_Finder
::formatParams($fields, 'Individual');
82 $dedupeResults = CRM_Dedupe_Finder
::dupesByParams($dedupeParams, 'Individual');
83 $this->assertEquals(count($dedupeResults), 1);
88 * Test that a rule set to is_reserved = 0 works.
90 * There is a different search used dependent on this variable.
92 * @throws \CRM_Core_Exception
94 public function testCustomRule() {
95 $this->setupForGroupDedupe();
97 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
98 'contact_type' => 'Individual',
101 'name' => 'TestRule',
102 'title' => 'TestRule',
106 foreach (['birth_date', 'first_name', 'last_name'] as $field) {
107 $rules[$field] = $this->callAPISuccess('Rule', 'create', [
108 'dedupe_rule_group_id' => $ruleGroup['id'],
109 'rule_table' => 'civicrm_contact',
111 'rule_field' => $field,
114 $foundDupes = CRM_Dedupe_Finder
::dupesInGroup($ruleGroup['id'], $this->groupID
);
115 $this->assertEquals(count($foundDupes), 4);
116 CRM_Dedupe_Finder
::dupes($ruleGroup['id']);
121 * Test a custom rule with a non-default field.
123 * @throws \CRM_Core_Exception
125 public function testCustomRuleWithAddress() {
126 $this->setupForGroupDedupe();
128 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
129 'contact_type' => 'Individual',
132 'name' => 'TestRule',
133 'title' => 'TestRule',
137 foreach (['postal_code'] as $field) {
138 $rules[$field] = $this->callAPISuccess('Rule', 'create', [
139 'dedupe_rule_group_id' => $ruleGroup['id'],
140 'rule_table' => 'civicrm_address',
142 'rule_field' => $field,
145 $foundDupes = CRM_Dedupe_Finder
::dupesInGroup($ruleGroup['id'], $this->groupID
);
146 $this->assertEquals(count($foundDupes), 1);
147 CRM_Dedupe_Finder
::dupes($ruleGroup['id']);
152 * Test rule from Richard
154 * @throws \CRM_Core_Exception
156 public function testRuleThreeContactFieldsEqualWeightWIthThresholdtheTotalSumOfAllWeight() {
157 $this->setupForGroupDedupe();
159 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
160 'contact_type' => 'Individual',
163 'name' => 'TestRule',
164 'title' => 'TestRule',
168 foreach (['first_name', 'last_name', 'birth_date'] as $field) {
169 $rules[$field] = $this->callAPISuccess('Rule', 'create', [
170 'dedupe_rule_group_id' => $ruleGroup['id'],
171 'rule_table' => 'civicrm_contact',
173 'rule_field' => $field,
176 $foundDupes = CRM_Dedupe_Finder
::dupesInGroup($ruleGroup['id'], $this->groupID
);
177 $this->assertCount(1, $foundDupes);
181 * Test a custom rule with a non-default field.
183 * @throws \CRM_Core_Exception
185 public function testInclusiveRule() {
186 $this->setupForGroupDedupe();
188 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
189 'contact_type' => 'Individual',
192 'name' => 'TestRule',
193 'title' => 'TestRule',
197 foreach (['first_name', 'last_name'] as $field) {
198 $rules[$field] = $this->callAPISuccess('Rule', 'create', [
199 'dedupe_rule_group_id' => $ruleGroup['id'],
200 'rule_table' => 'civicrm_contact',
202 'rule_field' => $field,
205 $foundDupes = CRM_Dedupe_Finder
::dupesInGroup($ruleGroup['id'], $this->groupID
);
206 $this->assertCount(4, $foundDupes);
207 CRM_Dedupe_Finder
::dupes($ruleGroup['id']);
211 * Test the supervised dedupe rule against a group.
215 public function testSupervisedDupes() {
216 $this->setupForGroupDedupe();
217 $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', ['is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Supervised']);
218 $foundDupes = CRM_Dedupe_Finder
::dupesInGroup($ruleGroup['id'], $this->groupID
);
219 // -------------------------------------------------------------------------
220 // default dedupe rule: threshold = 20 => (First + Last + Email) Matches ( 1 pair )
221 // --------------------------------------------------------------------------
222 // will - dale - will@example.com
223 // will - dale - will@example.com
224 // so 1 pair for - first + last + mail
225 $this->assertEquals(count($foundDupes), 1, 'Check Individual-Fuzzy dupe rule for dupesInGroup().');
229 * Test dupesByParams function.
231 * @throws \CRM_Core_Exception
233 public function testDupesByParams() {
234 // make dupe checks based on based on following contact sets:
235 // FIRST - LAST - EMAIL
236 // ---------------------------------
237 // robin - hood - robin@example.com
238 // robin - hood - hood@example.com
239 // robin - dale - robin@example.com
240 // little - dale - dale@example.com
241 // will - dale - dale@example.com
242 // will - dale - will@example.com
243 // will - dale - will@example.com
246 // FIXME: move create params to separate function
249 'first_name' => 'robin',
250 'last_name' => 'hood',
251 'email' => 'robin@example.com',
252 'contact_type' => 'Individual',
255 'first_name' => 'robin',
256 'last_name' => 'hood',
257 'email' => 'hood@example.com',
258 'contact_type' => 'Individual',
261 'first_name' => 'robin',
262 'last_name' => 'dale',
263 'email' => 'robin@example.com',
264 'contact_type' => 'Individual',
267 'first_name' => 'little',
268 'last_name' => 'dale',
269 'email' => 'dale@example.com',
270 'contact_type' => 'Individual',
273 'first_name' => 'will',
274 'last_name' => 'dale',
275 'email' => 'dale@example.com',
276 'contact_type' => 'Individual',
279 'first_name' => 'will',
280 'last_name' => 'dale',
281 'email' => 'will@example.com',
282 'contact_type' => 'Individual',
285 'first_name' => 'will',
286 'last_name' => 'dale',
287 'email' => 'will@example.com',
288 'contact_type' => 'Individual',
292 $this->hookClass
->setHook('civicrm_findDuplicates', [$this, 'hook_civicrm_findDuplicates']);
296 foreach ($params as $param) {
297 $contact = $this->callAPISuccess('contact', 'create', $param);
299 'contact_id' => $contact['id'],
300 'street_address' => 'Ambachtstraat 23',
301 'location_type_id' => 1,
303 $this->callAPISuccess('address', 'create', $params);
304 $contactIds[$count++
] = $contact['id'];
307 // verify that all contacts have been created separately
308 $this->assertEquals(count($contactIds), 7, 'Check for number of contacts.');
311 'first_name' => 'robin',
312 'last_name' => 'hood',
313 'email' => 'hood@example.com',
314 'street_address' => 'Ambachtstraat 23',
316 CRM_Core_TemporaryErrorScope
::useException();
317 $ids = CRM_Contact_BAO_Contact
::getDuplicateContacts($fields, 'Individual', 'General', [], TRUE, NULL, ['event_id' => 1]);
319 // Check with default Individual-General rule
320 $this->assertEquals(count($ids), 2, 'Check Individual-General rule for dupesByParams().');
322 // delete all created contacts
323 foreach ($contactIds as $contactId) {
324 $this->contactDelete($contactId);
329 * Implements hook_civicrm_findDuplicates().
331 * Locks in expected params
334 public function hook_civicrm_findDuplicates($dedupeParams, &$dedupeResults, $contextParams) {
335 $expectedDedupeParams = [
336 'check_permission' => TRUE,
337 'contact_type' => 'Individual',
339 'rule_group_id' => NULL,
340 'excluded_contact_ids' => [],
342 foreach ($expectedDedupeParams as $key => $value) {
343 $this->assertEquals($value, $dedupeParams[$key]);
345 $expectedDedupeResults = [
349 foreach ($expectedDedupeResults as $key => $value) {
350 $this->assertEquals($value, $dedupeResults[$key]);
353 $expectedContext = ['event_id' => 1];
354 foreach ($expectedContext as $key => $value) {
355 $this->assertEquals($value, $contextParams[$key]);
358 return $dedupeResults;
362 * Set up a group of dedupable contacts.
364 * @throws \CRM_Core_Exception
366 protected function setupForGroupDedupe() {
368 'name' => 'Dupe Group',
369 'title' => 'New Test Dupe Group',
372 'visibility' => 'Public Pages',
375 $result = $this->callAPISuccess('group', 'create', $params);
376 $this->groupID
= $result['id'];
380 'first_name' => 'robin',
381 'last_name' => 'hood',
382 'email' => 'robin@example.com',
383 'contact_type' => 'Individual',
384 'birth_date' => '2016-01-01',
385 'api.Address.create' => ['street_address' => '123 Happy world', 'location_type_id' => 'Billing', 'postal_code' => '99999'],
388 'first_name' => 'robin',
389 'last_name' => 'hood',
390 'email' => 'hood@example.com',
391 'contact_type' => 'Individual',
392 'birth_date' => '2016-01-01',
393 'api.Address.create' => ['street_address' => '123 Happy World', 'location_type_id' => 'Billing', 'postal_code' => '99999'],
396 'first_name' => 'robin',
397 'last_name' => 'dale',
398 'email' => 'robin@example.com',
399 'contact_type' => 'Individual',
402 'first_name' => 'little',
403 'last_name' => 'dale',
404 'email' => 'dale@example.com',
405 'contact_type' => 'Individual',
408 'first_name' => 'will',
409 'last_name' => 'dale',
410 'email' => 'dale@example.com',
411 'contact_type' => 'Individual',
414 'first_name' => 'will',
415 'last_name' => 'dale',
416 'email' => 'will@example.com',
417 'contact_type' => 'Individual',
420 'first_name' => 'will',
421 'last_name' => 'dale',
422 'email' => 'will@example.com',
423 'contact_type' => 'Individual',
428 foreach ($params as $param) {
429 $contact = $this->callAPISuccess('contact', 'create', $param);
430 $this->contactIDs
[$count++
] = $contact['id'];
433 'contact_id' => $contact['id'],
434 'group_id' => $this->groupID
,
436 $this->callAPISuccess('group_contact', 'create', $grpParams);
439 // verify that all contacts have been created separately
440 $this->assertEquals(count($this->contactIDs
), 7, 'Check for number of contacts.');