Merge pull request #15760 from colemanw/iconPicker
[civicrm-core.git] / tests / phpunit / CRM / Core / ManagedEntitiesTest.php
CommitLineData
6a488035
TO
1<?php
2
aba1cd8b
EM
3/**
4 * Class CRM_Core_ManagedEntitiesTest
acb109b7 5 * @group headless
aba1cd8b 6 */
6a488035 7class CRM_Core_ManagedEntitiesTest extends CiviUnitTestCase {
378e2654
TO
8 /**
9 * @var \Civi\API\Kernel
10 */
11 protected $apiKernel;
12
13 /**
14 * @var \Civi\API\Provider\AdhocProvider
15 */
16 protected $adhocProvider;
17
aba1cd8b 18 /**
1d3260ea 19 * @var String[]
aba1cd8b 20 */
bbf66e9c
TO
21 protected $modules;
22
6ea13979
TO
23 protected $fixtures;
24
00be9182 25 public function setUp() {
e4ccda28 26 $this->useTransaction(TRUE);
6a488035 27 parent::setUp();
9099cab3 28 $this->modules = [
6a488035
TO
29 'one' => new CRM_Core_Module('com.example.one', TRUE),
30 'two' => new CRM_Core_Module('com.example.two', TRUE),
9099cab3 31 ];
e4ccda28
TO
32
33 // Testing on drupal-demo fails because some extensions have mgd ents.
34 CRM_Core_DAO::singleValueQuery('DELETE FROM civicrm_managed');
6ea13979 35
9099cab3 36 $this->fixtures['com.example.one-foo'] = [
6ea13979
TO
37 'module' => 'com.example.one',
38 'name' => 'foo',
39 'entity' => 'CustomSearch',
9099cab3 40 'params' => [
6ea13979
TO
41 'version' => 3,
42 'class_name' => 'CRM_Example_One_Foo',
43 'is_reserved' => 1,
9099cab3
CW
44 ],
45 ];
46 $this->fixtures['com.example.one-bar'] = [
6ea13979
TO
47 'module' => 'com.example.one',
48 'name' => 'bar',
49 'entity' => 'CustomSearch',
9099cab3 50 'params' => [
6ea13979
TO
51 'version' => 3,
52 'class_name' => 'CRM_Example_One_Bar',
53 'is_reserved' => 1,
9099cab3
CW
54 ],
55 ];
56 $this->fixtures['com.example.one-CustomGroup'] = [
ee8b9cee
MM
57 'module' => 'com.example.one',
58 'name' => 'CustomGroup',
59 'entity' => 'CustomGroup',
9099cab3 60 'params' => [
ee8b9cee
MM
61 'version' => 3,
62 'name' => 'test_custom_group',
63 'title' => 'Test custom group',
64 'extends' => 'Individual',
9099cab3
CW
65 ],
66 ];
67 $this->fixtures['com.example.one-CustomField'] = [
ee8b9cee
MM
68 'module' => 'com.example.one',
69 'name' => 'CustomField',
70 'entity' => 'CustomField',
9099cab3 71 'params' => [
ee8b9cee
MM
72 'version' => 3,
73 'name' => 'test_custom_field',
74 'label' => 'Test custom field',
75 'custom_group_id' => 'test_custom_group',
76 'data_type' => 'String',
77 'html_type' => 'Text',
9099cab3
CW
78 ],
79 ];
378e2654 80
048222df 81 $this->apiKernel = \Civi::service('civi_api_kernel');
378e2654
TO
82 $this->adhocProvider = new \Civi\API\Provider\AdhocProvider(3, 'CustomSearch');
83 $this->apiKernel->registerApiProvider($this->adhocProvider);
6a488035
TO
84 }
85
00be9182 86 public function tearDown() {
6a488035 87 parent::tearDown();
048222df 88 \Civi::reset();
6a488035
TO
89 }
90
91 /**
92 * Set up an active module and, over time, the hook implementation changes
93 * to (1) create 'foo' entity, (2) create 'bar' entity', (3) remove 'foo'
94 * entity
95 */
00be9182 96 public function testAddRemoveEntitiesModule_UpdateAlways_DeleteAlways() {
9099cab3 97 $decls = [];
6a488035
TO
98
99 // create first managed entity ('foo')
6ea13979 100 $decls[] = $this->fixtures['com.example.one-foo'];
6a488035
TO
101 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
102 $me->reconcile();
103 $foo = $me->get('com.example.one', 'foo');
104 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
105 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
106
107 // later on, hook returns an extra managed entity ('bar')
6ea13979 108 $decls[] = $this->fixtures['com.example.one-bar'];
6a488035
TO
109 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
110 $me->reconcile();
111 $foo = $me->get('com.example.one', 'foo');
112 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
113 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
114 $bar = $me->get('com.example.one', 'bar');
115 $this->assertEquals('CRM_Example_One_Bar', $bar['name']);
116 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Bar"');
117
118 // and then hook changes its mind, removing 'foo' (first of two entities)
119 unset($decls[0]);
120 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
121 $me->reconcile();
122 $foo = $me->get('com.example.one', 'foo');
123 $this->assertTrue($foo === NULL);
124 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
125 $bar = $me->get('com.example.one', 'bar');
126 $this->assertEquals('CRM_Example_One_Bar', $bar['name']);
127 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Bar"');
128
129 // and then hook changes its mind, removing 'bar' (the last remaining entity)
130 unset($decls[1]);
131 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
132 $me->reconcile();
133 $foo = $me->get('com.example.one', 'foo');
134 $this->assertTrue($foo === NULL);
135 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
136 $bar = $me->get('com.example.one', 'bar');
137 $this->assertTrue($bar === NULL);
138 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Bar"');
139 }
140
141 /**
142 * Set up an active module with one managed-entity and, over
143 * time, the content of the entity changes
144 */
00be9182 145 public function testModifyDeclaration_UpdateAlways() {
9099cab3 146 $decls = [];
6a488035
TO
147
148 // create first managed entity ('foo')
6ea13979 149 $decls[] = $this->fixtures['com.example.one-foo'];
6a488035
TO
150 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
151 $me->reconcile();
152 $foo = $me->get('com.example.one', 'foo');
153 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
154 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
155
156 // later on, hook specification changes
157 $decls[0]['params']['class_name'] = 'CRM_Example_One_Foobar';
158 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
159 $me->reconcile();
160 $foo2 = $me->get('com.example.one', 'foo');
161 $this->assertEquals('CRM_Example_One_Foobar', $foo2['name']);
162 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
163 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_FooBar"');
164 $this->assertEquals($foo['id'], $foo2['id']);
165 }
166
0dd54586
TO
167 /**
168 * Set up an active module with one managed-entity and, over
169 * time, the content of the entity changes
170 */
00be9182 171 public function testModifyDeclaration_UpdateNever() {
9099cab3 172 $decls = [];
0dd54586
TO
173
174 // create first managed entity ('foo')
9099cab3 175 $decls[] = array_merge($this->fixtures['com.example.one-foo'], [
39b959db
SL
176 // Policy is to never update after initial creation
177 'update' => 'never',
9099cab3 178 ]);
0dd54586
TO
179 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
180 $me->reconcile();
181 $foo = $me->get('com.example.one', 'foo');
182 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
183 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
184
185 // later on, hook specification changes
186 $decls[0]['params']['class_name'] = 'CRM_Example_One_Foobar';
187 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
188 $me->reconcile();
189 $foo2 = $me->get('com.example.one', 'foo');
190 $this->assertEquals('CRM_Example_One_Foo', $foo2['name']);
191 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
192 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_FooBar"');
193 $this->assertEquals($foo['id'], $foo2['id']);
194 }
195
1f103dc4
TO
196 /**
197 * Set up an active module with one managed-entity using the
198 * policy "cleanup=>never". When the managed-entity goes away,
199 * ensure that the policy is followed (ie the entity is not
200 * deleted).
201 */
00be9182 202 public function testRemoveDeclaration_CleanupNever() {
9099cab3 203 $decls = [];
1f103dc4
TO
204
205 // create first managed entity ('foo')
9099cab3 206 $decls[] = array_merge($this->fixtures['com.example.one-foo'], [
21dfd5f5 207 'cleanup' => 'never',
9099cab3 208 ]);
1f103dc4
TO
209 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
210 $me->reconcile();
211 $foo = $me->get('com.example.one', 'foo');
212 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
213 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
214
215 // later on, entity definition disappears; but we decide not to do any cleanup (per policy)
9099cab3 216 $decls = [];
1f103dc4
TO
217 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
218 $me->reconcile();
219 $foo2 = $me->get('com.example.one', 'foo');
220 $this->assertEquals('CRM_Example_One_Foo', $foo2['name']);
221 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
222 $this->assertEquals($foo['id'], $foo2['id']);
223 }
224
378e2654
TO
225 /**
226 * Set up an active module with one managed-entity using the
227 * policy "cleanup=>never". When the managed-entity goes away,
228 * ensure that the policy is followed (ie the entity is not
229 * deleted).
230 */
00be9182 231 public function testRemoveDeclaration_CleanupUnused() {
9099cab3 232 $decls = [];
378e2654
TO
233
234 // create first managed entity ('foo')
9099cab3 235 $decls[] = array_merge($this->fixtures['com.example.one-foo'], [
21dfd5f5 236 'cleanup' => 'unused',
9099cab3 237 ]);
378e2654
TO
238 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
239 $me->reconcile();
240 $foo = $me->get('com.example.one', 'foo');
241 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
242 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
243
244 // Override 'getrefcount' ==> The refcount is 1
92915c55 245 $this->adhocProvider->addAction('getrefcount', 'access CiviCRM', function ($apiRequest) {
9099cab3
CW
246 return civicrm_api3_create_success([
247 [
378e2654
TO
248 'name' => 'mock',
249 'type' => 'mock',
250 'count' => 1,
9099cab3
CW
251 ],
252 ]);
378e2654
TO
253 });
254
255 // Later on, entity definition disappears; but we decide not to do any cleanup (per policy)
9099cab3 256 $decls = [];
378e2654
TO
257 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
258 $me->reconcile();
259 $foo2 = $me->get('com.example.one', 'foo');
260 $this->assertEquals('CRM_Example_One_Foo', $foo2['name']);
261 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
262 $this->assertEquals($foo['id'], $foo2['id']);
263
378e2654 264 // Override 'getrefcount' ==> The refcount is 0
92915c55 265 $this->adhocProvider->addAction('getrefcount', 'access CiviCRM', function ($apiRequest) {
9099cab3 266 return civicrm_api3_create_success([]);
378e2654
TO
267 });
268
269 // The entity definition disappeared and there's no reference; we decide to cleanup (per policy)
9099cab3 270 $decls = [];
378e2654
TO
271 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
272 $me->reconcile();
273 $foo3 = $me->get('com.example.one', 'foo');
274 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
275 $this->assertTrue($foo3 === NULL);
276 }
277
6a488035 278 /**
eceb18cc 279 * Setup an active module with a malformed entity declaration.
6a488035 280 */
00be9182 281 public function testInvalidDeclarationModule() {
6a488035 282 // create first managed entity ('foo')
9099cab3
CW
283 $decls = [];
284 $decls[] = [
39b959db
SL
285 // erroneous
286 'module' => 'com.example.unknown',
6a488035
TO
287 'name' => 'foo',
288 'entity' => 'CustomSearch',
9099cab3 289 'params' => [
6a488035
TO
290 'version' => 3,
291 'class_name' => 'CRM_Example_One_Foo',
292 'is_reserved' => 1,
9099cab3
CW
293 ],
294 ];
6a488035
TO
295 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
296 try {
297 $me->reconcile();
298 $this->fail('Expected exception when using invalid declaration');
0db6c3e1
TO
299 }
300 catch (Exception $e) {
6c6e6187 301 // good
6a488035
TO
302 }
303 }
304
305 /**
eceb18cc 306 * Setup an active module with a malformed entity declaration.
6a488035 307 */
00be9182 308 public function testMissingName() {
6a488035 309 // create first managed entity ('foo')
9099cab3
CW
310 $decls = [];
311 $decls[] = [
6a488035 312 'module' => 'com.example.unknown',
39b959db
SL
313 // erroneous
314 'name' => NULL,
6a488035 315 'entity' => 'CustomSearch',
9099cab3 316 'params' => [
6a488035
TO
317 'version' => 3,
318 'class_name' => 'CRM_Example_One_Foo',
319 'is_reserved' => 1,
9099cab3
CW
320 ],
321 ];
6a488035
TO
322 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
323 try {
324 $me->reconcile();
325 $this->fail('Expected exception when using invalid declaration');
0db6c3e1
TO
326 }
327 catch (Exception $e) {
6c6e6187 328 // good
6a488035
TO
329 }
330 }
331
332 /**
eceb18cc 333 * Setup an active module with a malformed entity declaration.
6a488035 334 */
00be9182 335 public function testMissingEntity() {
6a488035 336 // create first managed entity ('foo')
9099cab3
CW
337 $decls = [];
338 $decls[] = [
6a488035
TO
339 'module' => 'com.example.unknown',
340 'name' => 'foo',
39b959db
SL
341 // erroneous
342 'entity' => NULL,
9099cab3 343 'params' => [
6a488035
TO
344 'version' => 3,
345 'class_name' => 'CRM_Example_One_Foo',
346 'is_reserved' => 1,
9099cab3
CW
347 ],
348 ];
6a488035
TO
349 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
350 try {
351 $me->reconcile();
352 $this->fail('Expected exception when using invalid declaration');
0db6c3e1
TO
353 }
354 catch (Exception $e) {
6c6e6187 355 // good
6a488035
TO
356 }
357 }
358
359 /**
360 * Setup an active module with an entity -- then disable and re-enable the
361 * module
362 */
00be9182 363 public function testDeactivateReactivateModule() {
6a488035 364 // create first managed entity ('foo')
9099cab3 365 $decls = [];
6ea13979 366 $decls[] = $this->fixtures['com.example.one-foo'];
6a488035
TO
367 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
368 $me->reconcile();
369 $foo = $me->get('com.example.one', 'foo');
370 $this->assertEquals(1, $foo['is_active']);
371 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
372 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
373
374 // now deactivate module, which has empty decls and which cascades to managed object
375 $this->modules['one']->is_active = FALSE;
9099cab3 376 $me = new CRM_Core_ManagedEntities($this->modules, []);
6a488035
TO
377 $me->reconcile();
378 $foo = $me->get('com.example.one', 'foo');
379 $this->assertEquals(0, $foo['is_active']);
380 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
381 $this->assertDBQuery(0, 'SELECT is_active FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
382
383 // and reactivate module, which again provides decls and which cascades to managed object
384 $this->modules['one']->is_active = TRUE;
385 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
386 $me->reconcile();
387 $foo = $me->get('com.example.one', 'foo');
388 $this->assertEquals(1, $foo['is_active']);
389 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
390 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
391 }
392
393 /**
394 * Setup an active module with an entity -- then entirely uninstall the
395 * module
396 */
00be9182 397 public function testUninstallModule() {
6a488035 398 // create first managed entity ('foo')
9099cab3 399 $decls = [];
6ea13979 400 $decls[] = $this->fixtures['com.example.one-foo'];
6a488035
TO
401 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
402 $me->reconcile();
403 $foo = $me->get('com.example.one', 'foo');
404 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
405 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
406
e4f46be0 407 // then destroy module; note that decls go away
6a488035 408 unset($this->modules['one']);
9099cab3 409 $me = new CRM_Core_ManagedEntities($this->modules, []);
6a488035
TO
410 $me->reconcile();
411 $fooNew = $me->get('com.example.one', 'foo');
412 $this->assertTrue(NULL === $fooNew);
413 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
414 }
96025800 415
ee8b9cee
MM
416 public function testDependentEntitiesUninstallCleanly() {
417
418 // Install a module with two dependent managed entities
9099cab3 419 $decls = [];
ee8b9cee
MM
420 $decls[] = $this->fixtures['com.example.one-CustomGroup'];
421 $decls[] = $this->fixtures['com.example.one-CustomField'];
422 $me = new CRM_Core_ManagedEntities($this->modules, $decls);
423 $me->reconcile();
424
425 // Uninstall the module
426 unset($this->modules['one']);
427 $me = new CRM_Core_ManagedEntities($this->modules, []);
428 $me->reconcile();
429
430 // Ensure that no managed entities remain in the civicrm_managed
431 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_managed');
432
433 // Ensure that com.example.one-CustomGroup is deleted
434 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_custom_group WHERE name = "test_custom_group"');
435
436 // Ensure that com.example.one-CustomField is deleted
437 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_custom_field WHERE name = "test_custom_field"');
438
439 }
440
6a488035 441}