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