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