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