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