INFRA-132 - Add space before "{"
[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');
6c6e6187 82 $this->onlyIDNonZeroCount['get'] = array('ActivityType', 'Entity', 'Domain', 'Setting');
6a488035 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 185 foreach ($entities as $entityName => $entity ) {
22e263ad 186 if (!in_array($entityName, $invalidEntities)
bf38705f 187 && !in_array($entityName, $entitiesToFix)) {
22e263ad 188 if (!empty(self::$componentMap[$entityName]) && empty($enabledComponents[self::$componentMap[$entityName]])) {
6c6e6187
TO
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 269 if ($sequential === TRUE) {
6c6e6187 270 return $entitiesWithMetadataNotYetFixed;
32dafeec
EM
271 }
272 $entities = array();
273 foreach ($entitiesWithMetadataNotYetFixed as $e) {
274 $entities[] = array($e);
275 }
276 return $entities;
277 }
6c6e6187
TO
278 /**
279 * Generate list of entities to test for get by id functions
280 * @param bool $sequential
281 * @return array Entities to be skipped
282 */
b07a3bf9 283 public static function toBeSkipped_automock($sequential = FALSE) {
6c6e6187 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 /**
6c6e6187
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(
6c6e6187 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(
6c6e6187 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 */
9b873358 405 public function getKnownUnworkablesUpdateSingle($entity, $key) {
6a488035
TO
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
6c6e6187 421 'master_id', //creates relationship
6a488035 422 ),
6c6e6187 423 'cant_return' => array()
6a488035 424 ),
6ead217b
E
425 'Batch' => array(
426 'cant_update' => array(
427 'entity_table', // believe this field is defined in error
428 ),
429 'cant_return' => array(
430 'entity_table',
431 )
432 ),
1753bd71
TO
433 'CaseType' => array(
434 'cant_update' => array(
435 'definition',
436 )
437 ),
95520636
TO
438 'MembershipBlock' => array(
439 'cant_update' => array(
440 // The fake/auto-generated values leave us unable to properly cleanup fake data
441 'entity_type',
442 'entity_id',
443 )
444 ),
6a488035
TO
445 'Pledge' => array(
446 'cant_update' => array(
447 'pledge_original_installment_amount',
448 'installments',
449 'original_installment_amount',
450 'next_pay_date',
451 'amount' // can't be changed through API
452 ),
453 'break_return' => array(// if these are passed in they are retrieved from the wrong table
454 'honor_contact_id',
455 'cancel_date',
456 'contribution_page_id',
457 'financial_account_id',
458 'financial_type_id',
459 'currency'
460 ),
461 'cant_return' => array(// can't be retrieved from api
462 'honor_type_id', //due to uniquename missing
463 'end_date',
464 'modified_date',
465 'acknowledge_date',
466 'start_date',
467 'frequency_day',
468 'currency',
469 'max_reminders',
470 'initial_reminder_day',
471 'additional_reminder_day',
472 'frequency_unit',
473 'pledge_contribution_page_id',
474 'pledge_status_id',
475 'pledge_campaign_id',
b06d9acd 476 'pledge_financial_type_id',
6a488035
TO
477 )
478 ),
479 'PaymentProcessorType' => array(
480 'cant_update' => array(
481 'billing_mode',
482 ),
6c6e6187
TO
483 'break_return' => array(),
484 'cant_return' => array(),
6a488035 485 ),
db232378
EM
486 'PriceFieldValue' => array(
487 'cant_update' => array(
488 'weight', //won't update as there is no 1 in the same price set
489 ),
490 ),
6a488035 491 );
9b873358 492 if (empty($knownFailures[$entity]) || empty($knownFailures[$entity][$key])) {
6a488035
TO
493 return array();
494 }
495 return $knownFailures[$entity][$key];
496 }
497
498 /** testing the _get **/
499
500 /**
501 * @dataProvider toBeSkipped_get
6c6e6187 502 entities that don't need a get action
6a488035
TO
503 */
504 public function testNotImplemented_get($Entity) {
505 $result = civicrm_api($Entity, 'Get', array('version' => 3));
506 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
311873a0 507 // $this->assertContains("API ($Entity, Get) does not exist", $result['error_message']);
6c6e6187 508 $this->assertRegExp('/API (.*) does not exist/', $result['error_message']);
6a488035
TO
509 }
510
511 /**
512 * @dataProvider entities
513 * @expectedException PHPUnit_Framework_Error
514 */
515 public function testWithoutParam_get($Entity) {
516 // should get php complaining that a param is missing
517 $result = civicrm_api($Entity, 'Get');
518 }
519
520 /**
521 * @dataProvider entities
522 */
523 public function testGetFields($Entity) {
bd6658bd 524 if (in_array($Entity, $this->deprecatedAPI) || $Entity == 'Entity' || $Entity == 'CustomValue') {
6a488035
TO
525 return;
526 }
527
528 $result = civicrm_api($Entity, 'getfields', array('version' => 3));
529 $this->assertTrue(is_array($result['values']), "$Entity ::get fields doesn't return values array in line " . __LINE__);
530 foreach ($result['values'] as $key => $value) {
531 $this->assertTrue(is_array($value), $Entity . "::" . $key . " is not an array in line " . __LINE__);
532 }
533 }
534
535 /**
536 * @dataProvider entities_get
537 */
538 public function testEmptyParam_get($Entity) {
539
540 if (in_array($Entity, $this->toBeImplemented['get'])) {
541 // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented");
542 return;
543 }
544 $result = civicrm_api($Entity, 'Get', array());
545 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
546 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
547 }
548 /**
549 * @dataProvider entities_get
550 */
551 public function testEmptyParam_getString($Entity) {
552
553 if (in_array($Entity, $this->toBeImplemented['get'])) {
554 // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented");
555 return;
556 }
d0e1eff2 557 $result = $this->callAPIFailure($Entity, 'Get', 'string');
6a488035
TO
558 $this->assertEquals(2000, $result['error_code']);
559 $this->assertEquals('Input variable `params` is not an array', $result['error_message']);
560 }
561 /**
562 * @dataProvider entities_get
563 * @Xdepends testEmptyParam_get // no need to test the simple if the empty doesn't work/is skipped. doesn't seem to work
564 */
565 public function testSimple_get($Entity) {
566 // $this->markTestSkipped("test gives core error on test server (but not on our locals). Skip until we can get server to pass");
6a488035
TO
567 if (in_array($Entity, $this->toBeImplemented['get'])) {
568 return;
569 }
570 $result = civicrm_api($Entity, 'Get', array('version' => 3));
571 // @TODO: list the get that have mandatory params
572 if ($result['is_error']) {
573 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
574 // either id or contact_id or entity_id is one of the field missing
575 $this->assertContains("id", $result['error_message']);
576 }
577 else {
578 $this->assertEquals(3, $result['version']);
579 $this->assertArrayHasKey('count', $result);
580 $this->assertArrayHasKey('values', $result);
581 }
582 }
583
2fc5f1e7
EM
584 /**
585 * @dataProvider custom_data_entities_get
586 */
587 public function testCustomDataGet($entityName) {
588 $this->createLoggedInUser();// so subsidiary activities are created
589 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, $entityName . 'Test.php');
590 $customFieldName = 'custom_' . $ids['custom_field_id'];
591 $objects = $this->getMockableBAOObjects($entityName, 1);
592 $params = array('id' => $objects[0]->id, 'custom_' . $ids['custom_field_id'] => "custom string");
593 $result = $this->callAPISuccess($entityName, 'create', $params);
594
595 $getParams = array('id' => $result['id'], 'return' => array($customFieldName));
596 $check = $this->callAPISuccess($entityName, 'get', $getParams);
597 $this->assertEquals("custom string", $check['values'][$check['id']][$customFieldName]);
598
599 $this->customFieldDelete($ids['custom_field_id']);
600 $this->customGroupDelete($ids['custom_group_id']);
601 $this->callAPISuccess($entityName, 'delete', array('id' => $result['id']));
602 }
603
6a488035
TO
604 /**
605 * @dataProvider entities_get
606 */
607 public function testAcceptsOnlyID_get($Entity) {
608 // big random number. fun fact: if you multiply it by pi^e, the result is another random number, but bigger ;)
609 $nonExistantID = 30867307034;
b14ce773 610 if (in_array($Entity, $this->toBeImplemented['get'])
611 || in_array($Entity, $this->toBeSkipped_getByID())
612 ) {
6a488035
TO
613 return;
614 }
615
616 // FIXME
617 // the below function returns different values and hence an early return
618 // we'll fix this once beta1 is released
619 // return;
620
c679daca 621 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
6a488035
TO
622
623 if ($result['is_error']) {
624 // just to get a clearer message in the log
625 $this->assertEquals("only id should be enough", $result['error_message']);
626 }
627 if (!in_array($Entity, $this->onlyIDNonZeroCount['get'])) {
628 $this->assertEquals(0, $result['count']);
629 }
630 }
631
632 /**
4a97890c
TO
633 * Create two entities and make sure we can fetch them individually by ID
634 *
635 * @dataProvider entities_get
636 *
637 * limitations include the problem with avoiding loops when creating test objects -
638 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
639 * Currency - only seems to support US
640 */
641 public function testByID_get($entityName) {
b07a3bf9 642 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
4a97890c
TO
643 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
644 return;
645 }
646
18eee50e 647 $baos = $this->getMockableBAOObjects($entityName);
648 list($baoObj1, $baoObj2) = $baos;
4a97890c
TO
649
650 // fetch first by ID
6ead217b 651 $result = $this->callAPISuccess($entityName, 'get', array(
4a97890c
TO
652 'id' => $baoObj1->id,
653 ));
6ead217b 654
4a97890c
TO
655 $this->assertTrue(!empty($result['values'][$baoObj1->id]), 'Should find first object by id');
656 $this->assertEquals($baoObj1->id, $result['values'][$baoObj1->id]['id'], 'Should find id on first object');
657 $this->assertEquals(1, count($result['values']));
658
659 // fetch second by ID
6ead217b 660 $result = $this->callAPISuccess($entityName, 'get', array(
4a97890c
TO
661 'id' => $baoObj2->id,
662 ));
4a97890c
TO
663 $this->assertTrue(!empty($result['values'][$baoObj2->id]), 'Should find second object by id');
664 $this->assertEquals($baoObj2->id, $result['values'][$baoObj2->id]['id'], 'Should find id on second object');
665 $this->assertEquals(1, count($result['values']));
666 }
667
b9af4758
E
668 /**
669 * Ensure that the "get" operation accepts limiting the #result records.
670 *
671 * TODO Consider making a separate entity list ("entities_getlimit")
672 * For the moment, the "entities_updatesingle" list should give a good
673 * sense for which entities support createTestObject
674 *
675 * @dataProvider entities_getlimit
8efea814 676 *
c490a46a 677 * @param string $entityName
8efea814 678 *
b9af4758 679 */
00be9182 680 public function testLimit($entityName) {
b9af4758
E
681 $cases = array(); // each case is array(0 => $inputtedApiOptions, 1 => $expectedResultCount)
682 $cases[] = array(
683 array('options' => array('limit' => NULL)),
ebddc2d9
EM
684 30,
685 'check that a NULL limit returns unlimited',
b9af4758
E
686 );
687 $cases[] = array(
688 array('options' => array('limit' => FALSE)),
ebddc2d9
EM
689 30,
690 'check that a FALSE limit returns unlimited',
b9af4758
E
691 );
692 $cases[] = array(
693 array('options' => array('limit' => 0)),
ebddc2d9
EM
694 30,
695 'check that a 0 limit returns unlimited',
b9af4758
E
696 );
697 $cases[] = array(
698 array('options' => array('limit' => 5)),
699 5,
700 'check that a 5 limit returns 5',
701 );
702 $cases[] = array(
703 array(),
704 25,
705 'check that no limit returns 25',
706 );
707
6252a38c 708 $baoString = _civicrm_api3_get_BAO($entityName);
b9af4758
E
709 if (empty($baoString)) {
710 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
711 return;
712 }
713
714 // make 30 test items -- 30 > 25 (the default limit)
4038f8ec 715 $ids = array();
b9af4758 716 for ($i = 0; $i < 30; $i++) {
8d5544c5 717 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
4038f8ec 718 $ids[] = $baoObj->id;
b9af4758
E
719 }
720
721 // each case is array(0 => $inputtedApiOptions, 1 => $expectedResultCount)
722 foreach ($cases as $case) {
ebddc2d9 723 $this->checkLimitAgainstExpected($entityName, $case[0], $case[1], $case[2]);
a85a667f
EM
724
725 //non preferred / legacy syntax
22e263ad 726 if (isset($case[0]['options']['limit'])) {
ebddc2d9
EM
727 $this->checkLimitAgainstExpected($entityName, array('rowCount' => $case[0]['options']['limit']), $case[1], $case[2]);
728 $this->checkLimitAgainstExpected($entityName, array('option_limit' => $case[0]['options']['limit']), $case[1], $case[2]);
729 $this->checkLimitAgainstExpected($entityName, array('option.limit' => $case[0]['options']['limit']), $case[1], $case[2]);
a85a667f 730 }
b9af4758 731 }
4038f8ec
TO
732 foreach ($ids as $id) {
733 CRM_Core_DAO::deleteTestObjects($baoString, array('id' => $id));
734 }
8d5544c5 735 $baoObj->free();
b9af4758
E
736 }
737
dcf5b21f
EM
738 /**
739 * Ensure that the "get" operation accepts limiting the #result records.
740 *
741 * @dataProvider entities_getSqlOperators
742 *
c490a46a 743 * @param string $entityName
dcf5b21f 744 *
dcf5b21f 745 */
00be9182 746 public function testSqlOperators($entityName) {
dcf5b21f
EM
747 $baoString = _civicrm_api3_get_BAO($entityName);
748 if (empty($baoString)) {
749 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
750 return;
751 }
752 $entities = $this->callAPISuccess($entityName, 'get', array('options' => array('limit' => 0), 'return' => 'id'));
753 $entities = array_keys($entities['values']);
754 $totalEntities = count($entities);
755 if ($totalEntities < 3) {
756 $ids = array();
757 for ($i = 0; $i < 3 - $totalEntities; $i++) {
758 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
759 $ids[] = $baoObj->id;
760 }
761 $totalEntities = 3;
762 }
763 $entities = $this->callAPISuccess($entityName, 'get', array('options' => array('limit' => 0)));
764 $entities = array_keys($entities['values']);
765 $this->assertGreaterThan(2, $totalEntities);
766 $this->callAPISuccess($entityName, 'getsingle', array('id' => array('IN' => array($entities[0]))));
767 $this->callAPISuccessGetCount($entityName, array('id' => array('NOT IN' => array($entities[0]))), $totalEntities - 1);
768 $this->callAPISuccessGetCount($entityName, array('id' => array('>' => $entities[0])), $totalEntities - 1);
769 }
770
ebddc2d9
EM
771 /**
772 * Check that get fetches an appropriate number of results
773 *
e16033b4
TO
774 * @param string $entityName
775 * Name of entity to test.
dcf5b21f 776 * @param array $params
e16033b4 777 * @param int $limit
dcf5b21f 778 * @param string $message
ebddc2d9 779 */
00be9182 780 public function checkLimitAgainstExpected($entityName, $params, $limit, $message) {
ebddc2d9 781 $result = $this->callAPISuccess($entityName, 'get', $params);
22e263ad 782 if ($limit == 30) {
ebddc2d9
EM
783 $this->assertGreaterThanOrEqual($limit, $result['count'], $message);
784 $this->assertGreaterThanOrEqual($limit, $result['count'], $message);
785 }
786 else {
787 $this->assertEquals($limit, $result['count'], $message);
788 $this->assertEquals($limit, count($result['values']), $message);
789 }
790 }
afb0ff51
TO
791 /**
792 * Create two entities and make sure we can fetch them individually by ID (e.g. using "contact_id=>2"
793 * or "group_id=>4")
794 *
795 * @dataProvider entities_get
796 *
797 * limitations include the problem with avoiding loops when creating test objects -
798 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
799 * Currency - only seems to support US
800 */
801 public function testByIDAlias_get($entityName) {
c4de8b59 802 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
afb0ff51
TO
803 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
804 return;
805 }
806
6252a38c 807 $baoString = _civicrm_api3_get_BAO($entityName);
afb0ff51
TO
808 if (empty($baoString)) {
809 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
810 return;
811 }
812
c4de8b59
TO
813 $idFieldName = _civicrm_api_get_entity_name_from_camel($entityName) . '_id';
814
afb0ff51
TO
815 // create entities
816 $baoObj1 = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
817 $this->assertTrue(is_integer($baoObj1->id), 'check first id');
818 $this->deletableTestObjects[$baoString][] = $baoObj1->id;
819 $baoObj2 = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
820 $this->assertTrue(is_integer($baoObj2->id), 'check second id');
821 $this->deletableTestObjects[$baoString][] = $baoObj2->id;
822
823 // fetch first by ID
824 $result = civicrm_api($entityName, 'get', array(
825 'version' => 3,
c4de8b59 826 $idFieldName => $baoObj1->id,
afb0ff51
TO
827 ));
828 $this->assertAPISuccess($result);
829 $this->assertTrue(!empty($result['values'][$baoObj1->id]), 'Should find first object by id');
830 $this->assertEquals($baoObj1->id, $result['values'][$baoObj1->id]['id'], 'Should find id on first object');
831 $this->assertEquals(1, count($result['values']));
832
833 // fetch second by ID
834 $result = civicrm_api($entityName, 'get', array(
835 'version' => 3,
c4de8b59 836 $idFieldName => $baoObj2->id,
afb0ff51
TO
837 ));
838 $this->assertAPISuccess($result);
839 $this->assertTrue(!empty($result['values'][$baoObj2->id]), 'Should find second object by id');
840 $this->assertEquals($baoObj2->id, $result['values'][$baoObj2->id]['id'], 'Should find id on second object');
841 $this->assertEquals(1, count($result['values']));
842 }
843
844 /**
6a488035
TO
845 * @dataProvider entities_get
846 */
847 public function testNonExistantID_get($Entity) {
848 // cf testAcceptsOnlyID_get
849 $nonExistantID = 30867307034;
850 if (in_array($Entity, $this->toBeImplemented['get'])) {
851 return;
852 }
853
854 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
855
856 // redundant with testAcceptsOnlyID_get
857 if ($result['is_error']) {
858 return;
859 }
860
6a488035
TO
861 $this->assertArrayHasKey('version', $result);
862 $this->assertEquals(3, $result['version']);
863 if (!in_array($Entity, $this->onlyIDNonZeroCount['get'])) {
864 $this->assertEquals(0, $result['count']);
865 }
866 }
867
868 /** testing the _create **/
869
870 /**
871 * @dataProvider toBeSkipped_create
6c6e6187 872 entities that don't need a create action
6a488035
TO
873 */
874 public function testNotImplemented_create($Entity) {
875 $result = civicrm_api($Entity, 'Create', array('version' => 3));
876 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
311873a0 877 $this->assertContains(strtolower("API ($Entity, Create) does not exist"), strtolower($result['error_message']));
6a488035
TO
878 }
879
880 /**
881 * @dataProvider entities
882 * @expectedException PHPUnit_Framework_Error
883 */
884 public function testWithoutParam_create($Entity) {
885 // should create php complaining that a param is missing
886 $result = civicrm_api($Entity, 'Create');
887 }
888
889 /**
890 * @dataProvider entities_create
891 */
892 public function testEmptyParam_create($Entity) {
f27f2724 893 $this->markTestIncomplete("fixing this test to test the api functions fails on numberous tests
894 which will either create a completely blank entity (batch, participant status) or
18eee50e 895 have a damn good crack at it (e.g mailing job). Marking this as incomplete beats false success");
f27f2724 896 //
18eee50e 897 return;
6a488035
TO
898 if (in_array($Entity, $this->toBeImplemented['create'])) {
899 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
900 return;
901 }
18eee50e 902 $result = $this->callAPIFailure($Entity, 'Create', array());
6a488035
TO
903 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
904 }
905
18eee50e 906 /**
907 * @dataProvider entities_create
908 *
909 * Check that create doesn't work with an invalid
910 */
911 public function testInvalidID_create($Entity) {
912 // turn test off for noew
6ead217b 913 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
18eee50e 914 return;
915 if (in_array($Entity, $this->toBeImplemented['create'])) {
916 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
917 return;
918 }
919 $result = $this->callAPIFailure($Entity, 'Create', array('id' => 999));
920 }
921
6a488035
TO
922 /**
923 * @dataProvider entities
924 */
925 public function testCreateWrongTypeParamTag_create() {
926 $result = civicrm_api("Tag", 'Create', 'this is not a string');
927 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
928 $this->assertEquals("Input variable `params` is not an array", $result['error_message']);
929 }
930
931 /**
932 * @dataProvider entities_updatesingle
933 *
934 * limitations include the problem with avoiding loops when creating test objects -
935 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
936 * Currency - only seems to support US
937 */
938 public function testCreateSingleValueAlter($entityName) {
939 if (in_array($entityName, $this->toBeImplemented['create'])) {
940 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
941 return;
942 }
943
6252a38c 944 $baoString = _civicrm_api3_get_BAO($entityName);
6a488035
TO
945 $this->assertNotEmpty($baoString, $entityName);
946 $this->assertNotEmpty($entityName, $entityName);
39bc176e 947 $fieldsGet = $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'get'));
9b873358 948 if ($entityName != 'Pledge') {
6ead217b 949 $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'create'));
6a488035
TO
950 }
951 $fields = $fields['values'];
39bc176e 952 $return = array_keys($fieldsGet['values']);
6a488035 953 $valuesNotToReturn = $this->getKnownUnworkablesUpdateSingle($entityName, 'break_return');
6c6e6187 954 // these can't be requested as return values
1fd111c8 955 $entityValuesThatDoNotWork = array_merge(
6a488035
TO
956 $this->getKnownUnworkablesUpdateSingle($entityName, 'cant_update'),
957 $this->getKnownUnworkablesUpdateSingle($entityName, 'cant_return'),
958 $valuesNotToReturn
959 );
960
6c6e6187 961 $return = array_diff($return, $valuesNotToReturn);
6a488035
TO
962 $baoObj = new CRM_Core_DAO();
963 $baoObj->createTestObject($baoString, array('currency' => 'USD'), 2, 0);
39bc176e
EM
964
965 $getEntities = $this->callAPISuccess($entityName, 'get', array(
6ead217b
E
966 'sequential' => 1,
967 'return' => $return,
968 'options' => array(
969 'sort' => 'id DESC',
970 'limit' => 2,
971 ),
972 ));
a8699624 973
6a488035 974 // lets use first rather than assume only one exists
39bc176e
EM
975 $entity = $getEntities['values'][0];
976 $entity2 = $getEntities['values'][1];
977 $this->deletableTestObjects[$baoString][] = $entity['id'];
978 $this->deletableTestObjects[$baoString][] = $entity2['id'];
6a488035 979 foreach ($fields as $field => $specs) {
a8699624 980 $resetFKTo = NULL;
6a488035
TO
981 $fieldName = $field;
982 if (!empty($specs['uniquename'])) {
983 $fieldName = $specs['uniquename'];
984 }
985 if ($field == 'currency' || $field == 'id' || $field == strtolower($entityName) . '_id'
6c6e6187 986 || in_array($field, $entityValuesThatDoNotWork)) {
6a488035
TO
987 //@todo id & entity_id are correct but we should fix currency & frequency_day
988 continue;
989 }
0ce6d639 990 $this->assertArrayHasKey('type', $specs, "the _spec function for $entityName field $field does not specify the type");
6a488035
TO
991 switch ($specs['type']) {
992 case CRM_Utils_Type::T_DATE:
993 case CRM_Utils_Type::T_TIMESTAMP:
994 $entity[$fieldName] = '2012-05-20';
995 break;
6c6e6187 996
6a488035
TO
997 //case CRM_Utils_Type::T_DATETIME:
998
999 case 12:
1000 $entity[$fieldName] = '2012-05-20 03:05:20';
1001 break;
1002
1003 case CRM_Utils_Type::T_STRING:
1004 case CRM_Utils_Type::T_BLOB:
1005 case CRM_Utils_Type::T_MEDIUMBLOB:
1006 case CRM_Utils_Type::T_TEXT:
1007 case CRM_Utils_Type::T_LONGTEXT:
1008 case CRM_Utils_Type::T_EMAIL:
6c6e6187 1009 $entity[$fieldName] = substr('New String', 0, CRM_Utils_Array::Value('maxlength', $specs, 100));
6a488035
TO
1010 break;
1011
1012 case CRM_Utils_Type::T_INT:
1013 // probably created with a 1
a8699624
EM
1014 if ($fieldName == 'weight') {
1015 $entity[$fieldName] = 2;
1016 }
1017 elseif (!empty($specs['FKClassName'])) {
9b873358 1018 if ($specs['FKClassName'] == $baoString) {
6a488035
TO
1019 $entity[$fieldName] = (string) $entity2['id'];
1020 }
1021 else{
6ead217b 1022 $uniqueName = CRM_Utils_Array::value('uniqueName', $specs);
a8699624
EM
1023 if (!empty($entity[$fieldName])) {
1024 $resetFKTo = array($fieldName => $entity[$fieldName]);
1025 }
6ead217b 1026 $entity[$fieldName] = (string) empty($entity2[$field]) ? CRM_Utils_Array::value($uniqueName, $entity2) : $entity2[$field];
6c6e6187 1027 //todo - there isn't always something set here - & our checking on unset values is limited
6a488035
TO
1028 if (empty($entity[$field])) {
1029 unset($entity[$field]);
1030 }
1031 }
1032 }
a8699624
EM
1033 else {
1034 $entity[$fieldName] = '6';
1035 }
6a488035
TO
1036 break;
1037
6a488035
TO
1038 case CRM_Utils_Type::T_BOOLEAN:
1039 // probably created with a 1
1040 $entity[$fieldName] = '0';
1041 break;
1042
1043 case CRM_Utils_Type::T_FLOAT:
1044 case CRM_Utils_Type::T_MONEY:
edd31a24 1045 $entity[$field] = '22.75';
6a488035
TO
1046 break;
1047
1048 case CRM_Utils_Type::T_URL:
1049 $entity[$field] = 'warm.beer.com';
1050 }
4b5ff63c 1051 if (!empty($specs['pseudoconstant'])) {
6ead217b 1052 $options = $this->callAPISuccess($entityName, 'getoptions', array('context' => 'create', 'field' => $field));
3d3ef918 1053 if (empty($options['values'])) {
edd31a24 1054 //eg. pdf_format id doesn't ship with any
22e263ad 1055 if (isset($specs['pseudoconstant']['optionGroupName'])) {
edd31a24
EM
1056 $optionGroupID = $this->callAPISuccess('option_group', 'getvalue', array('name' => 'pdf_format', 'return' => 'id'));
1057 $optionValue = $this->callAPISuccess('option_value', 'create', array('option_group_id' => $optionGroupID, 'label' => 'new option value'));
1058 $options['values'][] = $optionValue['id'];
1059 }
3d3ef918
CW
1060 }
1061 $entity[$field] = array_rand($options['values']);
6a488035
TO
1062 }
1063 $updateParams = array(
6a488035 1064 'id' => $entity['id'],
6ead217b 1065 $field => isset($entity[$field]) ? $entity[$field] : NULL,
6a488035 1066 );
22e263ad 1067 if (isset($updateParams['financial_type_id']) && in_array($entityName, array('Grant'))) {
deb562a8
EM
1068 //api has special handling on these 2 fields for backward compatibility reasons
1069 $entity['contribution_type_id'] = $updateParams['financial_type_id'];
1070 }
6a488035 1071
f27f2724 1072 $update = $this->callAPISuccess($entityName, 'create', $updateParams);
6a488035
TO
1073 $checkParams = array(
1074 'id' => $entity['id'],
6a488035
TO
1075 'sequential' => 1,
1076 'return' => $return,
1077 'options' => array(
1078 'sort' => 'id DESC',
1079 'limit' => 2,
1080 ),
1081 );
1082
f27f2724 1083 $checkEntity = $this->callAPISuccess($entityName, 'getsingle', $checkParams);
b7e3da74 1084 $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
1085 if ($resetFKTo) {
1086 //reset the foreign key fields because otherwise our cleanup routine fails & some other unexpected stuff can kick in
1087 $entity = array_merge($entity, $resetFKTo);
1088 $updateParams = array_merge($updateParams, $resetFKTo);
1089 $this->callAPISuccess($entityName, 'create', $updateParams);
22e263ad 1090 if (isset($updateParams['financial_type_id']) && in_array($entityName, array('Grant'))) {
a8699624
EM
1091 //api has special handling on these 2 fields for backward compatibility reasons
1092 $entity['contribution_type_id'] = $updateParams['financial_type_id'];
1093 }
1094 }
6a488035 1095 }
6a488035
TO
1096 $baoObj->free();
1097 }
1098
1099 /** testing the _getFields **/
1100
1101 /** testing the _delete **/
1102
1103 /**
1104 * @dataProvider toBeSkipped_delete
6c6e6187 1105 entities that don't need a delete action
6a488035
TO
1106 */
1107 public function testNotImplemented_delete($Entity) {
1108 $nonExistantID = 151416349;
1109 $result = civicrm_api($Entity, 'Delete', array('version' => 3, 'id' => $nonExistantID));
1110 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
311873a0 1111 $this->assertContains(strtolower("API ($Entity, Delete) does not exist"), strtolower($result['error_message']));
6a488035
TO
1112 }
1113
1114 /**
1115 * @dataProvider entities
1116 * @expectedException PHPUnit_Framework_Error
1117 */
1118 public function testWithoutParam_delete($Entity) {
1119 // should delete php complaining that a param is missing
1120 $result = civicrm_api($Entity, 'Delete');
1121 }
1122
1123 /**
1124 * @dataProvider entities_delete
1125 */
1126 public function testEmptyParam_delete($Entity) {
1127 if (in_array($Entity, $this->toBeImplemented['delete'])) {
1128 // $this->markTestIncomplete("civicrm_api3_{$Entity}_delete to be implemented");
1129 return;
1130 }
1131 $result = civicrm_api($Entity, 'Delete', array());
1132 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
1133 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
1134 }
18eee50e 1135 /**
1136 * @dataProvider entities_delete
1137 */
1138 public function testInvalidID_delete($Entity) {
6ead217b
E
1139 // turn test off for now
1140 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
18eee50e 1141 return;
1142 if (in_array($Entity, $this->toBeImplemented['delete'])) {
1143 // $this->markTestIncomplete("civicrm_api3_{$Entity}_delete to be implemented");
1144 return;
1145 }
1146 $result = $this->callAPIFailure($Entity, 'Delete', array('id' => 999));
1147 }
6a488035
TO
1148 /**
1149 * @dataProvider entities
1150 */
1151 public function testDeleteWrongTypeParamTag_delete() {
1152 $result = civicrm_api("Tag", 'Delete', 'this is not a string');
1153 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
1154 $this->assertEquals("Input variable `params` is not an array", $result['error_message']);
1155 }
1156
18eee50e 1157 /**
1158 * Create two entities and make sure delete action only deletes one!
1159 *
1160 * @dataProvider entities_delete
1161 *
1162 * limitations include the problem with avoiding loops when creating test objects -
1163 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
1164 * Currency - only seems to support US
1165 */
1166 public function testByID_delete($entityName) {
1167 // turn test off for noew
1168 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
1169 return;
1170
1171 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
1172 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
1173 return;
1174 }
1175 $startCount = $this->callAPISuccess($entityName, 'getcount', array());
1176 $createcount = 2;
1177 $baos = $this->getMockableBAOObjects($entityName, $createcount);
1178 list($baoObj1, $baoObj2) = $baos;
1179
1180 // make sure exactly 2 exist
1181 $result = $this->callAPISuccess($entityName, 'getcount', array(),
1182 $createcount + $startCount
1183 );
1184
1185 $this->callAPISuccess($entityName, 'delete', array('id' => $baoObj2->id));
1186 //make sure 1 less exists now
1187 $result = $this->callAPISuccess($entityName, 'getcount', array(),
6c6e6187 1188 ($createcount + $startCount) - 1
18eee50e 1189 );
1190
1191 //make sure id #1 exists
1192 $result = $this->callAPISuccess($entityName, 'getcount', array('id' => $baoObj1->id),
1193 1
1194 );
1195 //make sure id #2 desn't exist
1196 $result = $this->callAPISuccess($entityName, 'getcount', array('id' => $baoObj2->id),
1197 0
1198 );
1199 }
1200
32dafeec
EM
1201 /**
1202 * Create two entities and make sure delete action only deletes one!
1203 *
1204 * @dataProvider entities_getfields
1205 *
1206 */
1207 public function testGetfieldsHasTitle($entity) {
1208 $entities = $this->getEntitiesSupportingCustomFields();
1209 if (in_array($entity, $entities)) {
1210 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, $entity . 'Test.php');
1211 }
6c6e6187 1212 $actions = $this->callAPISuccess($entity, 'getactions', array());
4c41ecb2 1213 foreach ($actions['values'] as $action) {
7385c761
EM
1214 if (substr($action, -7) == '_create' || substr($action, -4) == '_get' || substr($action, -7) == '_delete') {
1215 //getactions can't distinguish between contribution_page.create & contribution_page.create
1216 continue;
1217 }
4c41ecb2
EM
1218 $fields = $this->callAPISuccess($entity, 'getfields', array('action' => $action));
1219 if (!empty($ids) && in_array($action, array('create', 'get'))) {
7385c761 1220 $this->assertArrayHasKey('custom_' . $ids['custom_field_id'], $fields['values']);
4c41ecb2 1221 }
32dafeec 1222
4c41ecb2
EM
1223 foreach ($fields['values'] as $fieldName => $fieldSpec) {
1224 $this->assertArrayHasKey('title', $fieldSpec, "no title for $entity - $fieldName on action $action");
1225 $this->assertNotEmpty($fieldSpec['title'], "empty title for $entity - $fieldName");
1226 }
32dafeec 1227 }
32dafeec
EM
1228 if (!empty($ids)) {
1229 $this->customFieldDelete($ids['custom_field_id']);
1230 $this->customGroupDelete($ids['custom_group_id']);
1231 }
1232 }
1233
1234 /**
1235 * @return array
1236 */
1237 public function getEntitiesSupportingCustomFields() {
1238 $entities = self::custom_data_entities_get();
1239 $returnEntities = array();
1240 foreach ($entities as $entityArray) {
1241 $returnEntities[] = $entityArray[0];
1242 }
1243 return $returnEntities;
1244 }
8efea814 1245 /**
100fef9d 1246 * @param string $entityName
8efea814
EM
1247 * @param int $count
1248 *
8efea814
EM
1249 *
1250 * @return array
1251 */
1252 private function getMockableBAOObjects($entityName, $count = 2) {
6252a38c 1253 $baoString = _civicrm_api3_get_BAO($entityName);
18eee50e 1254 if (empty($baoString)) {
1255 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
1256 return;
1257 }
1258 $baos = array();
6ead217b 1259 $i = 0;
22e263ad 1260 while ($i < $count) {
6c6e6187 1261 // create entities
18eee50e 1262 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
1263 $this->assertTrue(is_integer($baoObj->id), 'check first id');
1264 $this->deletableTestObjects[$baoString][] = $baoObj->id;
1265 $baos[] = $baoObj;
1266 $i ++;
1267 }
1268 return $baos;
1269 }
1270
1271
6a488035
TO
1272 /**
1273 * Verify that HTML metacharacters provided as inputs appear consistently
1274 * as outputs.
1275 *
1276 * At time of writing, the encoding scheme requires (for example) that an
1277 * event title be partially-HTML-escaped before writing to DB. To provide
1278 * consistency, the API must perform extra encoding and decoding on some
1279 * fields.
1280 *
1281 * In this example, the event 'title' is subject to encoding, but the
1282 * event 'description' is not.
1283 */
1284 public function testEncodeDecodeConsistency() {
1285 // Create example
1286 $createResult = civicrm_api('Event', 'Create', array(
1287 'version' => 3,
1288 'title' => 'CiviCRM <> TheRest',
1289 'description' => 'TheRest <> CiviCRM',
1290 'event_type_id' => 1,
1291 'is_public' => 1,
1292 'start_date' => 20081021,
1293 ));
1294 $this->assertAPISuccess($createResult);
1295 $eventId = $createResult['id'];
1296 $this->assertEquals('CiviCRM <> TheRest', $createResult['values'][$eventId]['title']);
1297 $this->assertEquals('TheRest <> CiviCRM', $createResult['values'][$eventId]['description']);
1298
1299 // Verify "get" handles decoding in result value
1300 $getByIdResult = civicrm_api('Event', 'Get', array(
1301 'version' => 3,
1302 'id' => $eventId,
1303 ));
1304 $this->assertAPISuccess($getByIdResult);
1305 $this->assertEquals('CiviCRM <> TheRest', $getByIdResult['values'][$eventId]['title']);
1306 $this->assertEquals('TheRest <> CiviCRM', $getByIdResult['values'][$eventId]['description']);
1307
1308 // Verify "get" handles encoding in search value
1309 $getByTitleResult = civicrm_api('Event', 'Get', array(
1310 'version' => 3,
1311 'title' => 'CiviCRM <> TheRest',
1312 ));
1313 $this->assertAPISuccess($getByTitleResult);
1314 $this->assertEquals('CiviCRM <> TheRest', $getByTitleResult['values'][$eventId]['title']);
1315 $this->assertEquals('TheRest <> CiviCRM', $getByTitleResult['values'][$eventId]['description']);
1316
1317 // Verify that "getSingle" handles decoding
6ead217b 1318 $getSingleResult = $this->callAPISuccess('Event', 'GetSingle', array(
6a488035
TO
1319 'id' => $eventId,
1320 ));
1321
6a488035
TO
1322 $this->assertEquals('CiviCRM <> TheRest', $getSingleResult['title']);
1323 $this->assertEquals('TheRest <> CiviCRM', $getSingleResult['description']);
1324
1325 // Verify that chaining handles decoding
6ead217b 1326 $chainResult = $this->callAPISuccess('Event', 'Get', array(
6a488035 1327 'id' => $eventId,
6c6e6187 1328 'api.event.get' => array(),
6a488035
TO
1329 ));
1330 $this->assertEquals('CiviCRM <> TheRest', $chainResult['values'][$eventId]['title']);
1331 $this->assertEquals('TheRest <> CiviCRM', $chainResult['values'][$eventId]['description']);
1332 $this->assertEquals('CiviCRM <> TheRest', $chainResult['values'][$eventId]['api.event.get']['values'][0]['title']);
1333 $this->assertEquals('TheRest <> CiviCRM', $chainResult['values'][$eventId]['api.event.get']['values'][0]['description']);
1334
1335 // Verify that "setvalue" handles encoding for updates
1336 $setValueTitleResult = civicrm_api('Event', 'setvalue', array(
1337 'version' => 3,
1338 'id' => $eventId,
1339 'field' => 'title',
1340 'value' => 'setValueTitle: CiviCRM <> TheRest',
1341 ));
1342 $this->assertAPISuccess($setValueTitleResult);
1343 $this->assertEquals('setValueTitle: CiviCRM <> TheRest', $setValueTitleResult['values']['title']);
1344 $setValueDescriptionResult = civicrm_api('Event', 'setvalue', array(
1345 'version' => 3,
1346 'id' => $eventId,
1347 'field' => 'description',
1348 'value' => 'setValueDescription: TheRest <> CiviCRM',
1349 ));
bc2bc079 1350 //$this->assertTrue((bool)$setValueDescriptionResult['is_error']); // not supported by setValue
1351 $this->assertEquals('setValueDescription: TheRest <> CiviCRM', $setValueDescriptionResult['values']['description']);
6c6e6187 1352 }
6a488035
TO
1353
1354 /**
1355 * Verify that write operations (create/update) use partial HTML-encoding
1356 *
1357 * In this example, the event 'title' is subject to encoding, but the
1358 * event 'description' is not.
1359 */
1360 public function testEncodeWrite() {
1361 // Create example
1362 $createResult = civicrm_api('Event', 'Create', array(
1363 'version' => 3,
1364 'title' => 'createNew: CiviCRM <> TheRest',
1365 'description' => 'createNew: TheRest <> CiviCRM',
1366 'event_type_id' => 1,
1367 'is_public' => 1,
1368 'start_date' => 20081021,
1369 ));
1370 $this->assertAPISuccess($createResult);
1371 $eventId = $createResult['id'];
1372 $this->assertDBQuery('createNew: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
1373 1 => array($eventId, 'Integer')
1374 ));
1375 $this->assertDBQuery('createNew: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
1376 1 => array($eventId, 'Integer')
1377 ));
1378
1379 // Verify that "create" handles encoding for updates
1380 $createWithIdResult = civicrm_api('Event', 'Create', array(
1381 'version' => 3,
1382 'id' => $eventId,
1383 'title' => 'createWithId: CiviCRM <> TheRest',
1384 'description' => 'createWithId: TheRest <> CiviCRM',
1385 ));
1386 $this->assertAPISuccess($createWithIdResult);
1387 $this->assertDBQuery('createWithId: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
1388 1 => array($eventId, 'Integer')
1389 ));
1390 $this->assertDBQuery('createWithId: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
1391 1 => array($eventId, 'Integer')
1392 ));
1393
1394 // Verify that "setvalue" handles encoding for updates
1395 $setValueTitleResult = civicrm_api('Event', 'setvalue', array(
1396 'version' => 3,
1397 'id' => $eventId,
1398 'field' => 'title',
1399 'value' => 'setValueTitle: CiviCRM <> TheRest',
1400 ));
1401 $this->assertAPISuccess($setValueTitleResult);
1402 $this->assertDBQuery('setValueTitle: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
1403 1 => array($eventId, 'Integer')
1404 ));
1405 $setValueDescriptionResult = civicrm_api('Event', 'setvalue', array(
1406 'version' => 3,
1407 'id' => $eventId,
1408 'field' => 'description',
1409 'value' => 'setValueDescription: TheRest <> CiviCRM',
1410 ));
bc2bc079 1411 //$this->assertTrue((bool)$setValueDescriptionResult['is_error']); // not supported by setValue
1412 $this->assertAPISuccess($setValueDescriptionResult);
1413 $this->assertDBQuery('setValueDescription: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
1414 1 => array($eventId, 'Integer')
1415 ));
6a488035 1416 }
6a488035 1417}