ManagedEntitiesTest - Add coverage for filtering by module
[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
faba1457 25 public function setUp(): void {
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
20429eb9
RL
81 $this->fixtures['com.example.one-Job'] = [
82 'module' => 'com.example.one',
83 'name' => 'Job',
84 'entity' => 'Job',
85 'params' => [
86 'version' => 3,
87 'name' => 'test_job',
88 'run_frequency' => 'Daily',
89 'api_entity' => 'Job',
90 'api_action' => 'Get',
91 'parameters' => '',
92 ],
93 ];
d4cf4164
EM
94 $this->fixtures['com.example.one-Contact'] = [
95 'module' => 'com.example.one',
96 'name' => 'Contact',
97 'entity' => 'Contact',
98 'params' => [
99 'version' => 3,
100 'first_name' => 'Daffy',
101 'last_name' => 'Duck',
102 'contact_type' => 'Individual',
103 'update' => 'never',
104 ],
105 ];
15fbb541
TO
106 $this->fixtures['com.example.two-CustomGroup'] = [
107 'module' => 'com.example.two',
108 'name' => 'CustomGroup',
109 'entity' => 'CustomGroup',
110 'params' => [
111 'version' => 3,
112 'name' => 'test_custom_group_two',
113 'title' => 'Test custom group two',
114 'extends' => 'Individual',
115 ],
116 ];
117 $this->fixtures['com.example.three-CustomGroup'] = [
118 'module' => 'com.example.three',
119 'name' => 'CustomGroup',
120 'entity' => 'CustomGroup',
121 'params' => [
122 'version' => 3,
123 'name' => 'test_custom_group_three',
124 'title' => 'Test custom group three',
125 'extends' => 'Individual',
126 ],
127 ];
20429eb9 128
048222df 129 $this->apiKernel = \Civi::service('civi_api_kernel');
378e2654
TO
130 $this->adhocProvider = new \Civi\API\Provider\AdhocProvider(3, 'CustomSearch');
131 $this->apiKernel->registerApiProvider($this->adhocProvider);
8cb5ca10 132 $this->hookClass->setHook('civicrm_managed', [$this, 'hookManaged']);
6a488035
TO
133 }
134
594a9328 135 public function tearDown(): void {
6a488035 136 parent::tearDown();
048222df 137 \Civi::reset();
6a488035
TO
138 }
139
8cb5ca10
EM
140 /**
141 * @var array
142 */
143 protected $managedEntities = [];
144
145 /**
146 * Implements hook managed.
147 *
148 * @param array $entities
149 */
150 public function hookManaged(array &$entities): void {
151 $entities = $this->managedEntities;
152 }
153
6a488035
TO
154 /**
155 * Set up an active module and, over time, the hook implementation changes
156 * to (1) create 'foo' entity, (2) create 'bar' entity', (3) remove 'foo'
157 * entity
158 */
00be9182 159 public function testAddRemoveEntitiesModule_UpdateAlways_DeleteAlways() {
6a488035 160 // create first managed entity ('foo')
8cb5ca10
EM
161 $this->managedEntities = [$this->fixtures['com.example.one-foo']];
162 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
163 $me->reconcile();
164 $foo = $me->get('com.example.one', 'foo');
165 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
166 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
167
168 // later on, hook returns an extra managed entity ('bar')
8cb5ca10
EM
169 $this->managedEntities[] = $this->fixtures['com.example.one-bar'];
170 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
171 $me->reconcile();
172 $foo = $me->get('com.example.one', 'foo');
173 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
174 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
175 $bar = $me->get('com.example.one', 'bar');
176 $this->assertEquals('CRM_Example_One_Bar', $bar['name']);
177 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Bar"');
178
179 // and then hook changes its mind, removing 'foo' (first of two entities)
8cb5ca10
EM
180 unset($this->managedEntities[0]);
181 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
182 $me->reconcile();
183 $foo = $me->get('com.example.one', 'foo');
184 $this->assertTrue($foo === NULL);
185 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
186 $bar = $me->get('com.example.one', 'bar');
187 $this->assertEquals('CRM_Example_One_Bar', $bar['name']);
188 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Bar"');
189
190 // and then hook changes its mind, removing 'bar' (the last remaining entity)
8cb5ca10
EM
191 unset($this->managedEntities[1]);
192 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
193 $me->reconcile();
194 $foo = $me->get('com.example.one', 'foo');
195 $this->assertTrue($foo === NULL);
196 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
197 $bar = $me->get('com.example.one', 'bar');
8cb5ca10 198 $this->assertNull($bar);
6a488035
TO
199 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Bar"');
200 }
201
202 /**
203 * Set up an active module with one managed-entity and, over
204 * time, the content of the entity changes
8cb5ca10
EM
205 *
206 * @throws \CRM_Core_Exception
6a488035 207 */
8cb5ca10 208 public function testModifyDeclaration_UpdateAlways(): void {
6a488035 209 // create first managed entity ('foo')
8cb5ca10
EM
210 $this->managedEntities = [$this->fixtures['com.example.one-foo']];
211 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
212 $me->reconcile();
213 $foo = $me->get('com.example.one', 'foo');
214 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
215 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
216
217 // later on, hook specification changes
8cb5ca10
EM
218 $this->managedEntities[0]['params']['class_name'] = 'CRM_Example_One_Foobar';
219 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
220 $me->reconcile();
221 $foo2 = $me->get('com.example.one', 'foo');
222 $this->assertEquals('CRM_Example_One_Foobar', $foo2['name']);
223 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
224 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_FooBar"');
225 $this->assertEquals($foo['id'], $foo2['id']);
226 }
227
0dd54586
TO
228 /**
229 * Set up an active module with one managed-entity and, over
230 * time, the content of the entity changes
8cb5ca10
EM
231 *
232 * @throws \CRM_Core_Exception
0dd54586 233 */
8cb5ca10 234 public function testModifyDeclaration_UpdateNever(): void {
0dd54586 235 // create first managed entity ('foo')
8cb5ca10 236 $this->managedEntities[] = array_merge($this->fixtures['com.example.one-foo'], [
39b959db
SL
237 // Policy is to never update after initial creation
238 'update' => 'never',
9099cab3 239 ]);
8cb5ca10 240 $me = new CRM_Core_ManagedEntities($this->modules);
0dd54586
TO
241 $me->reconcile();
242 $foo = $me->get('com.example.one', 'foo');
243 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
244 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
245
246 // later on, hook specification changes
8cb5ca10
EM
247 $this->managedEntities[0]['params']['class_name'] = 'CRM_Example_One_Foobar';
248 $me = new CRM_Core_ManagedEntities($this->modules);
0dd54586
TO
249 $me->reconcile();
250 $foo2 = $me->get('com.example.one', 'foo');
251 $this->assertEquals('CRM_Example_One_Foo', $foo2['name']);
252 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
253 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_FooBar"');
254 $this->assertEquals($foo['id'], $foo2['id']);
255 }
256
1f103dc4
TO
257 /**
258 * Set up an active module with one managed-entity using the
259 * policy "cleanup=>never". When the managed-entity goes away,
260 * ensure that the policy is followed (ie the entity is not
261 * deleted).
8cb5ca10
EM
262 *
263 * @throws \CRM_Core_Exception
1f103dc4 264 */
8cb5ca10 265 public function testRemoveDeclaration_CleanupNever(): void {
1f103dc4 266 // create first managed entity ('foo')
8cb5ca10
EM
267 $this->managedEntities = [
268 array_merge($this->fixtures['com.example.one-foo'], ['cleanup' => 'never']),
269 ];
270 $me = new CRM_Core_ManagedEntities($this->modules);
1f103dc4
TO
271 $me->reconcile();
272 $foo = $me->get('com.example.one', 'foo');
273 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
274 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
275
276 // later on, entity definition disappears; but we decide not to do any cleanup (per policy)
8cb5ca10
EM
277 $this->managedEntities = [];
278 $me = new CRM_Core_ManagedEntities($this->modules);
1f103dc4
TO
279 $me->reconcile();
280 $foo2 = $me->get('com.example.one', 'foo');
281 $this->assertEquals('CRM_Example_One_Foo', $foo2['name']);
282 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
283 $this->assertEquals($foo['id'], $foo2['id']);
284 }
285
378e2654
TO
286 /**
287 * Set up an active module with one managed-entity using the
a2bf5923
CW
288 * policy "cleanup=>unused". When the managed-entity goes away,
289 * ensure that the policy is followed (ie the entity is conditionally
378e2654 290 * deleted).
8cb5ca10
EM
291 *
292 * @throws \CRM_Core_Exception
378e2654 293 */
8cb5ca10 294 public function testRemoveDeclaration_CleanupUnused(): void {
378e2654 295 // create first managed entity ('foo')
8cb5ca10
EM
296 $this->managedEntities = [array_merge($this->fixtures['com.example.one-foo'], ['cleanup' => 'unused'])];
297 $me = new CRM_Core_ManagedEntities($this->modules);
378e2654
TO
298 $me->reconcile();
299 $foo = $me->get('com.example.one', 'foo');
300 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
301 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
302
303 // Override 'getrefcount' ==> The refcount is 1
92915c55 304 $this->adhocProvider->addAction('getrefcount', 'access CiviCRM', function ($apiRequest) {
9099cab3
CW
305 return civicrm_api3_create_success([
306 [
378e2654
TO
307 'name' => 'mock',
308 'type' => 'mock',
309 'count' => 1,
9099cab3
CW
310 ],
311 ]);
378e2654
TO
312 });
313
314 // Later on, entity definition disappears; but we decide not to do any cleanup (per policy)
8cb5ca10
EM
315 $this->managedEntities = [];
316 $me = new CRM_Core_ManagedEntities($this->modules);
378e2654
TO
317 $me->reconcile();
318 $foo2 = $me->get('com.example.one', 'foo');
319 $this->assertEquals('CRM_Example_One_Foo', $foo2['name']);
320 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
321 $this->assertEquals($foo['id'], $foo2['id']);
322
378e2654 323 // Override 'getrefcount' ==> The refcount is 0
92915c55 324 $this->adhocProvider->addAction('getrefcount', 'access CiviCRM', function ($apiRequest) {
9099cab3 325 return civicrm_api3_create_success([]);
378e2654
TO
326 });
327
328 // The entity definition disappeared and there's no reference; we decide to cleanup (per policy)
8cb5ca10
EM
329 $this->managedEntities = [];
330 $me = new CRM_Core_ManagedEntities($this->modules);
378e2654
TO
331 $me->reconcile();
332 $foo3 = $me->get('com.example.one', 'foo');
333 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
8cb5ca10 334 $this->assertNull($foo3);
378e2654
TO
335 }
336
6a488035 337 /**
eceb18cc 338 * Setup an active module with a malformed entity declaration.
6a488035 339 */
8cb5ca10 340 public function testInvalidDeclarationModule(): void {
6a488035 341 // create first managed entity ('foo')
8cb5ca10
EM
342 $this->managedEntities = [
343 [
344 // erroneous
345 'module' => 'com.example.unknown',
346 'name' => 'foo',
347 'entity' => 'CustomSearch',
348 'params' => [
349 'version' => 3,
350 'class_name' => 'CRM_Example_One_Foo',
351 'is_reserved' => 1,
352 ],
9099cab3
CW
353 ],
354 ];
8cb5ca10 355 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
356 try {
357 $me->reconcile();
358 $this->fail('Expected exception when using invalid declaration');
0db6c3e1
TO
359 }
360 catch (Exception $e) {
6c6e6187 361 // good
6a488035
TO
362 }
363 }
364
365 /**
eceb18cc 366 * Setup an active module with a malformed entity declaration.
6a488035 367 */
8cb5ca10
EM
368 public function testMissingName(): void {
369 $this->managedEntities = [
370 [
371 'module' => 'com.example.unknown',
372 // erroneous
373 'name' => NULL,
374 'entity' => 'CustomSearch',
375 'params' => [
376 'version' => 3,
377 'class_name' => 'CRM_Example_One_Foo',
378 'is_reserved' => 1,
379 ],
9099cab3
CW
380 ],
381 ];
8cb5ca10 382 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
383 try {
384 $me->reconcile();
385 $this->fail('Expected exception when using invalid declaration');
0db6c3e1
TO
386 }
387 catch (Exception $e) {
6c6e6187 388 // good
6a488035
TO
389 }
390 }
391
392 /**
eceb18cc 393 * Setup an active module with a malformed entity declaration.
6a488035 394 */
8cb5ca10
EM
395 public function testMissingEntity(): void {
396 $this->managedEntities = [
397 [
398 'module' => 'com.example.unknown',
399 'name' => 'foo',
400 // erroneous
401 'entity' => NULL,
402 'params' => [
403 'version' => 3,
404 'class_name' => 'CRM_Example_One_Foo',
405 'is_reserved' => 1,
406 ],
9099cab3
CW
407 ],
408 ];
8cb5ca10 409 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
410 try {
411 $me->reconcile();
412 $this->fail('Expected exception when using invalid declaration');
0db6c3e1
TO
413 }
414 catch (Exception $e) {
6c6e6187 415 // good
6a488035
TO
416 }
417 }
418
419 /**
420 * Setup an active module with an entity -- then disable and re-enable the
421 * module
d4cf4164
EM
422 *
423 * @throws \CRM_Core_Exception
6a488035 424 */
d4cf4164 425 public function testDeactivateReactivateModule(): void {
20429eb9 426 $manager = CRM_Extension_System::singleton()->getManager();
d4cf4164
EM
427 // Register the hook so we can check there is no effort to de-activate contact.
428 $this->hookClass->setHook('civicrm_pre', [$this, 'preHook']);
429 // create first managed entities ('foo' & Contact)
8cb5ca10 430 $this->managedEntities = [$this->fixtures['com.example.one-foo'], $this->fixtures['com.example.one-Contact']];
20429eb9
RL
431 // Mock the contextual process info that would be added by CRM_Extension_Manager::install
432 $manager->setProcessesForTesting(['com.example.one' => ['install']]);
8cb5ca10 433 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
434 $me->reconcile();
435 $foo = $me->get('com.example.one', 'foo');
436 $this->assertEquals(1, $foo['is_active']);
437 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
438 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
439
8cb5ca10 440 // now deactivate module, which has no declarations and which cascades to managed object
6a488035 441 $this->modules['one']->is_active = FALSE;
20429eb9
RL
442 // Mock the contextual process info that would be added by CRM_Extension_Manager::disable
443 $manager->setProcessesForTesting(['com.example.one' => ['disable']]);
8cb5ca10
EM
444 $this->managedEntities = [];
445 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
446 $me->reconcile();
447 $foo = $me->get('com.example.one', 'foo');
448 $this->assertEquals(0, $foo['is_active']);
449 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
450 $this->assertDBQuery(0, 'SELECT is_active FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
451
452 // and reactivate module, which again provides decls and which cascades to managed object
453 $this->modules['one']->is_active = TRUE;
20429eb9
RL
454 // Mock the contextual process info that would be added by CRM_Extension_Manager::enable
455 $manager->setProcessesForTesting(['com.example.one' => ['enable']]);
8cb5ca10
EM
456 $this->managedEntities = [$this->fixtures['com.example.one-foo'], $this->fixtures['com.example.one-Contact']];
457
458 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
459 $me->reconcile();
460 $foo = $me->get('com.example.one', 'foo');
461 $this->assertEquals(1, $foo['is_active']);
462 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
463 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
20429eb9
RL
464
465 // Special case: Job entities.
466 //
467 // First we repeat the above steps, but adding the context that
468 // CRM_Extension_Manager adds when installing/enabling extensions.
469 //
470 // The behaviour should be as above.
8cb5ca10 471 $this->managedEntities = [$this->fixtures['com.example.one-Job']];
20429eb9
RL
472 // Mock the contextual process info that would be added by CRM_Extension_Manager::install
473 $manager->setProcessesForTesting(['com.example.one' => ['install']]);
8cb5ca10 474 $me = new CRM_Core_ManagedEntities($this->modules);
20429eb9
RL
475 $me->reconcile();
476 $job = $me->get('com.example.one', 'Job');
477 $this->assertEquals(1, $job['is_active']);
478 $this->assertEquals('test_job', $job['name']);
479 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_job WHERE name = "test_job"');
480 // Reset context.
481 $manager->setProcessesForTesting([]);
482
8cb5ca10 483 // now deactivate module
20429eb9
RL
484 $this->modules['one']->is_active = FALSE;
485 // Mock the contextual process info that would be added by CRM_Extension_Manager::disable
486 $manager->setProcessesForTesting(['com.example.one' => ['disable']]);
8cb5ca10
EM
487 $this->managedEntities = [];
488 $me = new CRM_Core_ManagedEntities($this->modules);
20429eb9
RL
489 $me->reconcile();
490 $job = $me->get('com.example.one', 'Job');
491 $this->assertEquals(0, $job['is_active']);
492 $this->assertEquals('test_job', $job['name']);
493 $this->assertDBQuery(0, 'SELECT is_active FROM civicrm_job WHERE name = "test_job"');
494
8cb5ca10 495 // and reactivate module
20429eb9 496 $this->modules['one']->is_active = TRUE;
8cb5ca10
EM
497 $this->managedEntities = [$this->fixtures['com.example.one-Job']];
498 $me = new CRM_Core_ManagedEntities($this->modules);
20429eb9
RL
499 // Mock the contextual process info that would be added by CRM_Extension_Manager::enable
500 $manager->setProcessesForTesting(['com.example.one' => ['enable']]);
501 $me->reconcile();
502 $job = $me->get('com.example.one', 'Job');
503 $this->assertEquals(1, $job['is_active']);
504 $this->assertEquals('test_job', $job['name']);
505 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_job WHERE name = "test_job"');
506
507 // Currently: module enabled, job enabled.
508 // Test that if we now manually disable the job, calling reconcile in a
509 // normal flush situation does NOT re-enable it.
510 // ... manually disable job.
511 $this->callAPISuccess('Job', 'create', ['id' => $job['id'], 'is_active' => 0]);
512
513 // ... now call reconcile in the context of a normal flush operation.
514 // Mock the contextual process info - there would not be any
515 $manager->setProcessesForTesting([]);
8cb5ca10 516 $me = new CRM_Core_ManagedEntities($this->modules);
20429eb9
RL
517 $me->reconcile();
518 $job = $me->get('com.example.one', 'Job');
519 $this->assertEquals(0, $job['is_active'], "Job that was manually set inactive should not have been set active again, but it was.");
520 $this->assertDBQuery(0, 'SELECT is_active FROM civicrm_job WHERE name = "test_job"');
521
522 // Now call reconcile again, but in the context of the job's extension being installed/enabled. This should re-enable the job.
523 foreach (['enable', 'install'] as $process) {
524 // Manually disable the job
525 $this->callAPISuccess('Job', 'create', ['id' => $job['id'], 'is_active' => 0]);
526 // Mock the contextual process info that would be added by CRM_Extension_Manager::enable
527 $manager->setProcessesForTesting(['com.example.one' => [$process]]);
8cb5ca10 528 $me = new CRM_Core_ManagedEntities($this->modules);
20429eb9
RL
529 $me->reconcile();
530 $job = $me->get('com.example.one', 'Job');
531 $this->assertEquals(1, $job['is_active']);
532 $this->assertEquals('test_job', $job['name']);
533 $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_job WHERE name = "test_job"');
534 }
535
536 // Reset context.
537 $manager->setProcessesForTesting([]);
6a488035
TO
538 }
539
d4cf4164
EM
540 /**
541 * Pre hook to test contact is not called on disable.
542 *
543 * @param string $op
544 * @param string $objectName
545 * @param int|null $id
546 * @param array $params
547 */
548 public function preHook($op, $objectName, $id, $params): void {
549 if ($op === 'edit' && $objectName === 'Individual') {
550 $this->assertArrayNotHasKey('is_active', $params);
551 }
552 }
553
6a488035
TO
554 /**
555 * Setup an active module with an entity -- then entirely uninstall the
556 * module
8cb5ca10
EM
557 *
558 * @throws \CRM_Core_Exception
6a488035 559 */
8cb5ca10
EM
560 public function testUninstallModule(): void {
561 $this->managedEntities = [$this->fixtures['com.example.one-foo']];
562 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
563 $me->reconcile();
564 $foo = $me->get('com.example.one', 'foo');
565 $this->assertEquals('CRM_Example_One_Foo', $foo['name']);
566 $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
567
8cb5ca10 568 // then destroy module
6a488035 569 unset($this->modules['one']);
8cb5ca10
EM
570 $this->managedEntities = [];
571 $me = new CRM_Core_ManagedEntities($this->modules);
6a488035
TO
572 $me->reconcile();
573 $fooNew = $me->get('com.example.one', 'foo');
8cb5ca10 574 $this->assertNull($fooNew);
6a488035
TO
575 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value WHERE name = "CRM_Example_One_Foo"');
576 }
96025800 577
8cb5ca10
EM
578 /**
579 * @throws \CRM_Core_Exception
580 */
581 public function testDependentEntitiesUninstallCleanly(): void {
ee8b9cee
MM
582
583 // Install a module with two dependent managed entities
8cb5ca10
EM
584 $this->managedEntities = [$this->fixtures['com.example.one-CustomGroup']];
585 $this->managedEntities[] = $this->fixtures['com.example.one-CustomField'];
586 $me = new CRM_Core_ManagedEntities($this->modules);
ee8b9cee
MM
587 $me->reconcile();
588
589 // Uninstall the module
590 unset($this->modules['one']);
8cb5ca10
EM
591 $this->managedEntities = [];
592 $me = new CRM_Core_ManagedEntities($this->modules);
ee8b9cee
MM
593 $me->reconcile();
594
595 // Ensure that no managed entities remain in the civicrm_managed
596 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_managed');
597
598 // Ensure that com.example.one-CustomGroup is deleted
599 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_custom_group WHERE name = "test_custom_group"');
600
601 // Ensure that com.example.one-CustomField is deleted
602 $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_custom_field WHERE name = "test_custom_field"');
603
604 }
605
15fbb541
TO
606 /**
607 * The hook_managed signature expanded slightly (adding the `$modules` filter).
608 * Pre-existing implementations may over-report (ie return entities despite the `$modules` filter).
609 * This test ensures that the framework respects the `$modules` filter (even if specific implementations don't).
610 */
611 public function testHookManaged_FilterModule() {
612 $this->managedEntities = [
613 $this->fixtures['com.example.one-bar'],
614 $this->fixtures['com.example.two-CustomGroup'],
615 $this->fixtures['com.example.three-CustomGroup'],
616 ];
617
618 $entitiesAll = [];
619 CRM_Utils_Hook::managed($entitiesAll);
620 $this->assertEquals($this->managedEntities, $entitiesAll);
621 $this->assertEquals(3, count($entitiesAll));
622
623 $entitiesTwoOnly = [];
624 CRM_Utils_Hook::managed($entitiesTwoOnly, ['com.example.two']);
625 $this->assertEquals([$this->fixtures['com.example.two-CustomGroup']], array_values($entitiesTwoOnly));
626 $this->assertEquals(1, count($entitiesTwoOnly));
627
628 $entitiesTwoExtra = [];
629 CRM_Utils_Hook::managed($entitiesTwoExtra, ['com.example.two', 'com.example.extra']);
630 $this->assertEquals([$this->fixtures['com.example.two-CustomGroup']], array_values($entitiesTwoExtra));
631 $this->assertEquals(1, count($entitiesTwoExtra));
632
633 $entitiesTwoThree = [];
634 CRM_Utils_Hook::managed($entitiesTwoThree, ['com.example.two', 'com.example.three']);
635 $this->assertEquals([$this->fixtures['com.example.two-CustomGroup'], $this->fixtures['com.example.three-CustomGroup']], array_values($entitiesTwoThree));
636 $this->assertEquals(2, count($entitiesTwoThree));
637 }
638
6a488035 639}