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