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