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