CRM-19562
[civicrm-core.git] / tests / phpunit / CRM / Contact / BAO / QueryTest.php
CommitLineData
6a488035 1<?php
0eea664b 2
6a488035
TO
3/**
4 * Include dataProvider for tests
acb109b7 5 * @group headless
6a488035
TO
6 */
7class CRM_Contact_BAO_QueryTest extends CiviUnitTestCase {
6a488035 8
e9479dcf
EM
9 /**
10 * @return CRM_Contact_BAO_QueryTestDataProvider
11 */
6a488035 12 public function dataProvider() {
acb1052e 13 return new CRM_Contact_BAO_QueryTestDataProvider();
6a488035
TO
14 }
15
00be9182 16 public function setUp() {
6a488035
TO
17 parent::setUp();
18 }
19
00be9182 20 public function tearDown() {
6a488035
TO
21 $tablesToTruncate = array(
22 'civicrm_group_contact',
23 'civicrm_group',
24 'civicrm_saved_search',
25 'civicrm_entity_tag',
26 'civicrm_tag',
27 'civicrm_contact',
9b1e4469 28 'civicrm_address',
6a488035
TO
29 );
30 $this->quickCleanup($tablesToTruncate);
31 }
32
33 /**
34 * Test CRM_Contact_BAO_Query::searchQuery()
6c6e6187 35 * @dataProvider dataProvider
1e1fdcf6
EM
36 * @param $fv
37 * @param $count
38 * @param $ids
39 * @param $full
6a488035 40 */
00be9182 41 public function testSearch($fv, $count, $ids, $full) {
6a488035
TO
42 $op = new PHPUnit_Extensions_Database_Operation_Insert();
43 $op->execute($this->_dbconn,
bbfd46a5 44 $this->createFlatXMLDataSet(
6a488035
TO
45 dirname(__FILE__) . '/queryDataset.xml'
46 )
47 );
48
49 $params = CRM_Contact_BAO_Query::convertFormValues($fv);
92915c55 50 $obj = new CRM_Contact_BAO_Query($params);
b81f44dd 51
52 // let's set useGroupBy=true since we are listing contacts here who might belong to
53 // more than one group / tag / notes etc.
54 $obj->_useGroupBy = TRUE;
55
92915c55 56 $dao = $obj->searchQuery();
6a488035
TO
57
58 $contacts = array();
59 while ($dao->fetch()) {
60 $contacts[] = $dao->contact_id;
61 }
62
63 sort($contacts, SORT_NUMERIC);
64
a15773db 65 $this->assertEquals($ids, $contacts);
6a488035 66 }
e5fccefb
EM
67
68 /**
eceb18cc 69 * Check that we get a successful result querying for home address.
e5fccefb
EM
70 * CRM-14263 search builder failure with search profile & address in criteria
71 */
00be9182 72 public function testSearchProfileHomeCityCRM14263() {
e5fccefb
EM
73 $contactID = $this->individualCreate();
74 CRM_Core_Config::singleton()->defaultSearchProfileID = 1;
92915c55
TO
75 $this->callAPISuccess('address', 'create', array(
76 'contact_id' => $contactID,
77 'city' => 'Cool City',
acb1052e 78 'location_type_id' => 1,
92915c55 79 ));
e5fccefb
EM
80 $params = array(
81 0 => array(
82 0 => 'city-1',
83 1 => '=',
84 2 => 'Cool City',
85 3 => 1,
86 4 => 0,
21dfd5f5 87 ),
e5fccefb
EM
88 );
89 $returnProperties = array(
90 'contact_type' => 1,
91 'contact_sub_type' => 1,
92 'sort_name' => 1,
93 );
94
95 $queryObj = new CRM_Contact_BAO_Query($params, $returnProperties);
96 try {
55eb4e22 97 $resultDAO = $queryObj->searchQuery(0, 0, NULL,
e5fccefb
EM
98 FALSE, FALSE,
99 FALSE, FALSE,
100 FALSE);
55eb4e22 101 $this->assertTrue($resultDAO->fetch());
e5fccefb 102 }
55eb4e22
EM
103 catch (PEAR_Exception $e) {
104 $err = $e->getCause();
105 $this->fail('invalid SQL created' . $e->getMessage() . " " . $err->userinfo);
e5fccefb 106
55eb4e22 107 }
e5fccefb
EM
108 }
109
55eb4e22 110 /**
eceb18cc 111 * Check that we get a successful result querying for home address.
55eb4e22
EM
112 * CRM-14263 search builder failure with search profile & address in criteria
113 */
00be9182 114 public function testSearchProfileHomeCityNoResultsCRM14263() {
55eb4e22
EM
115 $contactID = $this->individualCreate();
116 CRM_Core_Config::singleton()->defaultSearchProfileID = 1;
92915c55
TO
117 $this->callAPISuccess('address', 'create', array(
118 'contact_id' => $contactID,
119 'city' => 'Cool City',
acb1052e 120 'location_type_id' => 1,
92915c55 121 ));
55eb4e22
EM
122 $params = array(
123 0 => array(
124 0 => 'city-1',
125 1 => '=',
126 2 => 'Dumb City',
127 3 => 1,
128 4 => 0,
21dfd5f5 129 ),
55eb4e22
EM
130 );
131 $returnProperties = array(
132 'contact_type' => 1,
133 'contact_sub_type' => 1,
134 'sort_name' => 1,
135 );
136
137 $queryObj = new CRM_Contact_BAO_Query($params, $returnProperties);
138 try {
139 $resultDAO = $queryObj->searchQuery(0, 0, NULL,
140 FALSE, FALSE,
141 FALSE, FALSE,
142 FALSE);
143 $this->assertFalse($resultDAO->fetch());
144 }
145 catch (PEAR_Exception $e) {
146 $err = $e->getCause();
147 $this->fail('invalid SQL created' . $e->getMessage() . " " . $err->userinfo);
148
149 }
150 }
92915c55 151
6c6e6187
TO
152 /**
153 * CRM-14263 search builder failure with search profile & address in criteria
154 * We are retrieving primary here - checking the actual sql seems super prescriptive - but since the massive query object has
155 * so few tests detecting any change seems good here :-)
156 */
6ea503d4 157 public function testSearchProfilePrimaryCityCRM14263() {
6c6e6187
TO
158 $contactID = $this->individualCreate();
159 CRM_Core_Config::singleton()->defaultSearchProfileID = 1;
92915c55
TO
160 $this->callAPISuccess('address', 'create', array(
161 'contact_id' => $contactID,
162 'city' => 'Cool City',
acb1052e 163 'location_type_id' => 1,
92915c55 164 ));
6c6e6187 165 $params = array(
92915c55
TO
166 0 => array(
167 0 => 'city',
168 1 => '=',
169 2 => 'Cool City',
170 3 => 1,
171 4 => 0,
172 ),
173 );
6c6e6187 174 $returnProperties = array(
92915c55
TO
175 'contact_type' => 1,
176 'contact_sub_type' => 1,
177 'sort_name' => 1,
178 );
15093854 179 $expectedSQL = "SELECT contact_a.id as contact_id, contact_a.contact_type as `contact_type`, contact_a.contact_sub_type as `contact_sub_type`, contact_a.sort_name as `sort_name`, civicrm_address.id as address_id, civicrm_address.city as `city` FROM civicrm_contact contact_a LEFT JOIN civicrm_address ON ( contact_a.id = civicrm_address.contact_id AND civicrm_address.is_primary = 1 ) WHERE ( ( LOWER(civicrm_address.city) = 'cool city' ) ) AND (contact_a.is_deleted = 0) ORDER BY `contact_a`.`sort_name` asc, `contact_a`.`id` ";
6c6e6187
TO
180 $queryObj = new CRM_Contact_BAO_Query($params, $returnProperties);
181 try {
182 $this->assertEquals($expectedSQL, $queryObj->searchQuery(0, 0, NULL,
92915c55
TO
183 FALSE, FALSE,
184 FALSE, FALSE,
185 TRUE));
6c6e6187
TO
186 }
187 catch (PEAR_Exception $e) {
188 $err = $e->getCause();
189 $this->fail('invalid SQL created' . $e->getMessage() . " " . $err->userinfo);
55eb4e22 190
55eb4e22 191 }
6c6e6187 192 }
96025800 193
82ae55f4 194 /**
195 * Test set up to test calling the query object per GroupContactCache BAO usage.
196 *
197 * CRM-17254 ensure that if only the contact_id is required other fields should
198 * not be appended.
199 */
200 public function testGroupContactCacheAddSearch() {
201 $returnProperties = array('contact_id');
2f0c1d42 202 $params = array(array('group', 'IN', array(1), 0, 0));
82ae55f4 203
204 $query = new CRM_Contact_BAO_Query(
205 $params, $returnProperties,
206 NULL, TRUE, FALSE, 1,
207 TRUE,
208 TRUE, FALSE
209 );
210
211 list($select) = $query->query(FALSE);
212 $this->assertEquals('SELECT contact_a.id as contact_id', $select);
213 }
214
9b1e4469 215 /**
216 * Test smart groups with non-numeric don't fail on range queries.
217 *
218 * CRM-14720
219 */
220 public function testNumericPostal() {
221 $this->individualCreate(array('api.address.create' => array('postal_code' => 5, 'location_type_id' => 'Main')));
222 $this->individualCreate(array('api.address.create' => array('postal_code' => 'EH10 4RB-889', 'location_type_id' => 'Main')));
223 $this->individualCreate(array('api.address.create' => array('postal_code' => '4', 'location_type_id' => 'Main')));
224 $this->individualCreate(array('api.address.create' => array('postal_code' => '6', 'location_type_id' => 'Main')));
225
226 $params = array(array('postal_code_low', '=', 5, 0, 0));
227 CRM_Contact_BAO_Query::convertFormValues($params);
228
229 $query = new CRM_Contact_BAO_Query(
230 $params, array('contact_id'),
231 NULL, TRUE, FALSE, 1,
232 TRUE,
233 TRUE, FALSE
234 );
235
236 $sql = $query->query(FALSE);
237 $result = CRM_Core_DAO::executeQuery(implode(' ', $sql));
238 $this->assertEquals(2, $result->N);
239
240 // We save this as a smart group and then load it. With mysql warnings on & CRM-14720 this
241 // results in mysql warnings & hence fatal errors.
242 /// I was unable to get mysql warnings to activate in the context of the unit tests - but
243 // felt this code still provided a useful bit of coverage as it runs the various queries to load
244 // the group & could generate invalid sql if a bug were introduced.
245 $groupParams = array('title' => 'postal codes', 'formValues' => $params, 'is_active' => 1);
246 $group = CRM_Contact_BAO_Group::createSmartGroup($groupParams);
247 CRM_Contact_BAO_GroupContactCache::load($group, TRUE);
248 }
249
9a1491bb 250 /**
251 * Test smart groups with non-numeric don't fail on equal queries.
252 *
253 * CRM-14720
254 */
255 public function testNonNumericEqualsPostal() {
256 $this->individualCreate(array('api.address.create' => array('postal_code' => 5, 'location_type_id' => 'Main')));
257 $this->individualCreate(array('api.address.create' => array('postal_code' => 'EH10 4RB-889', 'location_type_id' => 'Main')));
258 $this->individualCreate(array('api.address.create' => array('postal_code' => '4', 'location_type_id' => 'Main')));
259 $this->individualCreate(array('api.address.create' => array('postal_code' => '6', 'location_type_id' => 'Main')));
260
261 $params = array(array('postal_code', '=', 'EH10 4RB-889', 0, 0));
262 CRM_Contact_BAO_Query::convertFormValues($params);
263
264 $query = new CRM_Contact_BAO_Query(
265 $params, array('contact_id'),
266 NULL, TRUE, FALSE, 1,
267 TRUE,
268 TRUE, FALSE
269 );
270
271 $sql = $query->query(FALSE);
272 $this->assertEquals("WHERE ( civicrm_address.postal_code = 'eh10 4rb-889' ) AND (contact_a.is_deleted = 0)", $sql[2]);
273 $result = CRM_Core_DAO::executeQuery(implode(' ', $sql));
274 $this->assertEquals(1, $result->N);
275
276 }
277
c3137c08 278 /**
279 * Test the group contact clause does not contain an OR.
280 *
281 * The search should return 3 contacts - 2 households in the smart group of
282 * Contact Type = Household and one Individual hard-added to it. The
283 * Household that meets both criteria should be returned once.
284 */
285 public function testGroupClause() {
286 $this->householdCreate();
287 $householdID = $this->householdCreate();
288 $individualID = $this->individualCreate();
289 $groupID = $this->smartGroupCreate();
290 $this->callAPISuccess('GroupContact', 'create', array('group_id' => $groupID, 'contact_id' => $individualID, 'status' => 'Added'));
291 $this->callAPISuccess('GroupContact', 'create', array('group_id' => $groupID, 'contact_id' => $householdID, 'status' => 'Added'));
292
485a3a1f 293 // Refresh the cache for test purposes. It would be better to alter to alter the GroupContact add function to add contacts to the cache.
294 CRM_Contact_BAO_GroupContactCache::remove($groupID, FALSE);
3875e6b6 295
296 $sql = CRM_Contact_BAO_Query::getQuery(
c3137c08 297 array(array('group', 'IN', array($groupID), 0, 0)),
298 array('contact_id')
299 );
300
3875e6b6 301 $dao = CRM_Core_DAO::executeQuery($sql);
c3137c08 302 $this->assertEquals(3, $dao->N);
3875e6b6 303 $this->assertFalse(strstr($sql, ' OR '));
304
305 $sql = CRM_Contact_BAO_Query::getQuery(
306 array(array('group', 'IN', array($groupID), 0, 0)),
307 array('contact_id' => 1, 'group' => 1)
308 );
309
310 $dao = CRM_Core_DAO::executeQuery($sql);
311 $this->assertEquals(3, $dao->N);
312 $this->assertFalse(strstr($sql, ' OR '), 'Query does not include or');
313 while ($dao->fetch()) {
314 $this->assertTrue(($dao->groups == $groupID || $dao->groups == ',' . $groupID), $dao->groups . ' includes ' . $groupID);
315 }
c3137c08 316 }
317
b1128d0b
SL
318 /**
319 * CRM-19562 ensure that only ids are used for contactid searching.
320 */
321 public function testContactIDClause() {
322 $params = array(
323 array("mark_x_93980", "=", 1, 0, 0),
324 array("mark_x_foo@example.com", "=", 1, 0, 0),
325 );
326 $returnProperties = array(
327 "sort_name" => 1,
328 "email" => 1,
329 "do_not_email" => 1,
330 "is_deceased" => 1,
331 "on_hold" => 1,
332 "display_name" => 1,
333 "preferred_mail_format" => 1,
334 );
335 $numberofContacts = 2;
336 $query = new CRM_Contact_BAO_Query($params, $returnProperties);
337 try {
338 $query->apiQuery($params, $returnProperties, NULL, NULL, 0, $numberofContacts);
339 }
340 catch (Exception $e) {
341 $this->assertEquals("A fatal error was triggered: One of parameters (value: foo@example.com) is not of the type Positive",
342 $e->getMessage());
343 }
344 }
345
6a488035 346}