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