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