Merge pull request #20741 from civicrm/5.39
[civicrm-core.git] / tests / phpunit / CRM / Contact / BAO / GroupContactCacheTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7d61e75f 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
7d61e75f
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035 11
6a488035
TO
12/**
13 * Test class for CRM_Contact_BAO_GroupContact BAO
14 *
6c6e6187 15 * @package CiviCRM
acb109b7 16 * @group headless
6a488035
TO
17 */
18class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
19
20 /**
eceb18cc 21 * Manually add and remove contacts from a smart group.
4f39b1e6
EM
22 *
23 * @throws \API_Exception
24 * @throws \CRM_Core_Exception
25 * @throws \CiviCRM_API3_Exception
6a488035 26 */
4f39b1e6
EM
27 public function testManualAddRemove(): void {
28 [$group, $living, $deceased] = $this->setupSmartGroup();
6a488035
TO
29
30 // Add $n1 to $g
4f39b1e6 31 $this->callAPISuccess('GroupContact', 'create', [
6a488035
TO
32 'contact_id' => $living[0]->id,
33 'group_id' => $group->id,
9099cab3 34 ]);
2828c36d 35
4f39b1e6 36 CRM_Contact_BAO_GroupContactCache::load($group);
6a488035 37 $this->assertCacheMatches(
9099cab3 38 [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id, $living[0]->id],
6a488035
TO
39 $group->id
40 );
41
42 // Remove $y1 from $g
9099cab3 43 $this->callAPISuccess('group_contact', 'create', [
6a488035
TO
44 'contact_id' => $deceased[0]->id,
45 'group_id' => $group->id,
46 'status' => 'Removed',
9099cab3 47 ]);
2828c36d 48
4f39b1e6 49 CRM_Contact_BAO_GroupContactCache::load($group);
6a488035 50 $this->assertCacheMatches(
9099cab3 51 [
92915c55
TO
52 $deceased[1]->id,
53 $deceased[2]->id,
28a04ea9 54 $living[0]->id,
9099cab3 55 ],
6a488035
TO
56 $group->id
57 );
58 }
b6708aeb 59
6a488035 60 /**
4f39b1e6
EM
61 * Allow removing contact from a parent group even if contact is in a child
62 * group. (CRM-8858).
63 *
64 * @throws \CRM_Core_Exception
6a488035 65 */
4f39b1e6
EM
66 public function testRemoveFromParentSmartGroup(): void {
67 // Create $c1, $c2, $c3
68 $deceased = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 1], 3);
69
6a488035 70 // Create smart group $parent
9099cab3 71 $params = [
6a488035
TO
72 'name' => 'Deceased Contacts',
73 'title' => 'Deceased Contacts',
74 'is_active' => 1,
9099cab3
CW
75 'formValues' => ['is_deceased' => 1],
76 ];
6a488035 77 $parent = CRM_Contact_BAO_Group::createSmartGroup($params);
9099cab3 78 $this->registerTestObjects([$parent]);
6a488035
TO
79
80 // Create group $child in $parent
9099cab3 81 $params = [
6a488035
TO
82 'name' => 'Child Group',
83 'title' => 'Child Group',
84 'is_active' => 1,
9099cab3
CW
85 'parents' => [$parent->id => 1],
86 ];
6a488035 87 $child = CRM_Contact_BAO_Group::create($params);
9099cab3 88 $this->registerTestObjects([$child]);
6a488035 89
6a488035
TO
90 // Add $c1, $c2, $c3 to $child
91 foreach ($deceased as $contact) {
9099cab3 92 $this->callAPISuccess('group_contact', 'create', [
6a488035
TO
93 'contact_id' => $contact->id,
94 'group_id' => $child->id,
9099cab3 95 ]);
6a488035
TO
96 }
97
4f39b1e6 98 CRM_Contact_BAO_GroupContactCache::load($parent);
6a488035 99 $this->assertCacheMatches(
9099cab3 100 [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
6a488035
TO
101 $parent->id
102 );
b6708aeb 103
6a488035 104 // Remove $c1 from $parent
4f39b1e6 105 $this->callAPISuccess('GroupContact', 'create', [
6a488035
TO
106 'contact_id' => $deceased[0]->id,
107 'group_id' => $parent->id,
108 'status' => 'Removed',
9099cab3 109 ]);
b6708aeb 110
6a488035 111 // Assert $c1 not in $parent
4f39b1e6 112 CRM_Contact_BAO_GroupContactCache::load($parent);
6a488035 113 $this->assertCacheMatches(
9099cab3 114 [
92915c55 115 $deceased[1]->id,
28a04ea9 116 $deceased[2]->id,
9099cab3 117 ],
6a488035
TO
118 $parent->id
119 );
b6708aeb 120
6a488035 121 // Assert $c1 still in $child
b6708aeb 122 $this->assertDBQuery(1,
6a488035 123 'select count(*) from civicrm_group_contact where group_id=%1 and contact_id=%2 and status=%3',
9099cab3
CW
124 [
125 1 => [$child->id, 'Integer'],
126 2 => [$deceased[0]->id, 'Integer'],
127 3 => ['Added', 'String'],
128 ]
6a488035
TO
129 );
130 }
131
132 /**
eceb18cc 133 * Assert that the cache for a group contains exactly the listed contacts.
6a488035 134 *
5a4f6742 135 * @param array $expectedContactIds
e16033b4 136 * Array(int).
5a4f6742 137 * @param int $groupId
6a488035 138 */
4f39b1e6 139 public function assertCacheMatches($expectedContactIds, $groupId): void {
6a488035 140 $sql = 'SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id = %1';
9099cab3 141 $params = [1 => [$groupId, 'Integer']];
6a488035 142 $dao = CRM_Core_DAO::executeQuery($sql, $params);
9099cab3 143 $actualContactIds = [];
6a488035
TO
144 while ($dao->fetch()) {
145 $actualContactIds[] = $dao->contact_id;
146 }
147
148 sort($expectedContactIds);
149 sort($actualContactIds);
150 $this->assertEquals($expectedContactIds, $actualContactIds);
151 }
152
0a0e1aab 153 /**
154 * Test the opportunistic refresh cache function does not touch non-expired entries.
155 */
156 public function testOpportunisticRefreshCacheNoChangeIfNotExpired() {
4f39b1e6 157 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3 158 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
0a0e1aab 159 $this->assertCacheMatches(
9099cab3 160 [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
0a0e1aab 161 $group->id
162 );
2b68a50c 163 CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
0a0e1aab 164
2a4fb809 165 $this->assertCacheNotRefreshed($deceased, $group);
0a0e1aab 166 }
167
168 /**
2a4fb809 169 * Test the opportunistic refresh cache function does refresh stale entries.
0a0e1aab 170 */
171 public function testOpportunisticRefreshChangeIfCacheDateFieldStale() {
4f39b1e6 172 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3 173 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
0a0e1aab 174 CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $group->id);
2a4fb809 175 $group->find(TRUE);
0a0e1aab 176 Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
177 sleep(1);
2b68a50c 178 CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
0a0e1aab 179
2a4fb809 180 $this->assertCacheRefreshed($group);
181 }
0a0e1aab 182
2a4fb809 183 /**
184 * Test the opportunistic refresh cache function does refresh expired entries if mode is deterministic.
185 */
186 public function testOpportunisticRefreshNoChangeWithDeterministicSetting() {
4f39b1e6 187 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3
CW
188 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']);
189 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
2a4fb809 190 $this->makeCacheStale($group);
2b68a50c 191 CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
2a4fb809 192 $this->assertCacheNotRefreshed($deceased, $group);
9099cab3 193 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
2a4fb809 194 }
195
196 /**
197 * Test the deterministic cache function refreshes with the deterministic setting.
198 */
199 public function testDeterministicRefreshChangeWithDeterministicSetting() {
4f39b1e6 200 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3
CW
201 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']);
202 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
2a4fb809 203 $this->makeCacheStale($group);
2b68a50c 204 CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush();
2a4fb809 205 $this->assertCacheRefreshed($group);
9099cab3 206 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
2a4fb809 207 }
208
209 /**
210 * Test the deterministic cache function refresh doesn't mess up non-expired.
211 */
212 public function testDeterministicRefreshChangeDoesNotTouchNonExpired() {
4f39b1e6 213 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3
CW
214 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']);
215 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
2b68a50c 216 CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush();
2a4fb809 217 $this->assertCacheNotRefreshed($deceased, $group);
9099cab3 218 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
2a4fb809 219 }
220
221 /**
222 * Test the deterministic cache function refreshes with the opportunistic setting.
223 *
224 * (hey it's an opportunity!).
225 */
226 public function testDeterministicRefreshChangeWithOpportunisticSetting() {
4f39b1e6 227 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3
CW
228 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
229 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
2a4fb809 230 $this->makeCacheStale($group);
2b68a50c 231 CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush();
2a4fb809 232 $this->assertCacheRefreshed($group);
233 }
234
235 /**
236 * Test the api job wrapper around the deterministic refresh works.
237 */
238 public function testJobWrapper() {
4f39b1e6 239 [$group, $living, $deceased] = $this->setupSmartGroup();
9099cab3
CW
240 $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
241 $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
2a4fb809 242 $this->makeCacheStale($group);
9099cab3 243 $this->callAPISuccess('Job', 'group_cache_flush', []);
2a4fb809 244 $this->assertCacheRefreshed($group);
0a0e1aab 245 }
246
6a488035
TO
247 // *** Everything below this should be moved to parent class ****
248
249 /**
1d3260ea
SL
250 * @var array
251 * (DAO_Name => array(int)) List of items to garbage-collect during tearDown
6a488035
TO
252 */
253 private $_testObjects;
254
6a488035
TO
255 /**
256 * Tears down the fixture, for example, closes a network connection.
2828c36d 257 *
6a488035 258 * This method is called after a test is executed.
6a488035 259 */
594a9328 260 protected function tearDown(): void {
6a488035 261 parent::tearDown();
eb68c129 262 $this->deleteTestObjects();
6a488035
TO
263 }
264
265 /**
2828c36d 266 * This is a wrapper for CRM_Core_DAO::createTestObject which tracks created entities.
6a488035
TO
267 *
268 * @see CRM_Core_DAO::createTestObject
2828c36d 269 *
270 * @param string $daoName
1e1fdcf6
EM
271 * @param array $params
272 * @param int $numObjects
273 * @param bool $createOnly
2828c36d 274 *
275 * @return array|NULL|object
6a488035 276 */
9099cab3 277 public function createTestObject($daoName, $params = [], $numObjects = 1, $createOnly = FALSE) {
6a488035
TO
278 $objects = CRM_Core_DAO::createTestObject($daoName, $params, $numObjects, $createOnly);
279 if (is_array($objects)) {
280 $this->registerTestObjects($objects);
0db6c3e1
TO
281 }
282 else {
9099cab3 283 $this->registerTestObjects([$objects]);
6a488035
TO
284 }
285 return $objects;
286 }
287
288 /**
2828c36d 289 * Register test objects.
290 *
5a4f6742
CW
291 * @param array $objects
292 * DAO or BAO objects.
6a488035 293 */
00be9182 294 public function registerTestObjects($objects) {
6a488035
TO
295 foreach ($objects as $object) {
296 $daoName = preg_replace('/_BAO_/', '_DAO_', get_class($object));
297 $this->_testObjects[$daoName][] = $object->id;
298 }
299 }
300
2828c36d 301 /**
302 * Delete test objects.
303 *
304 * Note: You might argue that the FK relations between test
305 * objects could make this problematic; however, it should
306 * behave intuitively as long as we mentally split our
307 * test-objects between the "manual/primary records"
308 * and the "automatic/secondary records"
309 */
00be9182 310 public function deleteTestObjects() {
6a488035
TO
311 foreach ($this->_testObjects as $daoName => $daoIds) {
312 foreach ($daoIds as $daoId) {
9099cab3 313 CRM_Core_DAO::deleteTestObjects($daoName, ['id' => $daoId]);
6a488035
TO
314 }
315 }
9099cab3 316 $this->_testObjects = [];
6a488035
TO
317 }
318
0a0e1aab 319 /**
320 * Set up a smart group testing scenario.
321 *
322 * @return array
323 */
4f39b1e6
EM
324 protected function setupSmartGroup(): array {
325 // Create contacts $y1, $y2, $y3 which do match $g; create $n1, $n2, $n3 which do not match $g
326 $living = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 0], 3);
327 $deceased = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 1], 3);
328 $this->assertCount(3, $deceased);
329 $this->assertCount(3, $living);
330
9099cab3 331 $params = [
0a0e1aab 332 'name' => 'Deceased Contacts',
333 'title' => 'Deceased Contacts',
334 'is_active' => 1,
9099cab3
CW
335 'formValues' => ['is_deceased' => 1],
336 ];
0a0e1aab 337 $group = CRM_Contact_BAO_Group::createSmartGroup($params);
9099cab3 338 $this->registerTestObjects([$group]);
0a0e1aab 339
0a0e1aab 340 // Assert: $g cache has exactly $y1, $y2, $y3
4f39b1e6 341 CRM_Contact_BAO_GroupContactCache::load($group);
0a0e1aab 342 $group->find(TRUE);
343 $this->assertCacheMatches(
9099cab3 344 [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
0a0e1aab 345 $group->id
346 );
347 // Reload the group so we have the cache_date & refresh_date.
9099cab3 348 return [$group, $living, $deceased];
0a0e1aab 349 }
350
2a4fb809 351 /**
352 * @param $deceased
353 * @param $group
354 *
355 * @throws \Exception
356 */
357 protected function assertCacheNotRefreshed($deceased, $group) {
358 $this->assertCacheMatches(
9099cab3 359 [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
2a4fb809 360 $group->id
361 );
9099cab3 362 $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $group->id]);
2a4fb809 363 $this->assertEquals($group->cache_date, $afterGroup['cache_date']);
364 }
365
366 /**
367 * Make the cache for the group stale, resetting it to before the timeout period.
368 *
369 * @param CRM_Contact_BAO_Group $group
370 */
371 protected function makeCacheStale(&$group) {
372 CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $group->id);
373 unset($group->cache_date);
374 $group->find(TRUE);
375 Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
376 }
377
378 /**
379 * @param $group
380 *
381 * @throws \Exception
382 */
383 protected function assertCacheRefreshed($group) {
384 $this->assertCacheMatches(
9099cab3 385 [],
2a4fb809 386 $group->id
387 );
388
9099cab3 389 $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $group->id]);
826096b0 390 $this->assertTrue(empty($afterGroup['cache_date']), 'cache date should not be set as the cache is not built');
2a4fb809 391 }
392
6451118c
JP
393 /**
394 * Test Smart group search
395 */
396 public function testSmartGroupSearchBuilder() {
9099cab3 397 $returnProperties = [
6451118c
JP
398 'contact_type' => 1,
399 'contact_sub_type' => 1,
400 'sort_name' => 1,
401 'group' => 1,
9099cab3 402 ];
4f39b1e6 403 [$group, $living, $deceased] = $this->setupSmartGroup();
6451118c 404
9099cab3 405 $params = [
6451118c
JP
406 'name' => 'Living Contacts',
407 'title' => 'Living Contacts',
408 'is_active' => 1,
9099cab3
CW
409 'formValues' => ['is_deceased' => 0],
410 ];
6451118c
JP
411 $group2 = CRM_Contact_BAO_Group::createSmartGroup($params);
412
413 //Filter on smart group with =, !=, IN and NOT IN operator.
9099cab3 414 $params = [['group', '=', $group2->id, 1, 0]];
6451118c
JP
415 $query = new CRM_Contact_BAO_Query(
416 $params, $returnProperties,
417 NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
418 FALSE,
419 FALSE, FALSE
420 );
9f6a1556
JP
421 $ids = $query->searchQuery(0, 0, NULL,
422 FALSE, FALSE, FALSE,
423 TRUE, FALSE
424 );
e24f9388 425 $key = $query->getGroupCacheTableKeys()[0];
ed17b5e2 426 $expectedWhere = "civicrm_group_contact_cache_{$key}.group_id IN (\"{$group2->id}\")";
275686a3 427 $this->assertStringContainsString($expectedWhere, $query->_whereClause);
9f6a1556 428 $this->_assertContactIds($query, "group_id = {$group2->id}");
6451118c 429
9099cab3 430 $params = [['group', '!=', $group->id, 1, 0]];
6451118c
JP
431 $query = new CRM_Contact_BAO_Query(
432 $params, $returnProperties,
433 NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
434 FALSE,
435 FALSE, FALSE
436 );
e24f9388 437 $key = $query->getGroupCacheTableKeys()[0];
6451118c 438 //Assert if proper where clause is present.
ed17b5e2 439 $expectedWhere = "civicrm_group_contact_{$key}.group_id != {$group->id} AND civicrm_group_contact_cache_{$key}.group_id IS NULL OR ( civicrm_group_contact_cache_{$key}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )";
275686a3 440 $this->assertStringContainsString($expectedWhere, $query->_whereClause);
9f6a1556 441 $this->_assertContactIds($query, "group_id != {$group->id}");
6451118c 442
9099cab3 443 $params = [['group', 'IN', [$group->id, $group2->id], 1, 0]];
6451118c
JP
444 $query = new CRM_Contact_BAO_Query(
445 $params, $returnProperties,
446 NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
447 FALSE,
448 FALSE, FALSE
449 );
e24f9388 450 $key = $query->getGroupCacheTableKeys()[0];
ed17b5e2 451 $expectedWhere = "civicrm_group_contact_cache_{$key}.group_id IN (\"{$group->id}\", \"{$group2->id}\")";
242d78de 452 $this->assertStringContainsString($expectedWhere, $query->_whereClause);
9f6a1556 453 $this->_assertContactIds($query, "group_id IN ({$group->id}, {$group2->id})");
6451118c 454
9099cab3 455 $params = [['group', 'NOT IN', [$group->id], 1, 0]];
6451118c
JP
456 $query = new CRM_Contact_BAO_Query(
457 $params, $returnProperties,
458 NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
459 FALSE,
460 FALSE, FALSE
461 );
e24f9388 462 $key = $query->getGroupCacheTableKeys()[0];
ed17b5e2 463 $expectedWhere = "civicrm_group_contact_{$key}.group_id NOT IN ( {$group->id} ) AND civicrm_group_contact_cache_{$key}.group_id IS NULL OR ( civicrm_group_contact_cache_{$key}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )";
275686a3 464 $this->assertStringContainsString($expectedWhere, $query->_whereClause);
9f6a1556 465 $this->_assertContactIds($query, "group_id NOT IN ({$group->id})");
e24f9388
SL
466 $this->callAPISuccess('group', 'delete', ['id' => $group->id]);
467 $this->callAPISuccess('group', 'delete', ['id' => $group2->id]);
468 }
469
e24f9388 470 public function testMultipleGroupWhereClause() {
9099cab3 471 $returnProperties = [
e24f9388
SL
472 'contact_type' => 1,
473 'contact_sub_type' => 1,
474 'sort_name' => 1,
475 'group' => 1,
9099cab3 476 ];
4f39b1e6 477 [$group, $living, $deceased] = $this->setupSmartGroup();
e24f9388 478
9099cab3 479 $params = [
e24f9388
SL
480 'name' => 'Living Contacts',
481 'title' => 'Living Contacts',
482 'is_active' => 1,
9099cab3
CW
483 'formValues' => ['is_deceased' => 0],
484 ];
e24f9388
SL
485 $group2 = CRM_Contact_BAO_Group::createSmartGroup($params);
486
487 //Filter on smart group with =, !=, IN and NOT IN operator.
9099cab3 488 $params = [['group', '=', $group2->id, 1, 0], ['group', '=', $group->id, 1, 0]];
e24f9388
SL
489 $query = new CRM_Contact_BAO_Query(
490 $params, $returnProperties,
491 NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS,
492 FALSE,
493 FALSE, FALSE
494 );
495 $ids = $query->searchQuery(0, 0, NULL,
496 FALSE, FALSE, FALSE,
497 TRUE, FALSE
498 );
499 $key1 = $query->getGroupCacheTableKeys()[0];
500 $key2 = $query->getGroupCacheTableKeys()[1];
501 $expectedWhere = 'civicrm_group_contact_cache_' . $key1 . '.group_id IN ("' . $group2->id . '") ) ) AND ( ( civicrm_group_contact_cache_' . $key2 . '.group_id IN ("' . $group->id . '")';
275686a3 502 $this->assertStringContainsString($expectedWhere, $query->_whereClause);
e24f9388
SL
503 // Check that we have 3 joins to the group contact cache 1 for each of the group where clauses and 1 for the fact we are returning groups in the select.
504 $expectedFrom1 = 'LEFT JOIN civicrm_group_contact_cache civicrm_group_contact_cache_' . $key1 . ' ON contact_a.id = civicrm_group_contact_cache_' . $key1 . '.contact_id';
275686a3 505 $this->assertStringContainsString($expectedFrom1, $query->_fromClause);
e24f9388 506 $expectedFrom2 = 'LEFT JOIN civicrm_group_contact_cache civicrm_group_contact_cache_' . $key2 . ' ON contact_a.id = civicrm_group_contact_cache_' . $key2 . '.contact_id';
275686a3 507 $this->assertStringContainsString($expectedFrom2, $query->_fromClause);
e24f9388 508 $expectedFrom3 = 'LEFT JOIN civicrm_group_contact_cache ON contact_a.id = civicrm_group_contact_cache.contact_id';
275686a3 509 $this->assertStringContainsString($expectedFrom3, $query->_fromClause);
9f6a1556
JP
510 }
511
512 /**
513 * Check if contact ids are fetched correctly.
514 *
515 * @param object $query
516 * @param string $groupWhereClause
517 */
518 public function _assertContactIds($query, $groupWhereClause) {
519 $contactIds = explode(',', $query->searchQuery(0, 0, NULL,
520 FALSE, FALSE, FALSE,
521 TRUE, FALSE
522 ));
9099cab3 523 $expectedContactIds = [];
9f6a1556
JP
524 $groupDAO = CRM_Core_DAO::executeQuery("SELECT contact_id FROM civicrm_group_contact_cache WHERE {$groupWhereClause}");
525 while ($groupDAO->fetch()) {
526 $expectedContactIds[] = $groupDAO->contact_id;
527 }
528 $this->assertEquals(sort($expectedContactIds), sort($contactIds));
6451118c
JP
529 }
530
6a488035 531}