Merge pull request #14925 from civicrm/5.16
[civicrm-core.git] / tests / phpunit / CRM / Dedupe / DedupeFinderTest.php
1 <?php
2
3 /**
4 * Class CRM_Dedupe_DedupeFinderTest
5 * @group headless
6 */
7 class CRM_Dedupe_DedupeFinderTest extends CiviUnitTestCase {
8
9 /**
10 * IDs of created contacts.
11 *
12 * @var array
13 */
14 protected $contactIDs = [];
15
16 /**
17 * ID of the group holding the contacts.
18 *
19 * @var int
20 */
21 protected $groupID;
22
23 /**
24 * Clean up after the test.
25 */
26 public function tearDown() {
27
28 foreach ($this->contactIDs as $contactId) {
29 $this->contactDelete($contactId);
30 }
31 if ($this->groupID) {
32 $this->callAPISuccess('group', 'delete', ['id' => $this->groupID]);
33 }
34 parent::tearDown();
35 }
36
37 /**
38 * Test the unsupervised dedupe rule against a group.
39 *
40 * @throws \Exception
41 */
42 public function testUnsupervisedDupes() {
43 // make dupe checks based on based on following contact sets:
44 // FIRST - LAST - EMAIL
45 // ---------------------------------
46 // robin - hood - robin@example.com
47 // robin - hood - hood@example.com
48 // robin - dale - robin@example.com
49 // little - dale - dale@example.com
50 // will - dale - dale@example.com
51 // will - dale - will@example.com
52 // will - dale - will@example.com
53 $this->setupForGroupDedupe();
54
55 $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', ['is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Unsupervised']);
56
57 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID);
58 $this->assertEquals(count($foundDupes), 3, 'Check Individual-Fuzzy dupe rule for dupesInGroup().');
59 }
60
61 /**
62 * Test that a rule set to is_reserved = 0 works.
63 *
64 * There is a different search used dependent on this variable.
65 */
66 public function testCustomRule() {
67 $this->setupForGroupDedupe();
68
69 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
70 'contact_type' => 'Individual',
71 'threshold' => 8,
72 'used' => 'General',
73 'name' => 'TestRule',
74 'title' => 'TestRule',
75 'is_reserved' => 0,
76 ]);
77 $rules = [];
78 foreach (['birth_date', 'first_name', 'last_name'] as $field) {
79 $rules[$field] = $this->callAPISuccess('Rule', 'create', [
80 'dedupe_rule_group_id' => $ruleGroup['id'],
81 'rule_table' => 'civicrm_contact',
82 'rule_weight' => 4,
83 'rule_field' => $field,
84 ]);
85 }
86 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID);
87 $this->assertEquals(count($foundDupes), 4);
88 CRM_Dedupe_Finder::dupes($ruleGroup['id']);
89
90 }
91
92 /**
93 * Test a custom rule with a non-default field.
94 */
95 public function testCustomRuleWithAddress() {
96 $this->setupForGroupDedupe();
97
98 $ruleGroup = $this->callAPISuccess('RuleGroup', 'create', [
99 'contact_type' => 'Individual',
100 'threshold' => 10,
101 'used' => 'General',
102 'name' => 'TestRule',
103 'title' => 'TestRule',
104 'is_reserved' => 0,
105 ]);
106 $rules = [];
107 foreach (['postal_code'] as $field) {
108 $rules[$field] = $this->callAPISuccess('Rule', 'create', [
109 'dedupe_rule_group_id' => $ruleGroup['id'],
110 'rule_table' => 'civicrm_address',
111 'rule_weight' => 10,
112 'rule_field' => $field,
113 ]);
114 }
115 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID);
116 $this->assertEquals(count($foundDupes), 1);
117 CRM_Dedupe_Finder::dupes($ruleGroup['id']);
118
119 }
120
121 /**
122 * Test the supervised dedupe rule against a group.
123 *
124 * @throws \Exception
125 */
126 public function testSupervisedDupes() {
127 $this->setupForGroupDedupe();
128 $ruleGroup = $this->callAPISuccessGetSingle('RuleGroup', ['is_reserved' => 1, 'contact_type' => 'Individual', 'used' => 'Supervised']);
129 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($ruleGroup['id'], $this->groupID);
130 // -------------------------------------------------------------------------
131 // default dedupe rule: threshold = 20 => (First + Last + Email) Matches ( 1 pair )
132 // --------------------------------------------------------------------------
133 // will - dale - will@example.com
134 // will - dale - will@example.com
135 // so 1 pair for - first + last + mail
136 $this->assertEquals(count($foundDupes), 1, 'Check Individual-Fuzzy dupe rule for dupesInGroup().');
137 }
138
139 /**
140 * Test dupesByParams function.
141 */
142 public function testDupesByParams() {
143 // make dupe checks based on based on following contact sets:
144 // FIRST - LAST - EMAIL
145 // ---------------------------------
146 // robin - hood - robin@example.com
147 // robin - hood - hood@example.com
148 // robin - dale - robin@example.com
149 // little - dale - dale@example.com
150 // will - dale - dale@example.com
151 // will - dale - will@example.com
152 // will - dale - will@example.com
153
154 // contact data set
155 // FIXME: move create params to separate function
156 $params = [
157 [
158 'first_name' => 'robin',
159 'last_name' => 'hood',
160 'email' => 'robin@example.com',
161 'contact_type' => 'Individual',
162 ],
163 [
164 'first_name' => 'robin',
165 'last_name' => 'hood',
166 'email' => 'hood@example.com',
167 'contact_type' => 'Individual',
168 ],
169 [
170 'first_name' => 'robin',
171 'last_name' => 'dale',
172 'email' => 'robin@example.com',
173 'contact_type' => 'Individual',
174 ],
175 [
176 'first_name' => 'little',
177 'last_name' => 'dale',
178 'email' => 'dale@example.com',
179 'contact_type' => 'Individual',
180 ],
181 [
182 'first_name' => 'will',
183 'last_name' => 'dale',
184 'email' => 'dale@example.com',
185 'contact_type' => 'Individual',
186 ],
187 [
188 'first_name' => 'will',
189 'last_name' => 'dale',
190 'email' => 'will@example.com',
191 'contact_type' => 'Individual',
192 ],
193 [
194 'first_name' => 'will',
195 'last_name' => 'dale',
196 'email' => 'will@example.com',
197 'contact_type' => 'Individual',
198 ],
199 ];
200
201 $this->hookClass->setHook('civicrm_findDuplicates', [$this, 'hook_civicrm_findDuplicates']);
202
203 $count = 1;
204
205 foreach ($params as $param) {
206 $contact = $this->callAPISuccess('contact', 'create', $param);
207 $params = [
208 'contact_id' => $contact['id'],
209 'street_address' => 'Ambachtstraat 23',
210 'location_type_id' => 1,
211 ];
212 $this->callAPISuccess('address', 'create', $params);
213 $contactIds[$count++] = $contact['id'];
214 }
215
216 // verify that all contacts have been created separately
217 $this->assertEquals(count($contactIds), 7, 'Check for number of contacts.');
218
219 $fields = [
220 'first_name' => 'robin',
221 'last_name' => 'hood',
222 'email' => 'hood@example.com',
223 'street_address' => 'Ambachtstraat 23',
224 ];
225 CRM_Core_TemporaryErrorScope::useException();
226 $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($fields, 'Individual', 'General', [], TRUE, NULL, ['event_id' => 1]);
227
228 // Check with default Individual-General rule
229 $this->assertEquals(count($ids), 2, 'Check Individual-General rule for dupesByParams().');
230
231 // delete all created contacts
232 foreach ($contactIds as $contactId) {
233 $this->contactDelete($contactId);
234 }
235 }
236
237 /**
238 * Implements hook_civicrm_findDuplicates().
239 *
240 * Locks in expected params
241 *
242 */
243 public function hook_civicrm_findDuplicates($dedupeParams, &$dedupeResults, $contextParams) {
244 $expectedDedupeParams = [
245 'check_permission' => TRUE,
246 'contact_type' => 'Individual',
247 'rule' => 'General',
248 'rule_group_id' => NULL,
249 'excluded_contact_ids' => [],
250 ];
251 foreach ($expectedDedupeParams as $key => $value) {
252 $this->assertEquals($value, $dedupeParams[$key]);
253 }
254 $expectedDedupeResults = [
255 'ids' => [],
256 'handled' => FALSE,
257 ];
258 foreach ($expectedDedupeResults as $key => $value) {
259 $this->assertEquals($value, $dedupeResults[$key]);
260 }
261
262 $expectedContext = ['event_id' => 1];
263 foreach ($expectedContext as $key => $value) {
264 $this->assertEquals($value, $contextParams[$key]);
265 }
266
267 return $dedupeResults;
268 }
269
270 /**
271 * Set up a group of dedupable contacts.
272 */
273 protected function setupForGroupDedupe() {
274 $params = [
275 'name' => 'Dupe Group',
276 'title' => 'New Test Dupe Group',
277 'domain_id' => 1,
278 'is_active' => 1,
279 'visibility' => 'Public Pages',
280 ];
281
282 $result = $this->callAPISuccess('group', 'create', $params);
283 $this->groupID = $result['id'];
284
285 $params = [
286 [
287 'first_name' => 'robin',
288 'last_name' => 'hood',
289 'email' => 'robin@example.com',
290 'contact_type' => 'Individual',
291 'birth_date' => '2016-01-01',
292 'api.Address.create' => ['location_type_id' => 'Billing', 'postal_code' => '99999'],
293 ],
294 [
295 'first_name' => 'robin',
296 'last_name' => 'hood',
297 'email' => 'hood@example.com',
298 'contact_type' => 'Individual',
299 'birth_date' => '2016-01-01',
300 'api.Address.create' => ['location_type_id' => 'Billing', 'postal_code' => '99999'],
301 ],
302 [
303 'first_name' => 'robin',
304 'last_name' => 'dale',
305 'email' => 'robin@example.com',
306 'contact_type' => 'Individual',
307 ],
308 [
309 'first_name' => 'little',
310 'last_name' => 'dale',
311 'email' => 'dale@example.com',
312 'contact_type' => 'Individual',
313 ],
314 [
315 'first_name' => 'will',
316 'last_name' => 'dale',
317 'email' => 'dale@example.com',
318 'contact_type' => 'Individual',
319 ],
320 [
321 'first_name' => 'will',
322 'last_name' => 'dale',
323 'email' => 'will@example.com',
324 'contact_type' => 'Individual',
325 ],
326 [
327 'first_name' => 'will',
328 'last_name' => 'dale',
329 'email' => 'will@example.com',
330 'contact_type' => 'Individual',
331 ],
332 ];
333
334 $count = 1;
335 foreach ($params as $param) {
336 $contact = $this->callAPISuccess('contact', 'create', $param);
337 $this->contactIDs[$count++] = $contact['id'];
338
339 $grpParams = [
340 'contact_id' => $contact['id'],
341 'group_id' => $this->groupID,
342 ];
343 $this->callAPISuccess('group_contact', 'create', $grpParams);
344 }
345
346 // verify that all contacts have been created separately
347 $this->assertEquals(count($this->contactIDs), 7, 'Check for number of contacts.');
348 }
349
350 }