INFRA-132 - tests/ - PHPStorm cleanup
[civicrm-core.git] / tests / phpunit / CRM / Utils / API / MatchOptionTest.php
1 <?php
2
3 require_once 'CiviTest/CiviUnitTestCase.php';
4
5 /**
6 * Test that the API accepts the 'match' and 'match-mandatory' options.
7 */
8 class CRM_Utils_API_MatchOptionTest extends CiviUnitTestCase {
9
10 /**
11 * @var array
12 */
13 var $noise;
14
15 public function setUp() {
16 parent::setUp();
17 $this->assertDBQuery(0, "SELECT count(*) FROM civicrm_contact WHERE first_name='Jeffrey' and last_name='Lebowski'");
18
19 // Create noise to ensure we don't accidentally/coincidentally match the first record
20 $this->noise['individual'] = $this->individualCreate(array(
21 'email' => 'ignore1@example.com',
22 // 'street_address-1' => 'Irrelevant'
23 'api.Address.create' => array(
24 'location_type_id' => 1,
25 'street_address' => '123 Irrelevant Str',
26 'supplemental_address_1' => 'Room 987',
27 ),
28 ));
29 }
30
31 public function tearDown() {
32 $noise = $this->callAPISuccess('Contact', 'get', array(
33 'id' => $this->noise['individual'],
34 'return' => array('email'),
35 'api.Address.get' => 1,
36 ));
37 $this->assertEquals(1, count($noise['values']));
38 foreach ($noise['values'] as $value) {
39 $this->assertEquals('ignore1@example.com', $value['email']);
40 $this->assertEquals(1, count($value['api.Address.get']['values']));
41 }
42 CRM_core_DAO::executeQuery('DELETE FROM civicrm_address WHERE contact_id=%1', array(
43 1 => array($this->noise['individual'], 'Positive'),
44 ));
45 $this->callAPISuccess('Contact', 'delete', array(
46 'id' => $this->noise['individual'],
47 ));
48 parent::tearDown();
49 }
50
51 /**
52 * If there's no pre-existing record, then insert a new one.
53 */
54 public function testCreateMatch_none() {
55 $result = $this->callAPISuccess('contact', 'create', array(
56 'options' => array(
57 'match' => array('first_name', 'last_name'),
58 ),
59 'contact_type' => 'Individual',
60 'first_name' => 'Jeffrey',
61 'last_name' => 'Lebowski',
62 'nick_name' => '',
63 'external_identifier' => '1',
64 ));
65 $this->assertEquals('Jeffrey', $result['values'][$result['id']]['first_name']);
66 $this->assertEquals('Lebowski', $result['values'][$result['id']]['last_name']);
67 }
68
69 /**
70 * If there's no pre-existing record, then throw an error.
71 */
72 public function testCreateMatchMandatory_none() {
73 $this->callAPIFailure('contact', 'create', array(
74 'options' => array(
75 'match-mandatory' => array('first_name', 'last_name'),
76 ),
77 'contact_type' => 'Individual',
78 'first_name' => 'Jeffrey',
79 'last_name' => 'Lebowski',
80 'nick_name' => '',
81 'external_identifier' => '1',
82 ), 'Failed to match existing record');
83 }
84
85 /**
86 * @return array
87 */
88 public function apiOptionNames() {
89 return array(
90 array('match'),
91 array('match-mandatory'),
92 );
93 }
94
95 /**
96 * If there's one pre-existing record, then update it.
97 *
98 * @dataProvider apiOptionNames
99 * @param string $apiOptionName
100 * E.g. "match" or "match-mandatory".
101 */
102 public function testCreateMatch_one($apiOptionName) {
103 // create basic record
104 $result1 = $this->callAPISuccess('contact', 'create', array(
105 'contact_type' => 'Individual',
106 'first_name' => 'Jeffrey',
107 'last_name' => 'Lebowski',
108 'nick_name' => '',
109 'external_identifier' => '1',
110 ));
111
112 $this->individualCreate(array('email' => 'ignore2@example.com')); // more noise!
113
114 // update the record by matching first/last name
115 $result2 = $this->callAPISuccess('contact', 'create', array(
116 'options' => array(
117 $apiOptionName => array('first_name', 'last_name'),
118 ),
119 'contact_type' => 'Individual',
120 'first_name' => 'Jeffrey',
121 'last_name' => 'Lebowski',
122 'nick_name' => 'The Dude',
123 'external_identifier' => '2',
124 ));
125
126 $this->assertEquals($result1['id'], $result2['id']);
127 $this->assertEquals('Jeffrey', $result2['values'][$result2['id']]['first_name']);
128 $this->assertEquals('Lebowski', $result2['values'][$result2['id']]['last_name']);
129 $this->assertEquals('The Dude', $result2['values'][$result2['id']]['nick_name']);
130 // Make sure it was a real update
131 $this->assertDBQuery(1, "SELECT count(*) FROM civicrm_contact WHERE first_name='Jeffrey' and last_name='Lebowski' AND nick_name = 'The Dude'");
132 }
133
134 /**
135 * If there's more than one pre-existing record, throw an error.
136 *
137 * @dataProvider apiOptionNames
138 * @param string $apiOptionName
139 * E.g. "match" or "match-mandatory".
140 */
141 public function testCreateMatch_many($apiOptionName) {
142 // create the first Lebowski
143 $result1 = $this->callAPISuccess('contact', 'create', array(
144 'contact_type' => 'Individual',
145 'first_name' => 'Jeffrey',
146 'last_name' => 'Lebowski',
147 'nick_name' => 'The Dude',
148 'external_identifier' => '1',
149 ));
150
151 // create the second Lebowski
152 $result2 = $this->callAPISuccess('contact', 'create', array(
153 'contact_type' => 'Individual',
154 'first_name' => 'Jeffrey',
155 'last_name' => 'Lebowski',
156 'nick_name' => 'The Big Lebowski',
157 'external_identifier' => '2',
158 ));
159
160 $this->individualCreate(array('email' => 'ignore2@example.com')); // more noise!
161
162 // Try to update - but fail due to ambiguity
163 $result3 = $this->callAPIFailure('contact', 'create', array(
164 'options' => array(
165 $apiOptionName => array('first_name', 'last_name'),
166 ),
167 'contact_type' => 'Individual',
168 'first_name' => 'Jeffrey',
169 'last_name' => 'Lebowski',
170 'nick_name' => '',
171 'external_identifier' => 'new',
172 ), 'Ambiguous match criteria');
173 }
174
175 /**
176 * When replacing one set with another set, match items within
177 * the set using a key.
178 */
179 public function testReplaceMatch_Email() {
180 // Create contact with two emails (j1,j2)
181 $createResult = $this->callAPISuccess('contact', 'create', array(
182 'contact_type' => 'Individual',
183 'first_name' => 'Jeffrey',
184 'last_name' => 'Lebowski',
185 'api.Email.replace' => array(
186 'options' => array('match' => 'location_type_id'),
187 'values' => array(
188 array('location_type_id' => 1, 'email' => 'j1-a@example.com', 'signature_text' => 'The Dude abides.'),
189 array(
190 'location_type_id' => 2,
191 'email' => 'j2@example.com',
192 'signature_text' => 'You know, a lotta ins, a lotta outs, a lotta what-have-yous.'
193 ),
194 ),
195 ),
196 ));
197 $this->assertEquals(1, $createResult['count']);
198 foreach ($createResult['values'] as $value) {
199 $this->assertAPISuccess($value['api.Email.replace']);
200 $this->assertEquals(2, $value['api.Email.replace']['count']);
201 foreach ($value['api.Email.replace']['values'] as $v2) {
202 $this->assertEquals($createResult['id'], $v2['contact_id']);
203 }
204 $createEmailValues = array_values($value['api.Email.replace']['values']);
205 }
206
207 // Update contact's emails -- specifically, modify j1, delete j2, add j3
208 $updateResult = $this->callAPISuccess('contact', 'create', array(
209 'id' => $createResult['id'],
210 'nick_name' => 'The Dude',
211 'api.Email.replace' => array(
212 'options' => array('match' => 'location_type_id'),
213 'values' => array(
214 array('location_type_id' => 1, 'email' => 'j1-b@example.com'),
215 array('location_type_id' => 3, 'email' => 'j3@example.com'),
216 ),
217 ),
218 ));
219 $this->assertEquals(1, $updateResult['count']);
220 foreach ($updateResult['values'] as $value) {
221 $this->assertAPISuccess($value['api.Email.replace']);
222 $this->assertEquals(2, $value['api.Email.replace']['count']);
223 foreach ($value['api.Email.replace']['values'] as $v2) {
224 $this->assertEquals($createResult['id'], $v2['contact_id']);
225 }
226 $updateEmailValues = array_values($value['api.Email.replace']['values']);
227 }
228
229 // Re-read from DB
230 $getResult = $this->callAPISuccess('Email', 'get', array(
231 'contact_id' => $createResult['id'],
232 ));
233 $this->assertEquals(2, $getResult['count']);
234 $getValues = array_values($getResult['values']);
235
236 // The first email (j1@example.com) is updated (same ID#) because it matched on contact_id+location_type_id.
237 $this->assertTrue(is_numeric($createEmailValues[0]['id']));
238 $this->assertTrue(is_numeric($updateEmailValues[0]['id']));
239 $this->assertTrue(is_numeric($getValues[0]['id']));
240 $this->assertEquals($createEmailValues[0]['id'], $updateEmailValues[0]['id']);
241 $this->assertEquals($createEmailValues[0]['id'], $getValues[0]['id']);
242 $this->assertEquals('j1-b@example.com', $getValues[0]['email']);
243 $this->assertEquals('The Dude abides.', $getValues[0]['signature_text']); // preserved from original creation; proves that we updated existing record
244
245 // The second email (j2@example.com) is deleted because contact_id+location_type_id doesn't appear in new list.
246 // The third email (j3@example.com) is inserted (new ID#) because it doesn't match an existing contact_id+location_type_id.
247 $this->assertTrue(is_numeric($createEmailValues[1]['id']));
248 $this->assertTrue(is_numeric($updateEmailValues[1]['id']));
249 $this->assertTrue(is_numeric($getValues[1]['id']));
250 $this->assertNotEquals($createEmailValues[1]['id'], $updateEmailValues[1]['id']);
251 $this->assertEquals($updateEmailValues[1]['id'], $getValues[1]['id']);
252 $this->assertEquals('j3@example.com', $getValues[1]['email']);
253 $this->assertTrue(empty($getValues[1]['signature_text']));
254 }
255
256 /**
257 * When replacing one set with another set, match items within
258 * the set using a key.
259 */
260 public function testReplaceMatch_Address() {
261 // Create contact with two addresses (j1,j2)
262 $createResult = $this->callAPISuccess('contact', 'create', array(
263 'contact_type' => 'Individual',
264 'first_name' => 'Jeffrey',
265 'last_name' => 'Lebowski',
266 'api.Address.replace' => array(
267 'options' => array('match' => 'location_type_id'),
268 'values' => array(
269 array(
270 'location_type_id' => 1,
271 'street_address' => 'j1-a Example Ave',
272 'supplemental_address_1' => 'The Dude abides.'
273 ),
274 array(
275 'location_type_id' => 2,
276 'street_address' => 'j2 Example Ave',
277 'supplemental_address_1' => 'You know, a lotta ins, a lotta outs, a lotta what-have-yous.'
278 ),
279 ),
280 ),
281 ));
282 $this->assertEquals(1, $createResult['count']);
283 foreach ($createResult['values'] as $value) {
284 $this->assertAPISuccess($value['api.Address.replace']);
285 $this->assertEquals(2, $value['api.Address.replace']['count']);
286 foreach ($value['api.Address.replace']['values'] as $v2) {
287 $this->assertEquals($createResult['id'], $v2['contact_id']);
288 }
289 $createAddressValues = array_values($value['api.Address.replace']['values']);
290 }
291
292 // Update contact's addresses -- specifically, modify j1, delete j2, add j3
293 $updateResult = $this->callAPISuccess('contact', 'create', array(
294 'id' => $createResult['id'],
295 'nick_name' => 'The Dude',
296 'api.Address.replace' => array(
297 'options' => array('match' => 'location_type_id'),
298 'values' => array(
299 array('location_type_id' => 1, 'street_address' => 'j1-b Example Ave'),
300 array('location_type_id' => 3, 'street_address' => 'j3 Example Ave'),
301 ),
302 ),
303 ));
304 $this->assertEquals(1, $updateResult['count']);
305 foreach ($updateResult['values'] as $value) {
306 $this->assertAPISuccess($value['api.Address.replace']);
307 $this->assertEquals(2, $value['api.Address.replace']['count']);
308 foreach ($value['api.Address.replace']['values'] as $v2) {
309 $this->assertEquals($createResult['id'], $v2['contact_id']);
310 }
311 $updateAddressValues = array_values($value['api.Address.replace']['values']);
312 }
313
314 // Re-read from DB
315 $getResult = $this->callAPISuccess('Address', 'get', array(
316 'contact_id' => $createResult['id'],
317 ));
318 $this->assertEquals(2, $getResult['count']);
319 $getValues = array_values($getResult['values']);
320
321 // The first street_address (j1 Example Ave) is updated (same ID#) because it matched on contact_id+location_type_id.
322 $this->assertTrue(is_numeric($createAddressValues[0]['id']));
323 $this->assertTrue(is_numeric($updateAddressValues[0]['id']));
324 $this->assertTrue(is_numeric($getValues[0]['id']));
325 $this->assertEquals($createAddressValues[0]['id'], $updateAddressValues[0]['id']);
326 $this->assertEquals($createAddressValues[0]['id'], $getValues[0]['id']);
327 $this->assertEquals('j1-b Example Ave', $getValues[0]['street_address']);
328 $this->assertEquals('The Dude abides.', $getValues[0]['supplemental_address_1']); // preserved from original creation; proves that we updated existing record
329
330 // The second street_address (j2 Example Ave) is deleted because contact_id+location_type_id doesn't appear in new list.
331 // The third street_address (j3 Example Ave) is inserted (new ID#) because it doesn't match an existing contact_id+location_type_id.
332 $this->assertTrue(is_numeric($createAddressValues[1]['id']));
333 $this->assertTrue(is_numeric($updateAddressValues[1]['id']));
334 $this->assertTrue(is_numeric($getValues[1]['id']));
335 $this->assertNotEquals($createAddressValues[1]['id'], $updateAddressValues[1]['id']);
336 $this->assertEquals($updateAddressValues[1]['id'], $getValues[1]['id']);
337 $this->assertEquals('j3 Example Ave', $getValues[1]['street_address']);
338 $this->assertTrue(empty($getValues[1]['supplemental_address_1']));
339 }
340
341 }