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