Commit | Line | Data |
---|---|---|
ad8d1ce3 RO |
1 | <?php |
2 | require_once 'CiviTest/CiviCaseTestCase.php'; | |
3 | ||
4 | /** | |
5 | * Class CRM_Case_PseudoConstantTest | |
6 | * @group headless | |
7 | */ | |
8 | class CRM_Case_XMLProcessor_ProcessTest extends CiviCaseTestCase { | |
9 | ||
10 | public function setUp() { | |
11 | parent::setUp(); | |
12 | ||
13 | $this->defaultAssigneeOptionsValues = []; | |
ad8d1ce3 | 14 | |
68098e7b RO |
15 | $this->setupContacts(); |
16 | $this->setupDefaultAssigneeOptions(); | |
17 | $this->setupRelationships(); | |
2058bf54 | 18 | $this->setupMoreRelationshipTypes(); |
68098e7b | 19 | $this->setupActivityDefinitions(); |
ad8d1ce3 RO |
20 | |
21 | $this->process = new CRM_Case_XMLProcessor_Process(); | |
22 | } | |
23 | ||
2058bf54 D |
24 | public function tearDown() { |
25 | $this->deleteMoreRelationshipTypes(); | |
26 | ||
27 | parent::tearDown(); | |
28 | } | |
29 | ||
68098e7b RO |
30 | /** |
31 | * Creates sample contacts. | |
32 | */ | |
33 | protected function setUpContacts() { | |
34 | $this->contacts = [ | |
35 | 'ana' => $this->individualCreate(), | |
36 | 'beto' => $this->individualCreate(), | |
37 | 'carlos' => $this->individualCreate(), | |
38 | ]; | |
39 | } | |
40 | ||
ad8d1ce3 RO |
41 | /** |
42 | * Adds the default assignee group and options to the test database. | |
43 | * It also stores the IDs of the options in an index. | |
44 | */ | |
68098e7b | 45 | protected function setupDefaultAssigneeOptions() { |
ad8d1ce3 | 46 | $options = [ |
39b959db | 47 | 'NONE', 'BY_RELATIONSHIP', 'SPECIFIC_CONTACT', 'USER_CREATING_THE_CASE', |
ad8d1ce3 RO |
48 | ]; |
49 | ||
50 | CRM_Core_BAO_OptionGroup::ensureOptionGroupExists([ | |
39b959db | 51 | 'name' => 'activity_default_assignee', |
ad8d1ce3 RO |
52 | ]); |
53 | ||
54 | foreach ($options as $option) { | |
55 | $optionValue = CRM_Core_BAO_OptionValue::ensureOptionValueExists([ | |
56 | 'option_group_id' => 'activity_default_assignee', | |
57 | 'name' => $option, | |
39b959db | 58 | 'label' => $option, |
ad8d1ce3 RO |
59 | ]); |
60 | ||
61 | $this->defaultAssigneeOptionsValues[$option] = $optionValue['value']; | |
62 | } | |
63 | } | |
64 | ||
65 | /** | |
66 | * Adds a relationship between the activity's target contact and default assignee. | |
67 | */ | |
68098e7b RO |
68 | protected function setupRelationships() { |
69 | $this->relationships = [ | |
70 | 'ana_is_pupil_of_beto' => [ | |
71 | 'type_id' => NULL, | |
72 | 'name_a_b' => 'Pupil of', | |
73 | 'name_b_a' => 'Instructor', | |
74 | 'contact_id_a' => $this->contacts['ana'], | |
39b959db | 75 | 'contact_id_b' => $this->contacts['beto'], |
68098e7b RO |
76 | ], |
77 | 'ana_is_spouse_of_carlos' => [ | |
78 | 'type_id' => NULL, | |
79 | 'name_a_b' => 'Spouse of', | |
80 | 'name_b_a' => 'Spouse of', | |
81 | 'contact_id_a' => $this->contacts['ana'], | |
39b959db | 82 | 'contact_id_b' => $this->contacts['carlos'], |
68098e7b RO |
83 | ], |
84 | 'unassigned_employee' => [ | |
85 | 'type_id' => NULL, | |
86 | 'name_a_b' => 'Employee of', | |
39b959db | 87 | 'name_b_a' => 'Employer', |
68098e7b RO |
88 | ], |
89 | ]; | |
90 | ||
91 | foreach ($this->relationships as $name => &$relationship) { | |
92 | $relationship['type_id'] = $this->relationshipTypeCreate([ | |
93 | 'contact_type_a' => 'Individual', | |
94 | 'contact_type_b' => 'Individual', | |
95 | 'name_a_b' => $relationship['name_a_b'], | |
96 | 'label_a_b' => $relationship['name_a_b'], | |
97 | 'name_b_a' => $relationship['name_b_a'], | |
39b959db | 98 | 'label_b_a' => $relationship['name_b_a'], |
68098e7b RO |
99 | ]); |
100 | ||
101 | if (isset($relationship['contact_id_a'])) { | |
102 | $this->callAPISuccess('Relationship', 'create', [ | |
103 | 'contact_id_a' => $relationship['contact_id_a'], | |
104 | 'contact_id_b' => $relationship['contact_id_b'], | |
105 | 'relationship_type_id' => $relationship['type_id'], | |
106 | ]); | |
107 | } | |
108 | } | |
ad8d1ce3 RO |
109 | } |
110 | ||
2058bf54 D |
111 | /** |
112 | * Set up some additional relationship types for some specific tests. | |
113 | */ | |
114 | protected function setupMoreRelationshipTypes() { | |
115 | $this->moreRelationshipTypes = [ | |
116 | 'unidirectional_name_label_different' => [ | |
117 | 'type_id' => NULL, | |
118 | 'name_a_b' => 'jm7ab', | |
119 | 'label_a_b' => 'Jedi Master is', | |
120 | 'name_b_a' => 'jm7ba', | |
121 | 'label_b_a' => 'Jedi Master for', | |
122 | 'description' => 'Jedi Master', | |
123 | ], | |
124 | 'unidirectional_name_label_same' => [ | |
125 | 'type_id' => NULL, | |
126 | 'name_a_b' => 'Quilt Maker is', | |
127 | 'label_a_b' => 'Quilt Maker is', | |
128 | 'name_b_a' => 'Quilt Maker for', | |
129 | 'label_b_a' => 'Quilt Maker for', | |
130 | 'description' => 'Quilt Maker', | |
131 | ], | |
132 | 'bidirectional_name_label_different' => [ | |
133 | 'type_id' => NULL, | |
134 | 'name_a_b' => 'f12', | |
135 | 'label_a_b' => 'Friend of', | |
136 | 'name_b_a' => 'f12', | |
137 | 'label_b_a' => 'Friend of', | |
138 | 'description' => 'Friend', | |
139 | ], | |
140 | 'bidirectional_name_label_same' => [ | |
141 | 'type_id' => NULL, | |
142 | 'name_a_b' => 'Enemy of', | |
143 | 'label_a_b' => 'Enemy of', | |
144 | 'name_b_a' => 'Enemy of', | |
145 | 'label_b_a' => 'Enemy of', | |
146 | 'description' => 'Enemy', | |
147 | ], | |
148 | ]; | |
149 | ||
150 | foreach ($this->moreRelationshipTypes as &$relationship) { | |
151 | $relationship['type_id'] = $this->relationshipTypeCreate([ | |
152 | 'contact_type_a' => 'Individual', | |
153 | 'contact_type_b' => 'Individual', | |
154 | 'name_a_b' => $relationship['name_a_b'], | |
155 | 'label_a_b' => $relationship['label_a_b'], | |
156 | 'name_b_a' => $relationship['name_b_a'], | |
157 | 'label_b_a' => $relationship['label_b_a'], | |
158 | 'description' => $relationship['description'], | |
159 | ]); | |
160 | } | |
161 | } | |
162 | ||
163 | /** | |
164 | * Clean up additional relationship types (tearDown). | |
165 | */ | |
166 | protected function deleteMoreRelationshipTypes() { | |
167 | foreach ($this->moreRelationshipTypes as $relationship) { | |
168 | $this->callAPISuccess('relationship_type', 'delete', ['id' => $relationship['type_id']]); | |
169 | } | |
170 | } | |
171 | ||
ad8d1ce3 | 172 | /** |
68098e7b RO |
173 | * Defines the the activity parameters and XML definitions. These can be used |
174 | * to create the activity. | |
175 | */ | |
176 | protected function setupActivityDefinitions() { | |
177 | $activityTypeXml = '<activity-type><name>Open Case</name></activity-type>'; | |
178 | $this->activityTypeXml = new SimpleXMLElement($activityTypeXml); | |
179 | $this->activityParams = [ | |
180 | 'activity_date_time' => date('Ymd'), | |
181 | 'caseID' => $this->caseTypeId, | |
182 | 'clientID' => $this->contacts['ana'], | |
183 | 'creatorID' => $this->_loggedInUser, | |
184 | ]; | |
185 | } | |
186 | ||
187 | /** | |
188 | * Tests the creation of activities where the default assignee should be the | |
189 | * target contact's instructor. Beto is the instructor for Ana. | |
ad8d1ce3 RO |
190 | */ |
191 | public function testCreateActivityWithDefaultContactByRelationship() { | |
68098e7b RO |
192 | $relationship = $this->relationships['ana_is_pupil_of_beto']; |
193 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['BY_RELATIONSHIP']; | |
194 | $this->activityTypeXml->default_assignee_relationship = "{$relationship['type_id']}_b_a"; | |
195 | ||
196 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); | |
197 | $this->assertActivityAssignedToContactExists($this->contacts['beto']); | |
198 | } | |
199 | ||
200 | /** | |
201 | * Tests when the default assignee relationship exists, but in the other direction only. | |
202 | * Ana is a pupil, but has no pupils related to her. | |
203 | */ | |
204 | public function testCreateActivityWithDefaultContactByRelationshipMissing() { | |
205 | $relationship = $this->relationships['ana_is_pupil_of_beto']; | |
206 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['BY_RELATIONSHIP']; | |
207 | $this->activityTypeXml->default_assignee_relationship = "{$relationship['type_id']}_a_b"; | |
208 | ||
209 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); | |
210 | $this->assertActivityAssignedToContactExists(NULL); | |
211 | } | |
212 | ||
213 | /** | |
214 | * Tests when the the default assignee relationship exists and is a bidirectional | |
215 | * relationship. Ana and Carlos are spouses. | |
216 | */ | |
217 | public function testCreateActivityWithDefaultContactByRelationshipBidirectional() { | |
218 | $relationship = $this->relationships['ana_is_spouse_of_carlos']; | |
219 | $this->activityParams['clientID'] = $this->contacts['carlos']; | |
ad8d1ce3 | 220 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['BY_RELATIONSHIP']; |
68098e7b | 221 | $this->activityTypeXml->default_assignee_relationship = "{$relationship['type_id']}_a_b"; |
ad8d1ce3 | 222 | |
68098e7b RO |
223 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
224 | $this->assertActivityAssignedToContactExists($this->contacts['ana']); | |
ad8d1ce3 RO |
225 | } |
226 | ||
227 | /** | |
68098e7b RO |
228 | * Tests when the default assignee relationship does not exist. Ana is not an |
229 | * employee for anyone. | |
ad8d1ce3 RO |
230 | */ |
231 | public function testCreateActivityWithDefaultContactByRelationButTheresNoRelationship() { | |
68098e7b | 232 | $relationship = $this->relationships['unassigned_employee']; |
ad8d1ce3 | 233 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['BY_RELATIONSHIP']; |
68098e7b | 234 | $this->activityTypeXml->default_assignee_relationship = "{$relationship['type_id']}_b_a"; |
ad8d1ce3 | 235 | |
68098e7b | 236 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
ad8d1ce3 RO |
237 | $this->assertActivityAssignedToContactExists(NULL); |
238 | } | |
239 | ||
240 | /** | |
241 | * Tests the creation of activities with default assignee set to a specific contact. | |
242 | */ | |
243 | public function testCreateActivityAssignedToSpecificContact() { | |
244 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['SPECIFIC_CONTACT']; | |
68098e7b | 245 | $this->activityTypeXml->default_assignee_contact = $this->contacts['carlos']; |
ad8d1ce3 | 246 | |
68098e7b RO |
247 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
248 | $this->assertActivityAssignedToContactExists($this->contacts['carlos']); | |
ad8d1ce3 RO |
249 | } |
250 | ||
251 | /** | |
252 | * Tests the creation of activities with default assignee set to a specific contact, | |
253 | * but the contact does not exist. | |
254 | */ | |
255 | public function testCreateActivityAssignedToNonExistantSpecificContact() { | |
256 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['SPECIFIC_CONTACT']; | |
257 | $this->activityTypeXml->default_assignee_contact = 987456321; | |
258 | ||
68098e7b | 259 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
ad8d1ce3 RO |
260 | $this->assertActivityAssignedToContactExists(NULL); |
261 | } | |
262 | ||
263 | /** | |
264 | * Tests the creation of activities with the default assignee being the one | |
265 | * creating the case's activity. | |
266 | */ | |
267 | public function testCreateActivityAssignedToUserCreatingTheCase() { | |
268 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['USER_CREATING_THE_CASE']; | |
269 | ||
68098e7b | 270 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
ad8d1ce3 RO |
271 | $this->assertActivityAssignedToContactExists($this->_loggedInUser); |
272 | } | |
273 | ||
274 | /** | |
275 | * Tests the creation of activities when the default assignee is set to NONE. | |
276 | */ | |
277 | public function testCreateActivityAssignedNoUser() { | |
278 | $this->activityTypeXml->default_assignee_type = $this->defaultAssigneeOptionsValues['NONE']; | |
279 | ||
68098e7b | 280 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
ad8d1ce3 RO |
281 | $this->assertActivityAssignedToContactExists(NULL); |
282 | } | |
283 | ||
284 | /** | |
285 | * Tests the creation of activities when the default assignee is set to NONE. | |
286 | */ | |
287 | public function testCreateActivityWithNoDefaultAssigneeOption() { | |
68098e7b | 288 | $this->process->createActivity($this->activityTypeXml, $this->activityParams); |
ad8d1ce3 RO |
289 | $this->assertActivityAssignedToContactExists(NULL); |
290 | } | |
291 | ||
292 | /** | |
293 | * Asserts that an activity was created where the assignee was the one related | |
294 | * to the target contact. | |
295 | * | |
296 | * @param int|null $assigneeContactId the ID of the expected assigned contact or NULL if expected to be empty. | |
297 | */ | |
298 | protected function assertActivityAssignedToContactExists($assigneeContactId) { | |
299 | $expectedContact = $assigneeContactId === NULL ? [] : [$assigneeContactId]; | |
300 | $result = $this->callAPISuccess('Activity', 'get', [ | |
68098e7b | 301 | 'target_contact_id' => $this->activityParams['clientID'], |
39b959db | 302 | 'return' => ['assignee_contact_id'], |
ad8d1ce3 RO |
303 | ]); |
304 | $activity = CRM_Utils_Array::first($result['values']); | |
305 | ||
306 | $this->assertNotNull($activity, 'Target contact has no activities assigned to them'); | |
307 | $this->assertEquals($expectedContact, $activity['assignee_contact_id'], 'Activity is not assigned to expected contact'); | |
308 | } | |
309 | ||
90b5313b | 310 | /** |
2058bf54 D |
311 | * Test that caseRoles() doesn't have name and label mixed up. |
312 | * | |
313 | * @param $key string The array key in the moreRelationshipTypes array that | |
314 | * is the relationship type we're currently testing. So not necessarily | |
315 | * unique for each entry in the dataprovider since want to test a given | |
316 | * relationship type against multiple xml strings. It's not a test | |
317 | * identifier, it's an array key to use to look up something. | |
318 | * @param $xmlString string | |
319 | * @param $expected array | |
320 | * @param $dontcare array We're re-using the data provider for two tests and | |
321 | * we don't care about those expected values. | |
322 | * | |
323 | * @dataProvider xmlCaseRoleDataProvider | |
324 | */ | |
325 | public function testCaseRoles($key, $xmlString, $expected, $dontcare) { | |
326 | $xmlObj = new SimpleXMLElement($xmlString); | |
327 | ||
328 | // element 0 is direction (a_b), 1 is the text we want | |
329 | $expectedArray = empty($expected) ? [] : ["{$this->moreRelationshipTypes[$key]['type_id']}_{$expected[0]}" => $expected[1]]; | |
330 | ||
331 | $this->assertEquals($expectedArray, $this->process->caseRoles($xmlObj->CaseRoles, FALSE)); | |
332 | } | |
333 | ||
334 | /** | |
335 | * Test that locateNameOrLabel doesn't have name and label mixed up. | |
336 | * | |
337 | * @param $key string The array key in the moreRelationshipTypes array that | |
338 | * is the relationship type we're currently testing. So not necessarily | |
339 | * unique for each entry in the dataprovider since want to test a given | |
340 | * relationship type against multiple xml strings. It's not a test | |
341 | * identifier, it's an array key to use to look up something. | |
342 | * @param $xmlString string | |
343 | * @param $dontcare array We're re-using the data provider for two tests and | |
344 | * we don't care about those expected values. | |
345 | * @param $expected array | |
90b5313b | 346 | * |
2058bf54 | 347 | * @dataProvider xmlCaseRoleDataProvider |
90b5313b | 348 | */ |
2058bf54 | 349 | public function testLocateNameOrLabel($key, $xmlString, $dontcare, $expected) { |
90b5313b | 350 | $xmlObj = new SimpleXMLElement($xmlString); |
2058bf54 D |
351 | |
352 | // element 0 is direction (a_b), 1 is the text we want. | |
353 | // In case of failure, the function is expected to return FALSE for the | |
354 | // direction and then for the text it just gives us back the string we | |
355 | // gave it. | |
356 | $expectedArray = empty($expected[0]) | |
357 | ? [FALSE, $expected[1]] | |
358 | : ["{$this->moreRelationshipTypes[$key]['type_id']}_{$expected[0]}", $expected[1]]; | |
359 | ||
360 | $this->assertEquals($expectedArray, $this->process->locateNameOrLabel($xmlObj->CaseRoles->RelationshipType)); | |
90b5313b D |
361 | } |
362 | ||
363 | /** | |
2058bf54 | 364 | * Data provider for testCaseRoles and testLocateNameOrLabel |
90b5313b D |
365 | * @return array |
366 | */ | |
2058bf54 | 367 | public function xmlCaseRoleDataProvider() { |
90b5313b | 368 | return [ |
2058bf54 D |
369 | // Simulate one that has been converted to the format it should be going |
370 | // forward, where name is the actual name, i.e. same as machineName. | |
371 | [ | |
372 | // this is the array key in the $this->moreRelationshipTypes array | |
373 | 'unidirectional_name_label_different', | |
374 | // some xml | |
375 | '<CaseType><CaseRoles><RelationshipType><name>jm7ba</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
376 | // this is the expected for testCaseRoles | |
377 | ['a_b', 'Jedi Master is'], | |
378 | // this is the expected for testLocateNameOrLabel | |
379 | ['a_b', 'jm7ba'], | |
380 | ], | |
381 | // Simulate one that is still in label format, i.e. one that is still in | |
382 | // xml files that haven't been updated, or in the db but upgrade script | |
383 | // not run yet. | |
384 | [ | |
385 | 'unidirectional_name_label_different', | |
386 | '<CaseType><CaseRoles><RelationshipType><name>Jedi Master for</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
387 | ['a_b', 'Jedi Master is'], | |
388 | ['a_b', 'jm7ba'], | |
389 | ], | |
390 | // Ditto but where we know name and label are the same in the db. | |
391 | [ | |
392 | 'unidirectional_name_label_same', | |
393 | '<CaseType><CaseRoles><RelationshipType><name>Quilt Maker for</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
394 | ['a_b', 'Quilt Maker is'], | |
395 | ['a_b', 'Quilt Maker for'], | |
396 | ], | |
397 | // Simulate one that is messed up and should fail, e.g. like a typo | |
398 | // in an xml file. Here we've made a typo on purpose. | |
399 | [ | |
400 | 'unidirectional_name_label_different', | |
401 | '<CaseType><CaseRoles><RelationshipType><name>Jedi Masterrrr for</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
402 | NULL, | |
403 | [FALSE, 'Jedi Masterrrr for'], | |
404 | ], | |
405 | // Now some similar tests to above but for bidirectional relationships. | |
406 | // Bidirectional relationship, name and label different, using machine name. | |
407 | [ | |
408 | 'bidirectional_name_label_different', | |
409 | '<CaseType><CaseRoles><RelationshipType><name>f12</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
410 | ['b_a', 'Friend of'], | |
411 | ['b_a', 'f12'], | |
412 | ], | |
413 | // Bidirectional relationship, name and label different, using display label. | |
414 | [ | |
415 | 'bidirectional_name_label_different', | |
416 | '<CaseType><CaseRoles><RelationshipType><name>Friend of</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
417 | ['b_a', 'Friend of'], | |
418 | ['b_a', 'f12'], | |
419 | ], | |
420 | // Bidirectional relationship, name and label same. | |
421 | [ | |
422 | 'bidirectional_name_label_same', | |
423 | '<CaseType><CaseRoles><RelationshipType><name>Enemy of</name><creator>1</creator><manager>1</manager></RelationshipType></CaseRoles></CaseType>', | |
424 | ['b_a', 'Enemy of'], | |
425 | ['b_a', 'Enemy of'], | |
426 | ], | |
90b5313b D |
427 | ]; |
428 | } | |
429 | ||
ad8d1ce3 | 430 | } |