CRM-16701 - fixed hack in commit f1d23743.
[civicrm-core.git] / tests / phpunit / api / v3 / SyntaxConformanceTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
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
TO
27
28require_once 'CiviTest/CiviUnitTestCase.php';
29
30
31/**
e5dd95b2 32 * Test that the core actions for APIv3 entities comply with standard syntax+behavior.
6a488035 33 *
e5dd95b2
TO
34 * By default, this tests all API entities. To only test specific entities, call phpunit with
35 * environment variable SYNTAX_CONFORMANCE_ENTITIES, e.g.
36 *
37 * env SYNTAX_CONFORMANCE_ENTITIES="Contact Event" ./scripts/phpunit api_v3_SyntaxConformanceTest
38 *
39 * @package CiviCRM_APIv3
40 * @subpackage API_Core
6a488035 41 */
8086a0bf 42class api_v3_SyntaxConformanceTest extends CiviUnitTestCase {
18eee50e 43 protected $_apiversion = 3;
6a488035 44
4a97890c
TO
45 /**
46 * @var array e.g. $this->deletes['CRM_Contact_DAO_Contact'][] = $contactID;
47 */
48 protected $deletableTestObjects;
49
be2e0c6a
TO
50 /**
51 * This test case doesn't require DB reset.
52 */
6a488035 53 public $DBResetRequired = FALSE;
6ead217b 54
8efea814
EM
55 protected $_entity;
56
be2e0c6a
TO
57 /**
58 * Map custom group entities to civicrm components.
59 */
82e1689e 60 static $componentMap = array(
82e1689e
N
61 'Contribution' => 'CiviContribute',
62 'Membership' => 'CiviMember',
63 'Participant' => 'CiviEvent',
82e1689e
N
64 'Event' => 'CiviEvent',
65 'Case' => 'CiviCase',
82e1689e
N
66 'Pledge' => 'CiviPledge',
67 'Grant' => 'CiviGrant',
bf38705f 68 'Campaign' => 'CiviCampaign',
69 'Survey' => 'CiviCampaign',
82e1689e
N
70 );
71
63e9c3fd
EM
72 /**
73 * Set up function.
74 *
75 * There are two types of missing APIs:
76 * Those that are to be implemented
77 * (in some future version when someone steps in -hint hint-). List the entities in toBeImplemented[ {$action} ]
78 * Those that don't exist
79 * and that will never exist (eg an obsoleted Entity
80 * they need to be returned by the function toBeSkipped_{$action} (because it has to be a static method and therefore couldn't access a this->toBeSkipped)
81 */
00be9182 82 public function setUp() {
6a488035 83 parent::setUp();
6ead217b 84 $this->enableCiviCampaign();
92915c55
TO
85 $this->toBeImplemented['get'] = array(
86 'Profile',
87 'CustomValue',
88 'Constant',
89 'CustomSearch',
90 'Extension',
91 'ReportTemplate',
92 'System',
db7de9c1 93 'Setting',
92915c55
TO
94 );
95 $this->toBeImplemented['create'] = array(
96 'SurveyRespondant',
97 'OptionGroup',
98 'MailingRecipients',
99 'UFMatch',
100 'LocationType',
101 'CustomSearch',
102 'Extension',
103 'ReportTemplate',
db7de9c1 104 'System',
92915c55
TO
105 );
106 $this->toBeImplemented['delete'] = array(
107 'MembershipPayment',
108 'OptionGroup',
109 'SurveyRespondant',
110 'UFJoin',
111 'UFMatch',
112 'Extension',
113 'LocationType',
db7de9c1 114 'System',
92915c55 115 );
567b2076
EM
116 $this->onlyIDNonZeroCount['get'] = array(
117 'ActivityType',
118 'Entity',
119 'Domain',
120 'Setting',
121 );
6a488035 122 $this->deprecatedAPI = array('Location', 'ActivityType', 'SurveyRespondant');
4a97890c 123 $this->deletableTestObjects = array();
6a488035
TO
124 }
125
00be9182 126 public function tearDown() {
4a97890c
TO
127 foreach ($this->deletableTestObjects as $entityName => $entities) {
128 foreach ($entities as $entityID) {
129 CRM_Core_DAO::deleteTestObjects($entityName, array('id' => $entityID));
130 }
131 }
132 }
6a488035 133
4cbe18b8 134 /**
db7de9c1
EM
135 * Generate list of all entities.
136 *
137 * @param array $skip
138 * Entities to skip.
4cbe18b8
EM
139 *
140 * @return array
141 */
db7de9c1 142 public static function entities($skip = array()) {
e5dd95b2
TO
143 // The order of operations in here is screwy. In the case where SYNTAX_CONFORMANCE_ENTITIES is
144 // defined, we should be able to parse+return it immediately. However, some weird dependency
145 // crept into the system where civicrm_api('Entity','get') must be called as part of entities()
146 // (even if its return value is ignored).
dcf56200 147
e5dd95b2 148 $tmp = civicrm_api('Entity', 'Get', array('version' => 3));
dcf56200 149 if (getenv('SYNTAX_CONFORMANCE_ENTITIES')) {
e5dd95b2 150 $tmp = array(
21dfd5f5 151 'values' => explode(' ', getenv('SYNTAX_CONFORMANCE_ENTITIES')),
e5dd95b2 152 );
dcf56200
TO
153 }
154
6a488035
TO
155 if (!is_array($skip)) {
156 $skip = array();
157 }
158 $tmp = array_diff($tmp['values'], $skip);
159 $entities = array();
160 foreach ($tmp as $e) {
161 $entities[] = array($e);
162 }
163 return $entities;
164 }
165
4cbe18b8 166 /**
db7de9c1
EM
167 * Get list of entities for get test.
168 *
4cbe18b8
EM
169 * @return array
170 */
6a488035
TO
171 public static function entities_get() {
172 // all the entities, beside the ones flagged
97715495 173 return static::entities(static::toBeSkipped_get(TRUE));
6a488035
TO
174 }
175
4cbe18b8 176 /**
567b2076
EM
177 * Get entities for create tests.
178 *
4cbe18b8
EM
179 * @return array
180 */
6a488035 181 public static function entities_create() {
97715495 182 return static::entities(static::toBeSkipped_create(TRUE));
6a488035
TO
183 }
184
4cbe18b8
EM
185 /**
186 * @return array
187 */
6a488035 188 public static function entities_updatesingle() {
97715495 189 return static::entities(static::toBeSkipped_updatesingle(TRUE));
6a488035
TO
190 }
191
4cbe18b8
EM
192 /**
193 * @return array
194 */
b9af4758
E
195 public static function entities_getlimit() {
196 return static::entities(static::toBeSkipped_getlimit());
197 }
198
ced9bfed
EM
199 /**
200 * Generate list of entities that can be retrieved using SQL operator syntax.
201 *
202 * @return array
203 */
dcf5b21f
EM
204 public static function entities_getSqlOperators() {
205 return static::entities(static::toBeSkipped_getSqlOperators());
206 }
92915c55 207
4cbe18b8
EM
208 /**
209 * @return array
210 */
6a488035 211 public static function entities_delete() {
97715495 212 return static::entities(static::toBeSkipped_delete(TRUE));
6a488035
TO
213 }
214
32dafeec
EM
215 /**
216 * @return array
217 */
218 public static function entities_getfields() {
219 return static::entities(static::toBeSkipped_getfields(TRUE));
220 }
92915c55 221
4cbe18b8
EM
222 /**
223 * @return array
224 */
2fc5f1e7
EM
225 public static function custom_data_entities_get() {
226 return static::custom_data_entities();
227 }
228
4cbe18b8
EM
229 /**
230 * @return array
231 */
2fc5f1e7 232 public static function custom_data_entities() {
82e1689e 233 $entities = CRM_Core_BAO_CustomQuery::$extendsMap;
bf38705f 234 $enabledComponents = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
82e1689e 235 $customDataEntities = array();
2fc5f1e7 236 $invalidEntities = array('Individual', 'Organization', 'Household');
2f6264b4 237 $entitiesToFix = array('Case', 'Relationship');
481a74f4 238 foreach ($entities as $entityName => $entity) {
22e263ad 239 if (!in_array($entityName, $invalidEntities)
92915c55
TO
240 && !in_array($entityName, $entitiesToFix)
241 ) {
22e263ad 242 if (!empty(self::$componentMap[$entityName]) && empty($enabledComponents[self::$componentMap[$entityName]])) {
6c6e6187
TO
243 CRM_Core_BAO_ConfigSetting::enableComponent(self::$componentMap[$entityName]);
244 }
245 $customDataEntities[] = array($entityName);
82e1689e
N
246 }
247 }
2fc5f1e7
EM
248 return $customDataEntities;
249 }
250
4cbe18b8 251 /**
567b2076
EM
252 * Get entities to be skipped on get tests.
253 *
4cbe18b8
EM
254 * @param bool $sequential
255 *
256 * @return array
257 */
6a488035 258 public static function toBeSkipped_get($sequential = FALSE) {
92915c55
TO
259 $entitiesWithoutGet = array(
260 'MailingEventSubscribe',
261 'MailingEventConfirm',
262 'MailingEventResubscribe',
263 'MailingEventUnsubscribe',
567b2076 264 'Location',
92915c55 265 );
6a488035
TO
266 if ($sequential === TRUE) {
267 return $entitiesWithoutGet;
268 }
269 $entities = array();
270 foreach ($entitiesWithoutGet as $e) {
271 $entities[] = array($e);
272 }
273 return $entities;
274 }
b7d29345 275
b14ce773 276 /**
fd786d03
EM
277 * Get entities to be skipped for get call.
278 *
b14ce773 279 * Mailing Contact Just doesn't support id. We have always insisted on finding a way to
4a2db77c 280 * support id in API but in this case the underlying tables are crying out for a restructure
e4f46be0 281 * & it just doesn't make sense, on the otherhand Event need id to be existent as pseudo property
f2dff5cc 282 * is been associated with it, so we need to bypass for get api otherwise it will through pseudo_match validation
b7d29345 283 *
4a2db77c 284 * @param bool $sequential
b7d29345 285 *
a6c01b45
CW
286 * @return array
287 * Entities that cannot be retrieved by ID
b14ce773 288 */
289 public static function toBeSkipped_getByID($sequential = FALSE) {
f2dff5cc 290 return array('MailingContact', 'Event');
b14ce773 291 }
6a488035 292
4cbe18b8
EM
293 /**
294 * @param bool $sequential
295 *
296 * @return array
297 */
6a488035 298 public static function toBeSkipped_create($sequential = FALSE) {
21eb0c57 299 $entitiesWithoutCreate = array('Constant', 'Entity', 'Location', 'Profile', 'MailingRecipients');
6a488035
TO
300 if ($sequential === TRUE) {
301 return $entitiesWithoutCreate;
302 }
303 $entities = array();
304 foreach ($entitiesWithoutCreate as $e) {
305 $entities[] = array($e);
306 }
307 return $entities;
308 }
309
4cbe18b8
EM
310 /**
311 * @param bool $sequential
312 *
313 * @return array
314 */
6a488035 315 public static function toBeSkipped_delete($sequential = FALSE) {
92915c55
TO
316 $entitiesWithout = array(
317 'MailingContact',
318 'MailingEventConfirm',
319 'MailingEventResubscribe',
320 'MailingEventSubscribe',
321 'MailingEventUnsubscribe',
322 'MailingRecipients',
323 'Constant',
324 'Entity',
325 'Location',
326 'Domain',
327 'Profile',
328 'CustomValue',
92c99a4a 329 'Setting',
92915c55 330 );
6a488035
TO
331 if ($sequential === TRUE) {
332 return $entitiesWithout;
333 }
334 $entities = array();
335 foreach ($entitiesWithout as $e) {
336 $entities[] = array($e);
337 }
338 return $entities;
339 }
b7d29345 340
32dafeec
EM
341 /**
342 * @param bool $sequential
343 *
344 * @return array
345 * @todo add metadata for ALL these entities
346 */
347 public static function toBeSkipped_getfields($sequential = FALSE) {
17eeaef9 348 $entitiesWithMetadataNotYetFixed = array('ReportTemplate', 'CustomSearch');
32dafeec 349 if ($sequential === TRUE) {
6c6e6187 350 return $entitiesWithMetadataNotYetFixed;
32dafeec
EM
351 }
352 $entities = array();
353 foreach ($entitiesWithMetadataNotYetFixed as $e) {
354 $entities[] = array($e);
355 }
356 return $entities;
357 }
92915c55 358
6c6e6187 359 /**
eceb18cc 360 * Generate list of entities to test for get by id functions.
6c6e6187 361 * @param bool $sequential
a6c01b45
CW
362 * @return array
363 * Entities to be skipped
6c6e6187 364 */
b07a3bf9 365 public static function toBeSkipped_automock($sequential = FALSE) {
92915c55
TO
366 $entitiesWithoutGet = array(
367 'MailingContact',
368 'EntityTag',
369 'Participant',
370 'ParticipantPayment',
371 'Setting',
372 'SurveyRespondant',
373 'MailingRecipients',
374 'CustomSearch',
375 'Extension',
376 'ReportTemplate',
af9b09df 377 'System',
92915c55 378 );
b07a3bf9
TO
379 if ($sequential === TRUE) {
380 return $entitiesWithoutGet;
381 }
382 $entities = array();
383 foreach ($entitiesWithoutGet as $e) {
384 $entities[] = array($e);
385 }
386 return $entities;
387 }
388
389
b7d29345 390 /**
6c6e6187 391 * At this stage exclude the ones that don't pass & add them as we can troubleshoot them
1e1fdcf6
EM
392 * @param bool $sequential
393 * @return array
6c6e6187 394 */
6a488035
TO
395 public static function toBeSkipped_updatesingle($sequential = FALSE) {
396 $entitiesWithout = array(
92915c55
TO
397 'Attachment',
398 // pseudo-entity; testUpdateSingleValueAlter doesn't introspect properly on it. Multiple magic fields
6a488035
TO
399 'Mailing',
400 'MailingGroup',
401 'MailingJob',
402 'Address',
403 'MailingEventUnsubscribe',
404 'MailingEventSubscribe',
405 'Constant',
406 'Entity',
407 'Location',
408 'Domain',
409 'Profile',
410 'CustomValue',
411 'SurveyRespondant',
6a488035
TO
412 'UFMatch',
413 'UFJoin',
414 'UFField',
415 'OptionValue',
416 'Relationship',
417 'RelationshipType',
6a488035
TO
418 'Note',
419 'OptionGroup',
420 'Membership',
6a488035
TO
421 'Group',
422 'GroupOrganization',
423 'GroupNesting',
424 'Job',
425 'File',
426 'EntityTag',
427 'CustomField',
428 'CustomGroup',
429 'Contribution',
6a488035
TO
430 'ActivityType',
431 'MailingEventConfirm',
432 'Case',
433 'Contact',
434 'ContactType',
435 'MailingEventResubscribe',
436 'UFGroup',
437 'Activity',
438 'Email',
439 'Event',
440 'GroupContact',
441 'MembershipPayment',
442 'Participant',
443 'ParticipantPayment',
444 'LineItem',
6a488035
TO
445 'PledgePayment',
446 'ContributionPage',
447 'Phone',
faacb3e4 448 'PaymentProcessor',
6a488035 449 'Setting',
b14ce773 450 'MailingContact',
af9b09df 451 'SystemLog',
92915c55 452 //skip this because it doesn't make sense to update logs,
6a488035
TO
453 );
454 if ($sequential === TRUE) {
455 return $entitiesWithout;
456 }
457 $entities = array();
458 foreach ($entitiesWithout as $e) {
459 $entities[] = array(
460 $e,
461 );
462 }
463 return array('pledge');
464 return $entities;
465 }
466
b7d29345 467 /**
b9af4758 468 * At this stage exclude the ones that don't pass & add them as we can troubleshoot them
b7d29345 469 */
b9af4758
E
470 public static function toBeSkipped_getlimit() {
471 $entitiesWithout = array(
92915c55
TO
472 'Case',
473 //case api has non-std mandatory fields one of (case_id, contact_id, activity_id, contact_id)
474 'EntityTag',
475 // non-standard api - has inappropriate mandatory fields & doesn't implement limit
476 'Event',
477 // failed 'check that a 5 limit returns 5' - probably is_template field is wrong or something, or could be limit doesn't work right
478 'Extension',
479 // can't handle creating 25
480 'Note',
481 // fails on 5 limit - probably a set up problem
482 'Setting',
483 //a bit of a pseudoapi - keys by domain
b9af4758
E
484 );
485 return $entitiesWithout;
486 }
487
dcf5b21f
EM
488 /**
489 * At this stage exclude the ones that don't pass & add them as we can troubleshoot them
490 */
491 public static function toBeSkipped_getSqlOperators() {
492 $entitiesWithout = array(
6c6e6187 493 'Case', //case api has non-std mandatory fields one of (case_id, contact_id, activity_id, contact_id)
0ae1863a 494 'Contact', // on the todo list!
dcf5b21f
EM
495 'EntityTag', // non-standard api - has inappropriate mandatory fields & doesn't implement limit
496 'Extension', // can't handle creating 25
497 'Note', // note has a default get that isn't implemented in createTestObject -meaning you don't 'get' them
dcf5b21f
EM
498 'Setting', //a bit of a pseudoapi - keys by domain
499 );
500 return $entitiesWithout;
501 }
502
0f583c8f
EM
503 /**
504 * @param $entity
505 * @param $key
506 *
507 * @return array
508 */
9b873358 509 public function getKnownUnworkablesUpdateSingle($entity, $key) {
6a488035
TO
510 // can't update values are values for which updates don't result in the value being changed
511 $knownFailures = array(
03fe1a00
TO
512 'ActionSchedule' => array(
513 'cant_update' => array(
514 'group_id',
515 ),
516 ),
0f583c8f
EM
517 'ActivityContact' => array(
518 'cant_update' => array(
92915c55
TO
519 'activity_id',
520 //we have an FK on activity_id + contact_id + record id so if we don't leave this one distinct we get an FK constraint error
0f583c8f
EM
521 ),
522 ),
6a488035
TO
523 'Address' => array(
524 'cant_update' => array(
525 'state_province_id', //issues with country id - need to ensure same country
6c6e6187 526 'master_id', //creates relationship
6a488035 527 ),
21dfd5f5 528 'cant_return' => array(),
6a488035 529 ),
6ead217b
E
530 'Batch' => array(
531 'cant_update' => array(
532 'entity_table', // believe this field is defined in error
533 ),
534 'cant_return' => array(
535 'entity_table',
21dfd5f5 536 ),
6ead217b 537 ),
1753bd71
TO
538 'CaseType' => array(
539 'cant_update' => array(
540 'definition',
21dfd5f5 541 ),
1753bd71 542 ),
95520636
TO
543 'MembershipBlock' => array(
544 'cant_update' => array(
545 // The fake/auto-generated values leave us unable to properly cleanup fake data
546 'entity_type',
547 'entity_id',
21dfd5f5 548 ),
95520636 549 ),
7d543448 550 'ContributionSoft' => array(
551 'cant_update' => array(
552 // can't be changed through api
553 'pcp_id',
554 ),
555 ),
6a488035
TO
556 'Pledge' => array(
557 'cant_update' => array(
558 'pledge_original_installment_amount',
559 'installments',
560 'original_installment_amount',
561 'next_pay_date',
af9b09df 562 'amount', // can't be changed through API,
6a488035
TO
563 ),
564 'break_return' => array(// if these are passed in they are retrieved from the wrong table
565 'honor_contact_id',
566 'cancel_date',
567 'contribution_page_id',
568 'financial_account_id',
569 'financial_type_id',
21dfd5f5 570 'currency',
6a488035
TO
571 ),
572 'cant_return' => array(// can't be retrieved from api
573 'honor_type_id', //due to uniquename missing
574 'end_date',
575 'modified_date',
576 'acknowledge_date',
577 'start_date',
578 'frequency_day',
579 'currency',
580 'max_reminders',
581 'initial_reminder_day',
582 'additional_reminder_day',
583 'frequency_unit',
584 'pledge_contribution_page_id',
585 'pledge_status_id',
586 'pledge_campaign_id',
b06d9acd 587 'pledge_financial_type_id',
21dfd5f5 588 ),
6a488035
TO
589 ),
590 'PaymentProcessorType' => array(
591 'cant_update' => array(
592 'billing_mode',
593 ),
6c6e6187
TO
594 'break_return' => array(),
595 'cant_return' => array(),
6a488035 596 ),
db232378
EM
597 'PriceFieldValue' => array(
598 'cant_update' => array(
599 'weight', //won't update as there is no 1 in the same price set
600 ),
601 ),
5ba7b9fd
JV
602 'SavedSearch' => array(
603 // I think the fields below are generated based on form_values.
604 'cant_update' => array(
605 'search_custom_id',
606 'where_clause',
607 'select_tables',
608 'where_tables',
dafc75f8
JV
609 ),
610 ),
6a488035 611 );
9b873358 612 if (empty($knownFailures[$entity]) || empty($knownFailures[$entity][$key])) {
6a488035
TO
613 return array();
614 }
615 return $knownFailures[$entity][$key];
616 }
617
be2e0c6a 618 /* ----- testing the _get ----- */
6a488035
TO
619
620 /**
621 * @dataProvider toBeSkipped_get
be2e0c6a 622 * Entities that don't need a get action
1e1fdcf6 623 * @param $Entity
6a488035
TO
624 */
625 public function testNotImplemented_get($Entity) {
626 $result = civicrm_api($Entity, 'Get', array('version' => 3));
ba4a1892 627 $this->assertEquals(1, $result['is_error']);
311873a0 628 // $this->assertContains("API ($Entity, Get) does not exist", $result['error_message']);
6c6e6187 629 $this->assertRegExp('/API (.*) does not exist/', $result['error_message']);
6a488035
TO
630 }
631
632 /**
633 * @dataProvider entities
634 * @expectedException PHPUnit_Framework_Error
1e1fdcf6 635 * @param $Entity
6a488035
TO
636 */
637 public function testWithoutParam_get($Entity) {
638 // should get php complaining that a param is missing
639 $result = civicrm_api($Entity, 'Get');
640 }
641
642 /**
643 * @dataProvider entities
1e1fdcf6 644 * @param $Entity
6a488035
TO
645 */
646 public function testGetFields($Entity) {
bd6658bd 647 if (in_array($Entity, $this->deprecatedAPI) || $Entity == 'Entity' || $Entity == 'CustomValue') {
6a488035
TO
648 return;
649 }
650
651 $result = civicrm_api($Entity, 'getfields', array('version' => 3));
652 $this->assertTrue(is_array($result['values']), "$Entity ::get fields doesn't return values array in line " . __LINE__);
653 foreach ($result['values'] as $key => $value) {
654 $this->assertTrue(is_array($value), $Entity . "::" . $key . " is not an array in line " . __LINE__);
655 }
656 }
657
658 /**
659 * @dataProvider entities_get
1e1fdcf6 660 * @param $Entity
6a488035
TO
661 */
662 public function testEmptyParam_get($Entity) {
663
664 if (in_array($Entity, $this->toBeImplemented['get'])) {
665 // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented");
666 return;
667 }
668 $result = civicrm_api($Entity, 'Get', array());
ba4a1892 669 $this->assertEquals(1, $result['is_error']);
6a488035
TO
670 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
671 }
92915c55 672
6a488035
TO
673 /**
674 * @dataProvider entities_get
1e1fdcf6 675 * @param $Entity
6a488035
TO
676 */
677 public function testEmptyParam_getString($Entity) {
678
679 if (in_array($Entity, $this->toBeImplemented['get'])) {
680 // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented");
681 return;
682 }
d0e1eff2 683 $result = $this->callAPIFailure($Entity, 'Get', 'string');
6a488035
TO
684 $this->assertEquals(2000, $result['error_code']);
685 $this->assertEquals('Input variable `params` is not an array', $result['error_message']);
686 }
92915c55 687
6a488035
TO
688 /**
689 * @dataProvider entities_get
690 * @Xdepends testEmptyParam_get // no need to test the simple if the empty doesn't work/is skipped. doesn't seem to work
1e1fdcf6 691 * @param $Entity
6a488035
TO
692 */
693 public function testSimple_get($Entity) {
694 // $this->markTestSkipped("test gives core error on test server (but not on our locals). Skip until we can get server to pass");
6a488035
TO
695 if (in_array($Entity, $this->toBeImplemented['get'])) {
696 return;
697 }
698 $result = civicrm_api($Entity, 'Get', array('version' => 3));
699 // @TODO: list the get that have mandatory params
700 if ($result['is_error']) {
701 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
702 // either id or contact_id or entity_id is one of the field missing
703 $this->assertContains("id", $result['error_message']);
704 }
705 else {
706 $this->assertEquals(3, $result['version']);
707 $this->assertArrayHasKey('count', $result);
708 $this->assertArrayHasKey('values', $result);
709 }
710 }
711
2fc5f1e7
EM
712 /**
713 * @dataProvider custom_data_entities_get
1e1fdcf6 714 * @param $entityName
2fc5f1e7
EM
715 */
716 public function testCustomDataGet($entityName) {
717 $this->createLoggedInUser();// so subsidiary activities are created
718 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, $entityName . 'Test.php');
719 $customFieldName = 'custom_' . $ids['custom_field_id'];
720 $objects = $this->getMockableBAOObjects($entityName, 1);
721 $params = array('id' => $objects[0]->id, 'custom_' . $ids['custom_field_id'] => "custom string");
722 $result = $this->callAPISuccess($entityName, 'create', $params);
723
724 $getParams = array('id' => $result['id'], 'return' => array($customFieldName));
725 $check = $this->callAPISuccess($entityName, 'get', $getParams);
726 $this->assertEquals("custom string", $check['values'][$check['id']][$customFieldName]);
727
728 $this->customFieldDelete($ids['custom_field_id']);
729 $this->customGroupDelete($ids['custom_group_id']);
730 $this->callAPISuccess($entityName, 'delete', array('id' => $result['id']));
731 }
732
6a488035
TO
733 /**
734 * @dataProvider entities_get
1e1fdcf6 735 * @param $Entity
6a488035
TO
736 */
737 public function testAcceptsOnlyID_get($Entity) {
738 // big random number. fun fact: if you multiply it by pi^e, the result is another random number, but bigger ;)
739 $nonExistantID = 30867307034;
b14ce773 740 if (in_array($Entity, $this->toBeImplemented['get'])
92915c55 741 || in_array($Entity, $this->toBeSkipped_getByID())
b14ce773 742 ) {
6a488035
TO
743 return;
744 }
745
746 // FIXME
747 // the below function returns different values and hence an early return
748 // we'll fix this once beta1 is released
749 // return;
750
c679daca 751 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
6a488035
TO
752
753 if ($result['is_error']) {
754 // just to get a clearer message in the log
755 $this->assertEquals("only id should be enough", $result['error_message']);
756 }
757 if (!in_array($Entity, $this->onlyIDNonZeroCount['get'])) {
758 $this->assertEquals(0, $result['count']);
759 }
760 }
761
762 /**
eceb18cc 763 * Create two entities and make sure we can fetch them individually by ID.
4a97890c
TO
764 *
765 * @dataProvider entities_get
766 *
767 * limitations include the problem with avoiding loops when creating test objects -
768 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
769 * Currency - only seems to support US
1e1fdcf6 770 * @param $entityName
4a97890c
TO
771 */
772 public function testByID_get($entityName) {
b07a3bf9 773 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
4a97890c
TO
774 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
775 return;
776 }
777
18eee50e 778 $baos = $this->getMockableBAOObjects($entityName);
779 list($baoObj1, $baoObj2) = $baos;
4a97890c
TO
780
781 // fetch first by ID
6ead217b 782 $result = $this->callAPISuccess($entityName, 'get', array(
4a97890c
TO
783 'id' => $baoObj1->id,
784 ));
6ead217b 785
4a97890c
TO
786 $this->assertTrue(!empty($result['values'][$baoObj1->id]), 'Should find first object by id');
787 $this->assertEquals($baoObj1->id, $result['values'][$baoObj1->id]['id'], 'Should find id on first object');
788 $this->assertEquals(1, count($result['values']));
789
790 // fetch second by ID
6ead217b 791 $result = $this->callAPISuccess($entityName, 'get', array(
4a97890c
TO
792 'id' => $baoObj2->id,
793 ));
4a97890c
TO
794 $this->assertTrue(!empty($result['values'][$baoObj2->id]), 'Should find second object by id');
795 $this->assertEquals($baoObj2->id, $result['values'][$baoObj2->id]['id'], 'Should find id on second object');
796 $this->assertEquals(1, count($result['values']));
797 }
798
b9af4758
E
799 /**
800 * Ensure that the "get" operation accepts limiting the #result records.
801 *
802 * TODO Consider making a separate entity list ("entities_getlimit")
803 * For the moment, the "entities_updatesingle" list should give a good
804 * sense for which entities support createTestObject
805 *
806 * @dataProvider entities_getlimit
8efea814 807 *
c490a46a 808 * @param string $entityName
b9af4758 809 */
00be9182 810 public function testLimit($entityName) {
b9af4758
E
811 $cases = array(); // each case is array(0 => $inputtedApiOptions, 1 => $expectedResultCount)
812 $cases[] = array(
813 array('options' => array('limit' => NULL)),
ebddc2d9
EM
814 30,
815 'check that a NULL limit returns unlimited',
b9af4758
E
816 );
817 $cases[] = array(
818 array('options' => array('limit' => FALSE)),
ebddc2d9
EM
819 30,
820 'check that a FALSE limit returns unlimited',
b9af4758
E
821 );
822 $cases[] = array(
823 array('options' => array('limit' => 0)),
ebddc2d9
EM
824 30,
825 'check that a 0 limit returns unlimited',
b9af4758
E
826 );
827 $cases[] = array(
828 array('options' => array('limit' => 5)),
829 5,
830 'check that a 5 limit returns 5',
831 );
832 $cases[] = array(
833 array(),
834 25,
835 'check that no limit returns 25',
836 );
837
6252a38c 838 $baoString = _civicrm_api3_get_BAO($entityName);
b9af4758
E
839 if (empty($baoString)) {
840 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
841 return;
842 }
843
844 // make 30 test items -- 30 > 25 (the default limit)
4038f8ec 845 $ids = array();
b9af4758 846 for ($i = 0; $i < 30; $i++) {
8d5544c5 847 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
4038f8ec 848 $ids[] = $baoObj->id;
b9af4758
E
849 }
850
851 // each case is array(0 => $inputtedApiOptions, 1 => $expectedResultCount)
852 foreach ($cases as $case) {
ebddc2d9 853 $this->checkLimitAgainstExpected($entityName, $case[0], $case[1], $case[2]);
a85a667f
EM
854
855 //non preferred / legacy syntax
22e263ad 856 if (isset($case[0]['options']['limit'])) {
ebddc2d9
EM
857 $this->checkLimitAgainstExpected($entityName, array('rowCount' => $case[0]['options']['limit']), $case[1], $case[2]);
858 $this->checkLimitAgainstExpected($entityName, array('option_limit' => $case[0]['options']['limit']), $case[1], $case[2]);
859 $this->checkLimitAgainstExpected($entityName, array('option.limit' => $case[0]['options']['limit']), $case[1], $case[2]);
a85a667f 860 }
b9af4758 861 }
4038f8ec
TO
862 foreach ($ids as $id) {
863 CRM_Core_DAO::deleteTestObjects($baoString, array('id' => $id));
864 }
8d5544c5 865 $baoObj->free();
b9af4758
E
866 }
867
dcf5b21f
EM
868 /**
869 * Ensure that the "get" operation accepts limiting the #result records.
870 *
871 * @dataProvider entities_getSqlOperators
872 *
c490a46a 873 * @param string $entityName
dcf5b21f 874 */
00be9182 875 public function testSqlOperators($entityName) {
dcf5b21f
EM
876 $baoString = _civicrm_api3_get_BAO($entityName);
877 if (empty($baoString)) {
878 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
879 return;
880 }
881 $entities = $this->callAPISuccess($entityName, 'get', array('options' => array('limit' => 0), 'return' => 'id'));
882 $entities = array_keys($entities['values']);
883 $totalEntities = count($entities);
884 if ($totalEntities < 3) {
885 $ids = array();
886 for ($i = 0; $i < 3 - $totalEntities; $i++) {
887 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
888 $ids[] = $baoObj->id;
889 }
890 $totalEntities = 3;
891 }
892 $entities = $this->callAPISuccess($entityName, 'get', array('options' => array('limit' => 0)));
893 $entities = array_keys($entities['values']);
894 $this->assertGreaterThan(2, $totalEntities);
895 $this->callAPISuccess($entityName, 'getsingle', array('id' => array('IN' => array($entities[0]))));
896 $this->callAPISuccessGetCount($entityName, array('id' => array('NOT IN' => array($entities[0]))), $totalEntities - 1);
897 $this->callAPISuccessGetCount($entityName, array('id' => array('>' => $entities[0])), $totalEntities - 1);
898 }
899
ebddc2d9 900 /**
eceb18cc 901 * Check that get fetches an appropriate number of results.
ebddc2d9 902 *
e16033b4
TO
903 * @param string $entityName
904 * Name of entity to test.
dcf5b21f 905 * @param array $params
e16033b4 906 * @param int $limit
dcf5b21f 907 * @param string $message
ebddc2d9 908 */
00be9182 909 public function checkLimitAgainstExpected($entityName, $params, $limit, $message) {
ebddc2d9 910 $result = $this->callAPISuccess($entityName, 'get', $params);
22e263ad 911 if ($limit == 30) {
ebddc2d9
EM
912 $this->assertGreaterThanOrEqual($limit, $result['count'], $message);
913 $this->assertGreaterThanOrEqual($limit, $result['count'], $message);
914 }
915 else {
916 $this->assertEquals($limit, $result['count'], $message);
917 $this->assertEquals($limit, count($result['values']), $message);
918 }
919 }
92915c55 920
afb0ff51
TO
921 /**
922 * Create two entities and make sure we can fetch them individually by ID (e.g. using "contact_id=>2"
923 * or "group_id=>4")
924 *
925 * @dataProvider entities_get
926 *
927 * limitations include the problem with avoiding loops when creating test objects -
928 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
929 * Currency - only seems to support US
1e1fdcf6
EM
930 * @param $entityName
931 * @throws \PHPUnit_Framework_IncompleteTestError
afb0ff51
TO
932 */
933 public function testByIDAlias_get($entityName) {
c4de8b59 934 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
afb0ff51
TO
935 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
936 return;
937 }
938
6252a38c 939 $baoString = _civicrm_api3_get_BAO($entityName);
afb0ff51
TO
940 if (empty($baoString)) {
941 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
942 return;
943 }
944
c4de8b59
TO
945 $idFieldName = _civicrm_api_get_entity_name_from_camel($entityName) . '_id';
946
afb0ff51
TO
947 // create entities
948 $baoObj1 = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
a1a2a83d 949 $this->assertTrue(is_int($baoObj1->id), 'check first id');
afb0ff51
TO
950 $this->deletableTestObjects[$baoString][] = $baoObj1->id;
951 $baoObj2 = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
a1a2a83d 952 $this->assertTrue(is_int($baoObj2->id), 'check second id');
afb0ff51
TO
953 $this->deletableTestObjects[$baoString][] = $baoObj2->id;
954
955 // fetch first by ID
956 $result = civicrm_api($entityName, 'get', array(
957 'version' => 3,
c4de8b59 958 $idFieldName => $baoObj1->id,
afb0ff51
TO
959 ));
960 $this->assertAPISuccess($result);
961 $this->assertTrue(!empty($result['values'][$baoObj1->id]), 'Should find first object by id');
962 $this->assertEquals($baoObj1->id, $result['values'][$baoObj1->id]['id'], 'Should find id on first object');
963 $this->assertEquals(1, count($result['values']));
964
965 // fetch second by ID
966 $result = civicrm_api($entityName, 'get', array(
967 'version' => 3,
c4de8b59 968 $idFieldName => $baoObj2->id,
afb0ff51
TO
969 ));
970 $this->assertAPISuccess($result);
971 $this->assertTrue(!empty($result['values'][$baoObj2->id]), 'Should find second object by id');
972 $this->assertEquals($baoObj2->id, $result['values'][$baoObj2->id]['id'], 'Should find id on second object');
973 $this->assertEquals(1, count($result['values']));
974 }
975
976 /**
6a488035 977 * @dataProvider entities_get
1e1fdcf6 978 * @param $Entity
6a488035
TO
979 */
980 public function testNonExistantID_get($Entity) {
981 // cf testAcceptsOnlyID_get
982 $nonExistantID = 30867307034;
983 if (in_array($Entity, $this->toBeImplemented['get'])) {
984 return;
985 }
986
987 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
988
989 // redundant with testAcceptsOnlyID_get
990 if ($result['is_error']) {
991 return;
992 }
993
6a488035
TO
994 $this->assertArrayHasKey('version', $result);
995 $this->assertEquals(3, $result['version']);
996 if (!in_array($Entity, $this->onlyIDNonZeroCount['get'])) {
997 $this->assertEquals(0, $result['count']);
998 }
999 }
1000
a1a2a83d 1001 /* ---- testing the _create ---- */
6a488035
TO
1002
1003 /**
1004 * @dataProvider toBeSkipped_create
6c6e6187 1005 entities that don't need a create action
1e1fdcf6 1006 * @param $Entity
6a488035
TO
1007 */
1008 public function testNotImplemented_create($Entity) {
1009 $result = civicrm_api($Entity, 'Create', array('version' => 3));
ba4a1892 1010 $this->assertEquals(1, $result['is_error']);
311873a0 1011 $this->assertContains(strtolower("API ($Entity, Create) does not exist"), strtolower($result['error_message']));
6a488035
TO
1012 }
1013
1014 /**
1015 * @dataProvider entities
1016 * @expectedException PHPUnit_Framework_Error
1e1fdcf6 1017 * @param $Entity
6a488035
TO
1018 */
1019 public function testWithoutParam_create($Entity) {
1020 // should create php complaining that a param is missing
1021 $result = civicrm_api($Entity, 'Create');
1022 }
1023
1024 /**
1025 * @dataProvider entities_create
1e1fdcf6
EM
1026 * @param $Entity
1027 * @throws \PHPUnit_Framework_IncompleteTestError
6a488035
TO
1028 */
1029 public function testEmptyParam_create($Entity) {
e4f46be0 1030 $this->markTestIncomplete("fixing this test to test the api functions fails on numerous tests
f27f2724 1031 which will either create a completely blank entity (batch, participant status) or
18eee50e 1032 have a damn good crack at it (e.g mailing job). Marking this as incomplete beats false success");
18eee50e 1033 return;
6a488035
TO
1034 if (in_array($Entity, $this->toBeImplemented['create'])) {
1035 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
1036 return;
1037 }
18eee50e 1038 $result = $this->callAPIFailure($Entity, 'Create', array());
6a488035
TO
1039 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
1040 }
1041
18eee50e 1042 /**
1043 * @dataProvider entities_create
1044 *
1045 * Check that create doesn't work with an invalid
1e1fdcf6
EM
1046 * @param $Entity
1047 * @throws \PHPUnit_Framework_IncompleteTestError
18eee50e 1048 */
1049 public function testInvalidID_create($Entity) {
1050 // turn test off for noew
6ead217b 1051 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
18eee50e 1052 return;
1053 if (in_array($Entity, $this->toBeImplemented['create'])) {
1054 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
1055 return;
1056 }
1057 $result = $this->callAPIFailure($Entity, 'Create', array('id' => 999));
1058 }
1059
6a488035
TO
1060 /**
1061 * @dataProvider entities
1062 */
1063 public function testCreateWrongTypeParamTag_create() {
1064 $result = civicrm_api("Tag", 'Create', 'this is not a string');
ba4a1892 1065 $this->assertEquals(1, $result['is_error']);
6a488035
TO
1066 $this->assertEquals("Input variable `params` is not an array", $result['error_message']);
1067 }
1068
1069 /**
1070 * @dataProvider entities_updatesingle
1071 *
1072 * limitations include the problem with avoiding loops when creating test objects -
1073 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
1074 * Currency - only seems to support US
1e1fdcf6 1075 * @param $entityName
6a488035
TO
1076 */
1077 public function testCreateSingleValueAlter($entityName) {
1078 if (in_array($entityName, $this->toBeImplemented['create'])) {
1079 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
1080 return;
1081 }
1082
6252a38c 1083 $baoString = _civicrm_api3_get_BAO($entityName);
6a488035
TO
1084 $this->assertNotEmpty($baoString, $entityName);
1085 $this->assertNotEmpty($entityName, $entityName);
905fd0e9 1086 $fieldsGet = $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'get', 'options' => array('get_options' => 'all')));
9b873358 1087 if ($entityName != 'Pledge') {
905fd0e9 1088 $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'create', 'options' => array('get_options' => 'all')));
6a488035
TO
1089 }
1090 $fields = $fields['values'];
39bc176e 1091 $return = array_keys($fieldsGet['values']);
6a488035 1092 $valuesNotToReturn = $this->getKnownUnworkablesUpdateSingle($entityName, 'break_return');
6c6e6187 1093 // these can't be requested as return values
1fd111c8 1094 $entityValuesThatDoNotWork = array_merge(
92915c55
TO
1095 $this->getKnownUnworkablesUpdateSingle($entityName, 'cant_update'),
1096 $this->getKnownUnworkablesUpdateSingle($entityName, 'cant_return'),
1097 $valuesNotToReturn
1098 );
6a488035 1099
6c6e6187 1100 $return = array_diff($return, $valuesNotToReturn);
6a488035
TO
1101 $baoObj = new CRM_Core_DAO();
1102 $baoObj->createTestObject($baoString, array('currency' => 'USD'), 2, 0);
39bc176e
EM
1103
1104 $getEntities = $this->callAPISuccess($entityName, 'get', array(
6ead217b
E
1105 'sequential' => 1,
1106 'return' => $return,
1107 'options' => array(
1108 'sort' => 'id DESC',
1109 'limit' => 2,
1110 ),
1111 ));
a8699624 1112
6a488035 1113 // lets use first rather than assume only one exists
39bc176e
EM
1114 $entity = $getEntities['values'][0];
1115 $entity2 = $getEntities['values'][1];
1116 $this->deletableTestObjects[$baoString][] = $entity['id'];
1117 $this->deletableTestObjects[$baoString][] = $entity2['id'];
6a488035 1118 foreach ($fields as $field => $specs) {
a8699624 1119 $resetFKTo = NULL;
6a488035
TO
1120 $fieldName = $field;
1121 if (!empty($specs['uniquename'])) {
1122 $fieldName = $specs['uniquename'];
1123 }
1124 if ($field == 'currency' || $field == 'id' || $field == strtolower($entityName) . '_id'
92915c55
TO
1125 || in_array($field, $entityValuesThatDoNotWork)
1126 ) {
6a488035
TO
1127 //@todo id & entity_id are correct but we should fix currency & frequency_day
1128 continue;
1129 }
0ce6d639 1130 $this->assertArrayHasKey('type', $specs, "the _spec function for $entityName field $field does not specify the type");
6a488035
TO
1131 switch ($specs['type']) {
1132 case CRM_Utils_Type::T_DATE:
6a488035
TO
1133 $entity[$fieldName] = '2012-05-20';
1134 break;
6c6e6187 1135
b6afca8f 1136 case CRM_Utils_Type::T_TIMESTAMP:
6a488035
TO
1137 case 12:
1138 $entity[$fieldName] = '2012-05-20 03:05:20';
1139 break;
1140
1141 case CRM_Utils_Type::T_STRING:
1142 case CRM_Utils_Type::T_BLOB:
1143 case CRM_Utils_Type::T_MEDIUMBLOB:
1144 case CRM_Utils_Type::T_TEXT:
1145 case CRM_Utils_Type::T_LONGTEXT:
1146 case CRM_Utils_Type::T_EMAIL:
e39fff89 1147 if ($fieldName == 'form_values' && $entity_name == 'SavedSearch') {
f1d23743
JV
1148 // This is a hack for the SavedSearch API. It expects form_values
1149 // to be a serialized array.
1150 // If you want to fix this, you should definitely read this forum
1151 // post.
1152 // http://forum.civicrm.org/index.php/topic,33990.0.html
1153 // See also my question on the CiviCRM Stack Exchange:
1154 // https://civicrm.stackexchange.com/questions/3437
e29aa052 1155 $entity[$fieldName] = array('sort_name' => "SortName2");
f1d23743
JV
1156 }
1157 else {
1158 $entity[$fieldName] = substr('New String', 0, CRM_Utils_Array::Value('maxlength', $specs, 100));
1159 }
6a488035
TO
1160 break;
1161
1162 case CRM_Utils_Type::T_INT:
1163 // probably created with a 1
a8699624
EM
1164 if ($fieldName == 'weight') {
1165 $entity[$fieldName] = 2;
1166 }
1167 elseif (!empty($specs['FKClassName'])) {
9b873358 1168 if ($specs['FKClassName'] == $baoString) {
6a488035
TO
1169 $entity[$fieldName] = (string) $entity2['id'];
1170 }
92e4c2a5 1171 else {
6ead217b 1172 $uniqueName = CRM_Utils_Array::value('uniqueName', $specs);
a8699624
EM
1173 if (!empty($entity[$fieldName])) {
1174 $resetFKTo = array($fieldName => $entity[$fieldName]);
1175 }
6ead217b 1176 $entity[$fieldName] = (string) empty($entity2[$field]) ? CRM_Utils_Array::value($uniqueName, $entity2) : $entity2[$field];
6c6e6187 1177 //todo - there isn't always something set here - & our checking on unset values is limited
6a488035
TO
1178 if (empty($entity[$field])) {
1179 unset($entity[$field]);
1180 }
1181 }
1182 }
a8699624
EM
1183 else {
1184 $entity[$fieldName] = '6';
1185 }
6a488035
TO
1186 break;
1187
6a488035
TO
1188 case CRM_Utils_Type::T_BOOLEAN:
1189 // probably created with a 1
1190 $entity[$fieldName] = '0';
1191 break;
1192
1193 case CRM_Utils_Type::T_FLOAT:
1194 case CRM_Utils_Type::T_MONEY:
edd31a24 1195 $entity[$field] = '22.75';
6a488035
TO
1196 break;
1197
1198 case CRM_Utils_Type::T_URL:
1199 $entity[$field] = 'warm.beer.com';
1200 }
905fd0e9
CW
1201 if (empty($specs['FKClassName']) && (!empty($specs['pseudoconstant']) || !empty($specs['options']))) {
1202 $options = CRM_Utils_Array::value('options', $specs, array());
1203 if (!$options) {
edd31a24 1204 //eg. pdf_format id doesn't ship with any
22e263ad 1205 if (isset($specs['pseudoconstant']['optionGroupName'])) {
92915c55 1206 $optionValue = $this->callAPISuccess('option_value', 'create', array(
905fd0e9 1207 'option_group_id' => $specs['pseudoconstant']['optionGroupName'],
af9b09df 1208 'label' => 'new option value',
905fd0e9 1209 'sequential' => 1,
92915c55 1210 ));
905fd0e9
CW
1211 $optionValue = $optionValue['values'];
1212 $options[$optionValue[0]['value']] = 'new option value';
edd31a24 1213 }
3d3ef918 1214 }
905fd0e9
CW
1215 $entity[$field] = array_rand($options);
1216 }
1217 if (!empty($specs['FKClassName']) && !empty($specs['pseudoconstant'])) {
1218 // in the weird situation where a field has both an fk and pseudoconstant defined,
1219 // e.g. campaign_id field, need to flush caches.
1220 // FIXME: Why doesn't creating a campaign clear caches?
1221 civicrm_api3($entityName, 'getfields', array('cache_clear' => 1));
6a488035
TO
1222 }
1223 $updateParams = array(
6a488035 1224 'id' => $entity['id'],
6ead217b 1225 $field => isset($entity[$field]) ? $entity[$field] : NULL,
6a488035 1226 );
22e263ad 1227 if (isset($updateParams['financial_type_id']) && in_array($entityName, array('Grant'))) {
deb562a8
EM
1228 //api has special handling on these 2 fields for backward compatibility reasons
1229 $entity['contribution_type_id'] = $updateParams['financial_type_id'];
1230 }
6a488035 1231
f27f2724 1232 $update = $this->callAPISuccess($entityName, 'create', $updateParams);
6a488035
TO
1233 $checkParams = array(
1234 'id' => $entity['id'],
6a488035
TO
1235 'sequential' => 1,
1236 'return' => $return,
1237 'options' => array(
1238 'sort' => 'id DESC',
1239 'limit' => 2,
1240 ),
1241 );
1242
f27f2724 1243 $checkEntity = $this->callAPISuccess($entityName, 'getsingle', $checkParams);
92915c55
TO
1244 $this->assertAPIArrayComparison($entity, $checkEntity, array(), "checking if $fieldName was correctly updated\n" . print_r(array(
1245 'update-params' => $updateParams,
1246 'update-result' => $update,
1247 'getsingle-params' => $checkParams,
1248 'getsingle-result' => $checkEntity,
af9b09df 1249 'expected entity' => $entity,
92915c55 1250 ), TRUE));
a8699624
EM
1251 if ($resetFKTo) {
1252 //reset the foreign key fields because otherwise our cleanup routine fails & some other unexpected stuff can kick in
1253 $entity = array_merge($entity, $resetFKTo);
1254 $updateParams = array_merge($updateParams, $resetFKTo);
1255 $this->callAPISuccess($entityName, 'create', $updateParams);
22e263ad 1256 if (isset($updateParams['financial_type_id']) && in_array($entityName, array('Grant'))) {
a8699624
EM
1257 //api has special handling on these 2 fields for backward compatibility reasons
1258 $entity['contribution_type_id'] = $updateParams['financial_type_id'];
1259 }
1260 }
6a488035 1261 }
6a488035
TO
1262 $baoObj->free();
1263 }
1264
be2e0c6a 1265 /* ---- testing the _getFields ---- */
6a488035 1266
be2e0c6a 1267 /* ---- testing the _delete ---- */
6a488035
TO
1268
1269 /**
1270 * @dataProvider toBeSkipped_delete
6c6e6187 1271 entities that don't need a delete action
1e1fdcf6 1272 * @param $Entity
6a488035
TO
1273 */
1274 public function testNotImplemented_delete($Entity) {
1275 $nonExistantID = 151416349;
1276 $result = civicrm_api($Entity, 'Delete', array('version' => 3, 'id' => $nonExistantID));
ba4a1892 1277 $this->assertEquals(1, $result['is_error']);
311873a0 1278 $this->assertContains(strtolower("API ($Entity, Delete) does not exist"), strtolower($result['error_message']));
6a488035
TO
1279 }
1280
1281 /**
1282 * @dataProvider entities
1283 * @expectedException PHPUnit_Framework_Error
1e1fdcf6 1284 * @param $Entity
6a488035
TO
1285 */
1286 public function testWithoutParam_delete($Entity) {
1287 // should delete php complaining that a param is missing
1288 $result = civicrm_api($Entity, 'Delete');
1289 }
1290
1291 /**
1292 * @dataProvider entities_delete
1e1fdcf6 1293 * @param $Entity
6a488035
TO
1294 */
1295 public function testEmptyParam_delete($Entity) {
1296 if (in_array($Entity, $this->toBeImplemented['delete'])) {
1297 // $this->markTestIncomplete("civicrm_api3_{$Entity}_delete to be implemented");
1298 return;
1299 }
1300 $result = civicrm_api($Entity, 'Delete', array());
ba4a1892 1301 $this->assertEquals(1, $result['is_error']);
6a488035
TO
1302 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
1303 }
92915c55 1304
18eee50e 1305 /**
1306 * @dataProvider entities_delete
1e1fdcf6
EM
1307 * @param $Entity
1308 * @throws \PHPUnit_Framework_IncompleteTestError
18eee50e 1309 */
1310 public function testInvalidID_delete($Entity) {
6ead217b
E
1311 // turn test off for now
1312 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
18eee50e 1313 return;
1314 if (in_array($Entity, $this->toBeImplemented['delete'])) {
1315 // $this->markTestIncomplete("civicrm_api3_{$Entity}_delete to be implemented");
1316 return;
1317 }
1318 $result = $this->callAPIFailure($Entity, 'Delete', array('id' => 999));
1319 }
92915c55 1320
6a488035
TO
1321 /**
1322 * @dataProvider entities
1323 */
1324 public function testDeleteWrongTypeParamTag_delete() {
1325 $result = civicrm_api("Tag", 'Delete', 'this is not a string');
ba4a1892 1326 $this->assertEquals(1, $result['is_error']);
6a488035
TO
1327 $this->assertEquals("Input variable `params` is not an array", $result['error_message']);
1328 }
1329
18eee50e 1330 /**
1331 * Create two entities and make sure delete action only deletes one!
1332 *
1333 * @dataProvider entities_delete
1334 *
1335 * limitations include the problem with avoiding loops when creating test objects -
1336 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
1337 * Currency - only seems to support US
1e1fdcf6
EM
1338 * @param $entityName
1339 * @throws \PHPUnit_Framework_IncompleteTestError
18eee50e 1340 */
1341 public function testByID_delete($entityName) {
1342 // turn test off for noew
1343 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
1344 return;
1345
1346 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
1347 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
1348 return;
1349 }
1350 $startCount = $this->callAPISuccess($entityName, 'getcount', array());
1351 $createcount = 2;
1352 $baos = $this->getMockableBAOObjects($entityName, $createcount);
1353 list($baoObj1, $baoObj2) = $baos;
1354
1355 // make sure exactly 2 exist
1356 $result = $this->callAPISuccess($entityName, 'getcount', array(),
1357 $createcount + $startCount
1358 );
1359
1360 $this->callAPISuccess($entityName, 'delete', array('id' => $baoObj2->id));
1361 //make sure 1 less exists now
1362 $result = $this->callAPISuccess($entityName, 'getcount', array(),
6c6e6187 1363 ($createcount + $startCount) - 1
18eee50e 1364 );
1365
1366 //make sure id #1 exists
1367 $result = $this->callAPISuccess($entityName, 'getcount', array('id' => $baoObj1->id),
1368 1
1369 );
1370 //make sure id #2 desn't exist
1371 $result = $this->callAPISuccess($entityName, 'getcount', array('id' => $baoObj2->id),
1372 0
1373 );
1374 }
1375
32dafeec
EM
1376 /**
1377 * Create two entities and make sure delete action only deletes one!
1378 *
1379 * @dataProvider entities_getfields
1e1fdcf6 1380 * @param $entity
32dafeec
EM
1381 */
1382 public function testGetfieldsHasTitle($entity) {
1383 $entities = $this->getEntitiesSupportingCustomFields();
1384 if (in_array($entity, $entities)) {
1385 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, $entity . 'Test.php');
1386 }
6c6e6187 1387 $actions = $this->callAPISuccess($entity, 'getactions', array());
4c41ecb2 1388 foreach ($actions['values'] as $action) {
92915c55 1389 if (substr($action, -7) == '_create' || substr($action, -4) == '_get' || substr($action, -7) == '_delete') {
7385c761
EM
1390 //getactions can't distinguish between contribution_page.create & contribution_page.create
1391 continue;
1392 }
4c41ecb2
EM
1393 $fields = $this->callAPISuccess($entity, 'getfields', array('action' => $action));
1394 if (!empty($ids) && in_array($action, array('create', 'get'))) {
7385c761 1395 $this->assertArrayHasKey('custom_' . $ids['custom_field_id'], $fields['values']);
4c41ecb2 1396 }
32dafeec 1397
4c41ecb2
EM
1398 foreach ($fields['values'] as $fieldName => $fieldSpec) {
1399 $this->assertArrayHasKey('title', $fieldSpec, "no title for $entity - $fieldName on action $action");
1400 $this->assertNotEmpty($fieldSpec['title'], "empty title for $entity - $fieldName");
1401 }
32dafeec 1402 }
32dafeec
EM
1403 if (!empty($ids)) {
1404 $this->customFieldDelete($ids['custom_field_id']);
1405 $this->customGroupDelete($ids['custom_group_id']);
1406 }
1407 }
1408
1409 /**
1410 * @return array
1411 */
1412 public function getEntitiesSupportingCustomFields() {
1413 $entities = self::custom_data_entities_get();
1414 $returnEntities = array();
1415 foreach ($entities as $entityArray) {
1416 $returnEntities[] = $entityArray[0];
1417 }
1418 return $returnEntities;
1419 }
92915c55 1420
8efea814 1421 /**
100fef9d 1422 * @param string $entityName
8efea814
EM
1423 * @param int $count
1424 *
8efea814
EM
1425 * @return array
1426 */
1427 private function getMockableBAOObjects($entityName, $count = 2) {
6252a38c 1428 $baoString = _civicrm_api3_get_BAO($entityName);
18eee50e 1429 if (empty($baoString)) {
1430 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
fcb93467 1431 return array();
18eee50e 1432 }
1433 $baos = array();
6ead217b 1434 $i = 0;
22e263ad 1435 while ($i < $count) {
6c6e6187 1436 // create entities
18eee50e 1437 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
a1a2a83d 1438 $this->assertTrue(is_int($baoObj->id), 'check first id');
18eee50e 1439 $this->deletableTestObjects[$baoString][] = $baoObj->id;
1440 $baos[] = $baoObj;
2241036a 1441 $i++;
18eee50e 1442 }
1443 return $baos;
1444 }
1445
1446
6a488035 1447 /**
fe482240 1448 * Verify that HTML metacharacters provided as inputs appear consistently.
6a488035
TO
1449 * as outputs.
1450 *
1451 * At time of writing, the encoding scheme requires (for example) that an
1452 * event title be partially-HTML-escaped before writing to DB. To provide
1453 * consistency, the API must perform extra encoding and decoding on some
1454 * fields.
1455 *
1456 * In this example, the event 'title' is subject to encoding, but the
1457 * event 'description' is not.
1458 */
1459 public function testEncodeDecodeConsistency() {
1460 // Create example
1461 $createResult = civicrm_api('Event', 'Create', array(
1462 'version' => 3,
1463 'title' => 'CiviCRM <> TheRest',
1464 'description' => 'TheRest <> CiviCRM',
1465 'event_type_id' => 1,
1466 'is_public' => 1,
1467 'start_date' => 20081021,
1468 ));
1469 $this->assertAPISuccess($createResult);
1470 $eventId = $createResult['id'];
1471 $this->assertEquals('CiviCRM <> TheRest', $createResult['values'][$eventId]['title']);
1472 $this->assertEquals('TheRest <> CiviCRM', $createResult['values'][$eventId]['description']);
1473
1474 // Verify "get" handles decoding in result value
1475 $getByIdResult = civicrm_api('Event', 'Get', array(
1476 'version' => 3,
1477 'id' => $eventId,
1478 ));
1479 $this->assertAPISuccess($getByIdResult);
1480 $this->assertEquals('CiviCRM <> TheRest', $getByIdResult['values'][$eventId]['title']);
1481 $this->assertEquals('TheRest <> CiviCRM', $getByIdResult['values'][$eventId]['description']);
1482
1483 // Verify "get" handles encoding in search value
1484 $getByTitleResult = civicrm_api('Event', 'Get', array(
1485 'version' => 3,
1486 'title' => 'CiviCRM <> TheRest',
1487 ));
1488 $this->assertAPISuccess($getByTitleResult);
1489 $this->assertEquals('CiviCRM <> TheRest', $getByTitleResult['values'][$eventId]['title']);
1490 $this->assertEquals('TheRest <> CiviCRM', $getByTitleResult['values'][$eventId]['description']);
1491
1492 // Verify that "getSingle" handles decoding
6ead217b 1493 $getSingleResult = $this->callAPISuccess('Event', 'GetSingle', array(
6a488035
TO
1494 'id' => $eventId,
1495 ));
1496
6a488035
TO
1497 $this->assertEquals('CiviCRM <> TheRest', $getSingleResult['title']);
1498 $this->assertEquals('TheRest <> CiviCRM', $getSingleResult['description']);
1499
1500 // Verify that chaining handles decoding
6ead217b 1501 $chainResult = $this->callAPISuccess('Event', 'Get', array(
6a488035 1502 'id' => $eventId,
6c6e6187 1503 'api.event.get' => array(),
6a488035
TO
1504 ));
1505 $this->assertEquals('CiviCRM <> TheRest', $chainResult['values'][$eventId]['title']);
1506 $this->assertEquals('TheRest <> CiviCRM', $chainResult['values'][$eventId]['description']);
1507 $this->assertEquals('CiviCRM <> TheRest', $chainResult['values'][$eventId]['api.event.get']['values'][0]['title']);
1508 $this->assertEquals('TheRest <> CiviCRM', $chainResult['values'][$eventId]['api.event.get']['values'][0]['description']);
1509
1510 // Verify that "setvalue" handles encoding for updates
1511 $setValueTitleResult = civicrm_api('Event', 'setvalue', array(
1512 'version' => 3,
1513 'id' => $eventId,
1514 'field' => 'title',
1515 'value' => 'setValueTitle: CiviCRM <> TheRest',
1516 ));
1517 $this->assertAPISuccess($setValueTitleResult);
1518 $this->assertEquals('setValueTitle: CiviCRM <> TheRest', $setValueTitleResult['values']['title']);
1519 $setValueDescriptionResult = civicrm_api('Event', 'setvalue', array(
1520 'version' => 3,
1521 'id' => $eventId,
1522 'field' => 'description',
1523 'value' => 'setValueDescription: TheRest <> CiviCRM',
1524 ));
bc2bc079 1525 //$this->assertTrue((bool)$setValueDescriptionResult['is_error']); // not supported by setValue
1526 $this->assertEquals('setValueDescription: TheRest <> CiviCRM', $setValueDescriptionResult['values']['description']);
6c6e6187 1527 }
6a488035
TO
1528
1529 /**
1530 * Verify that write operations (create/update) use partial HTML-encoding
1531 *
1532 * In this example, the event 'title' is subject to encoding, but the
1533 * event 'description' is not.
1534 */
1535 public function testEncodeWrite() {
1536 // Create example
1537 $createResult = civicrm_api('Event', 'Create', array(
1538 'version' => 3,
1539 'title' => 'createNew: CiviCRM <> TheRest',
1540 'description' => 'createNew: TheRest <> CiviCRM',
1541 'event_type_id' => 1,
1542 'is_public' => 1,
1543 'start_date' => 20081021,
1544 ));
1545 $this->assertAPISuccess($createResult);
1546 $eventId = $createResult['id'];
1547 $this->assertDBQuery('createNew: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
21dfd5f5 1548 1 => array($eventId, 'Integer'),
6a488035
TO
1549 ));
1550 $this->assertDBQuery('createNew: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
21dfd5f5 1551 1 => array($eventId, 'Integer'),
6a488035
TO
1552 ));
1553
1554 // Verify that "create" handles encoding for updates
1555 $createWithIdResult = civicrm_api('Event', 'Create', array(
1556 'version' => 3,
1557 'id' => $eventId,
1558 'title' => 'createWithId: CiviCRM <> TheRest',
1559 'description' => 'createWithId: TheRest <> CiviCRM',
1560 ));
1561 $this->assertAPISuccess($createWithIdResult);
1562 $this->assertDBQuery('createWithId: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
21dfd5f5 1563 1 => array($eventId, 'Integer'),
6a488035
TO
1564 ));
1565 $this->assertDBQuery('createWithId: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
21dfd5f5 1566 1 => array($eventId, 'Integer'),
6a488035
TO
1567 ));
1568
1569 // Verify that "setvalue" handles encoding for updates
1570 $setValueTitleResult = civicrm_api('Event', 'setvalue', array(
1571 'version' => 3,
1572 'id' => $eventId,
1573 'field' => 'title',
1574 'value' => 'setValueTitle: CiviCRM <> TheRest',
1575 ));
1576 $this->assertAPISuccess($setValueTitleResult);
1577 $this->assertDBQuery('setValueTitle: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
21dfd5f5 1578 1 => array($eventId, 'Integer'),
6a488035
TO
1579 ));
1580 $setValueDescriptionResult = civicrm_api('Event', 'setvalue', array(
1581 'version' => 3,
1582 'id' => $eventId,
1583 'field' => 'description',
1584 'value' => 'setValueDescription: TheRest <> CiviCRM',
1585 ));
bc2bc079 1586 //$this->assertTrue((bool)$setValueDescriptionResult['is_error']); // not supported by setValue
1587 $this->assertAPISuccess($setValueDescriptionResult);
1588 $this->assertDBQuery('setValueDescription: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
21dfd5f5 1589 1 => array($eventId, 'Integer'),
bc2bc079 1590 ));
6a488035 1591 }
96025800 1592
6a488035 1593}