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