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