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