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