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