CRM-14181, remove special handing for enums
[civicrm-core.git] / tests / phpunit / api / v3 / SyntaxConformanceTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 require_once 'CiviTest/CiviUnitTestCase.php';
29
30
31 /**
32 * Test APIv3 civicrm_sytanc conformance* functions
33 *
34 * @package CiviCRM_APIv3
35 * @subpackage API_Core
36 */
37
38 class api_v3_SyntaxConformanceTest extends CiviUnitTestCase {
39 protected $_apiversion = 3;
40
41 /**
42 * @var array e.g. $this->deletes['CRM_Contact_DAO_Contact'][] = $contactID;
43 */
44 protected $deletableTestObjects;
45
46 /** This test case doesn't require DB reset */
47 public $DBResetRequired = FALSE;
48
49 /* they are two types of missing APIs:
50 - Those that are to be implemented
51 (in some future version when someone steps in -hint hint-). List the entities in toBeImplemented[ {$action} ]
52 Those that don't exist
53 and that will never exist (eg an obsoleted Entity
54 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)
55 */
56 function setUp() {
57 parent::setUp();
58 $this->enableCiviCampaign();
59 $this->toBeImplemented['get'] = array('Profile', 'CustomValue', 'Constant', 'CustomSearch', 'Extension', 'ReportTemplate', 'System', 'Setting');
60 $this->toBeImplemented['create'] = array('SurveyRespondant', 'OptionGroup', 'MailingRecipients', 'UFMatch', 'LocationType', 'CustomSearch', 'Extension', 'ReportTemplate', 'System');
61 $this->toBeImplemented['delete'] = array('MembershipPayment', 'OptionGroup', 'SurveyRespondant', 'UFJoin', 'UFMatch', 'Extension', 'LocationType', 'System');
62 $this->onlyIDNonZeroCount['get'] = array('ActivityType', 'Entity', 'Domain','Setting');
63 $this->deprecatedAPI = array('Location', 'ActivityType', 'SurveyRespondant');
64 $this->deletableTestObjects = array();
65 }
66
67 function tearDown() {
68 foreach ($this->deletableTestObjects as $entityName => $entities) {
69 foreach ($entities as $entityID) {
70 CRM_Core_DAO::deleteTestObjects($entityName, array('id' => $entityID));
71 }
72 }
73 }
74
75 public static function entities($skip = NULL) {
76 // To only test specific entities, call phpunit with SYNTAX_CONFORMANCE_ENTITIES="TheEntityName"
77 // or uncomment this line:
78 //return array(array ('Tag'), array ('Activity') );
79
80 if (getenv('SYNTAX_CONFORMANCE_ENTITIES')) {
81 $result = array();
82 foreach (explode(' ', getenv('SYNTAX_CONFORMANCE_ENTITIES')) as $entity) {
83 $result[] = array($entity);
84 }
85 return $result;
86 }
87
88 $tmp = civicrm_api('Entity', 'Get', array('version' => 3));
89 if (!is_array($skip)) {
90 $skip = array();
91 }
92 $tmp = array_diff($tmp['values'], $skip);
93 $entities = array();
94 foreach ($tmp as $e) {
95 $entities[] = array($e);
96 }
97 return $entities;
98 }
99
100 public static function entities_get() {
101 // all the entities, beside the ones flagged
102 return static::entities(static::toBeSkipped_get(TRUE));
103 }
104
105 public static function entities_create() {
106 return static::entities(static::toBeSkipped_create(TRUE));
107 }
108
109 public static function entities_updatesingle() {
110 return static::entities(static::toBeSkipped_updatesingle(TRUE));
111 }
112
113 public static function entities_delete() {
114 return static::entities(static::toBeSkipped_delete(TRUE));
115 }
116
117 public static function toBeSkipped_get($sequential = FALSE) {
118 $entitiesWithoutGet = array('MailingEventSubscribe', 'MailingEventConfirm', 'MailingEventResubscribe', 'MailingEventUnsubscribe', 'MailingGroup', 'Location');
119 if ($sequential === TRUE) {
120 return $entitiesWithoutGet;
121 }
122 $entities = array();
123 foreach ($entitiesWithoutGet as $e) {
124 $entities[] = array($e);
125 }
126 return $entities;
127 }
128 /**
129 * Mailing Contact Just doesn't support id. We have always insisted on finding a way to
130 * support id in API but in this case the underlying tables are crying out for a restructue
131 * & it just doesn't make sense
132 * @param unknown_type $sequential
133 * @return multitype:string |multitype:multitype:string
134 */
135 public static function toBeSkipped_getByID($sequential = FALSE) {
136 return array('MailingContact');
137 }
138
139 public static function toBeSkipped_create($sequential = FALSE) {
140 $entitiesWithoutCreate = array('MailingGroup', 'Constant', 'Entity', 'Location', 'Profile', 'MailingRecipients');
141 if ($sequential === TRUE) {
142 return $entitiesWithoutCreate;
143 }
144 $entities = array();
145 foreach ($entitiesWithoutCreate as $e) {
146 $entities[] = array($e);
147 }
148 return $entities;
149 }
150
151 public static function toBeSkipped_delete($sequential = FALSE) {
152 $entitiesWithout = array('MailingGroup', 'Constant', 'Entity', 'Location', 'Domain', 'Profile', 'CustomValue');
153 if ($sequential === TRUE) {
154 return $entitiesWithout;
155 }
156 $entities = array();
157 foreach ($entitiesWithout as $e) {
158 $entities[] = array($e);
159 }
160 return $entities;
161 }
162 /**
163 * Generate list of entities to test for get by id functions
164 * @param boolean $sequential
165 * @return multitype:string |multitype:multitype:string
166 */
167 public static function toBeSkipped_automock($sequential = FALSE) {
168 $entitiesWithoutGet = array('MailingContact', 'EntityTag', 'Participant', 'ParticipantPayment', 'Setting', 'SurveyRespondant', 'MailingRecipients', 'CustomSearch', 'Extension', 'ReportTemplate', 'System');
169 if ($sequential === TRUE) {
170 return $entitiesWithoutGet;
171 }
172 $entities = array();
173 foreach ($entitiesWithoutGet as $e) {
174 $entities[] = array($e);
175 }
176 return $entities;
177 }
178
179
180 /*
181 * At this stage exclude the ones that don't pass & add them as we can troubleshoot them
182 */
183
184 public static function toBeSkipped_updatesingle($sequential = FALSE) {
185 $entitiesWithout = array(
186 'Mailing',
187 'MailingGroup',
188 'MailingJob',
189 'Address',
190 'MailingEventUnsubscribe',
191 'MailingEventSubscribe',
192 'Constant',
193 'Entity',
194 'Location',
195 'Domain',
196 'Profile',
197 'CustomValue',
198 'SurveyRespondant',
199 'Tag',
200 'UFMatch',
201 'UFJoin',
202 'UFField',
203 'OptionValue',
204 'Relationship',
205 'RelationshipType',
206 'ParticipantStatusType',
207 'Note',
208 'OptionGroup',
209 'Membership',
210 'MembershipType',
211 'MembershipStatus',
212 'Group',
213 'GroupOrganization',
214 'GroupNesting',
215 'Job',
216 'File',
217 'EntityTag',
218 'CustomField',
219 'CustomGroup',
220 'Contribution',
221 'ContributionRecur',
222 'ActivityType',
223 'MailingEventConfirm',
224 'Case',
225 'Contact',
226 'ContactType',
227 'MailingEventResubscribe',
228 'UFGroup',
229 'Activity',
230 'Email',
231 'Event',
232 'GroupContact',
233 'MembershipPayment',
234 'Participant',
235 'ParticipantPayment',
236 'LineItem',
237 'PriceSet',
238 'PriceField',
239 'PriceFieldValue',
240 'PledgePayment',
241 'ContributionPage',
242 'Phone',
243 'PaymentProcessor',
244 'MailSettings',
245 'Setting',
246 'MailingContact',
247 );
248 if ($sequential === TRUE) {
249 return $entitiesWithout;
250 }
251 $entities = array();
252 foreach ($entitiesWithout as $e) {
253 $entities[] = array(
254 $e,
255 );
256 }
257 return array('pledge');
258 return $entities;
259 }
260
261 public function getKnownUnworkablesUpdateSingle($entity, $key){
262 // can't update values are values for which updates don't result in the value being changed
263 $knownFailures = array(
264 'ActionSchedule' => array(
265 'cant_update' => array(
266 'group_id',
267 ),
268 ),
269 'Address' => array(
270 'cant_update' => array(
271 'state_province_id', //issues with country id - need to ensure same country
272 'master_id',//creates relationship
273 ),
274 'cant_return' => array(
275 )
276 ),
277 'Batch' => array(
278 'cant_update' => array(
279 'entity_table', // believe this field is defined in error
280 ),
281 'cant_return' => array(
282 'entity_table',
283 )
284 ),
285 'Pledge' => array(
286 'cant_update' => array(
287 'pledge_original_installment_amount',
288 'installments',
289 'original_installment_amount',
290 'next_pay_date',
291 'amount' // can't be changed through API
292 ),
293 'break_return' => array(// if these are passed in they are retrieved from the wrong table
294 'honor_contact_id',
295 'cancel_date',
296 'contribution_page_id',
297 'financial_account_id',
298 'financial_type_id',
299 'currency'
300 ),
301 'cant_return' => array(// can't be retrieved from api
302 'honor_type_id', //due to uniquename missing
303 'end_date',
304 'modified_date',
305 'acknowledge_date',
306 'start_date',
307 'frequency_day',
308 'currency',
309 'max_reminders',
310 'initial_reminder_day',
311 'additional_reminder_day',
312 'frequency_unit',
313 'pledge_contribution_page_id',
314 'pledge_status_id',
315 'pledge_campaign_id',
316 )
317 ),
318 'PaymentProcessorType' => array(
319 'cant_update' => array(
320 'billing_mode',
321 ),
322 'break_return' => array(
323 ),
324 'cant_return' => array(
325 ),
326 ),
327 );
328 if(empty($knownFailures[$entity]) || empty($knownFailures[$entity][$key])){
329 return array();
330 }
331 return $knownFailures[$entity][$key];
332 }
333
334 /** testing the _get **/
335
336 /**
337 * @dataProvider toBeSkipped_get
338 entities that don't need a get action
339 */
340 public function testNotImplemented_get($Entity) {
341 $result = civicrm_api($Entity, 'Get', array('version' => 3));
342 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
343 $this->assertContains("API ($Entity, Get) does not exist", $result['error_message']);
344 }
345
346 /**
347 * @dataProvider entities
348 * @expectedException PHPUnit_Framework_Error
349 */
350 public function testWithoutParam_get($Entity) {
351 // should get php complaining that a param is missing
352 $result = civicrm_api($Entity, 'Get');
353 }
354
355 /**
356 * @dataProvider entities
357 */
358 public function testGetFields($Entity) {
359 if (in_array($Entity, $this->deprecatedAPI) || $Entity == 'Entity' || $Entity == 'CustomValue' || $Entity == 'MailingGroup') {
360 return;
361 }
362
363 $result = civicrm_api($Entity, 'getfields', array('version' => 3));
364 $this->assertTrue(is_array($result['values']), "$Entity ::get fields doesn't return values array in line " . __LINE__);
365 foreach ($result['values'] as $key => $value) {
366 $this->assertTrue(is_array($value), $Entity . "::" . $key . " is not an array in line " . __LINE__);
367 }
368 }
369
370 /**
371 * @dataProvider entities_get
372 */
373 public function testEmptyParam_get($Entity) {
374
375 if (in_array($Entity, $this->toBeImplemented['get'])) {
376 // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented");
377 return;
378 }
379 $result = civicrm_api($Entity, 'Get', array());
380 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
381 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
382 }
383 /**
384 * @dataProvider entities_get
385 */
386 public function testEmptyParam_getString($Entity) {
387
388 if (in_array($Entity, $this->toBeImplemented['get'])) {
389 // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented");
390 return;
391 }
392 $result = $this->callAPIFailure($Entity, 'Get', 'string');
393 $this->assertEquals(2000, $result['error_code']);
394 $this->assertEquals('Input variable `params` is not an array', $result['error_message']);
395 }
396 /**
397 * @dataProvider entities_get
398 * @Xdepends testEmptyParam_get // no need to test the simple if the empty doesn't work/is skipped. doesn't seem to work
399 */
400 public function testSimple_get($Entity) {
401 // $this->markTestSkipped("test gives core error on test server (but not on our locals). Skip until we can get server to pass");
402 if (in_array($Entity, $this->toBeImplemented['get'])) {
403 return;
404 }
405 $result = civicrm_api($Entity, 'Get', array('version' => 3));
406 // @TODO: list the get that have mandatory params
407 if ($result['is_error']) {
408 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
409 // either id or contact_id or entity_id is one of the field missing
410 $this->assertContains("id", $result['error_message']);
411 }
412 else {
413 $this->assertEquals(3, $result['version']);
414 $this->assertArrayHasKey('count', $result);
415 $this->assertArrayHasKey('values', $result);
416 }
417 }
418
419 /**
420 * @dataProvider entities_get
421 */
422 public function testAcceptsOnlyID_get($Entity) {
423 // big random number. fun fact: if you multiply it by pi^e, the result is another random number, but bigger ;)
424 $nonExistantID = 30867307034;
425 if (in_array($Entity, $this->toBeImplemented['get'])
426 || in_array($Entity, $this->toBeSkipped_getByID())
427 ) {
428 return;
429 }
430
431 // FIXME
432 // the below function returns different values and hence an early return
433 // we'll fix this once beta1 is released
434 // return;
435
436 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
437
438 if ($result['is_error']) {
439 // just to get a clearer message in the log
440 $this->assertEquals("only id should be enough", $result['error_message']);
441 }
442 if (!in_array($Entity, $this->onlyIDNonZeroCount['get'])) {
443 $this->assertEquals(0, $result['count']);
444 }
445 }
446
447 /**
448 * Create two entities and make sure we can fetch them individually by ID
449 *
450 * @dataProvider entities_get
451 *
452 * limitations include the problem with avoiding loops when creating test objects -
453 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
454 * Currency - only seems to support US
455 */
456 public function testByID_get($entityName) {
457 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
458 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
459 return;
460 }
461
462 $baos = $this->getMockableBAOObjects($entityName);
463 list($baoObj1, $baoObj2) = $baos;
464
465 // fetch first by ID
466 $result = $this->callAPISuccess($entityName, 'get', array(
467 'id' => $baoObj1->id,
468 ));
469
470 $this->assertTrue(!empty($result['values'][$baoObj1->id]), 'Should find first object by id');
471 $this->assertEquals($baoObj1->id, $result['values'][$baoObj1->id]['id'], 'Should find id on first object');
472 $this->assertEquals(1, count($result['values']));
473
474 // fetch second by ID
475 $result = $this->callAPISuccess($entityName, 'get', array(
476 'id' => $baoObj2->id,
477 ));
478 $this->assertTrue(!empty($result['values'][$baoObj2->id]), 'Should find second object by id');
479 $this->assertEquals($baoObj2->id, $result['values'][$baoObj2->id]['id'], 'Should find id on second object');
480 $this->assertEquals(1, count($result['values']));
481 }
482
483 /**
484 * Create two entities and make sure we can fetch them individually by ID (e.g. using "contact_id=>2"
485 * or "group_id=>4")
486 *
487 * @dataProvider entities_get
488 *
489 * limitations include the problem with avoiding loops when creating test objects -
490 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
491 * Currency - only seems to support US
492 */
493 public function testByIDAlias_get($entityName) {
494 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
495 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
496 return;
497 }
498
499 $baoString = _civicrm_api3_get_DAO($entityName);
500 if (empty($baoString)) {
501 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
502 return;
503 }
504
505 $idFieldName = _civicrm_api_get_entity_name_from_camel($entityName) . '_id';
506
507 // create entities
508 $baoObj1 = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
509 $this->assertTrue(is_integer($baoObj1->id), 'check first id');
510 $this->deletableTestObjects[$baoString][] = $baoObj1->id;
511 $baoObj2 = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
512 $this->assertTrue(is_integer($baoObj2->id), 'check second id');
513 $this->deletableTestObjects[$baoString][] = $baoObj2->id;
514
515 // fetch first by ID
516 $result = civicrm_api($entityName, 'get', array(
517 'version' => 3,
518 $idFieldName => $baoObj1->id,
519 ));
520 $this->assertAPISuccess($result);
521 $this->assertTrue(!empty($result['values'][$baoObj1->id]), 'Should find first object by id');
522 $this->assertEquals($baoObj1->id, $result['values'][$baoObj1->id]['id'], 'Should find id on first object');
523 $this->assertEquals(1, count($result['values']));
524
525 // fetch second by ID
526 $result = civicrm_api($entityName, 'get', array(
527 'version' => 3,
528 $idFieldName => $baoObj2->id,
529 ));
530 $this->assertAPISuccess($result);
531 $this->assertTrue(!empty($result['values'][$baoObj2->id]), 'Should find second object by id');
532 $this->assertEquals($baoObj2->id, $result['values'][$baoObj2->id]['id'], 'Should find id on second object');
533 $this->assertEquals(1, count($result['values']));
534 }
535
536 /**
537 * @dataProvider entities_get
538 */
539 public function testNonExistantID_get($Entity) {
540 // cf testAcceptsOnlyID_get
541 $nonExistantID = 30867307034;
542 if (in_array($Entity, $this->toBeImplemented['get'])) {
543 return;
544 }
545
546 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
547
548 // redundant with testAcceptsOnlyID_get
549 if ($result['is_error']) {
550 return;
551 }
552
553
554 $this->assertArrayHasKey('version', $result);
555 $this->assertEquals(3, $result['version']);
556 if (!in_array($Entity, $this->onlyIDNonZeroCount['get'])) {
557 $this->assertEquals(0, $result['count']);
558 }
559 }
560
561 /** testing the _create **/
562
563 /**
564 * @dataProvider toBeSkipped_create
565 entities that don't need a create action
566 */
567 public function testNotImplemented_create($Entity) {
568 $result = civicrm_api($Entity, 'Create', array('version' => 3));
569 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
570 $this->assertContains("API ($Entity, Create) does not exist", $result['error_message']);
571 }
572
573 /**
574 * @dataProvider entities
575 * @expectedException PHPUnit_Framework_Error
576 */
577 public function testWithoutParam_create($Entity) {
578 // should create php complaining that a param is missing
579 $result = civicrm_api($Entity, 'Create');
580 }
581
582 /**
583 * @dataProvider entities_create
584 */
585 public function testEmptyParam_create($Entity) {
586 $this->markTestIncomplete("fixing this test to test the api functions fails on numberous tests
587 which will either create a completely blank entity (batch, participant status) or
588 have a damn good crack at it (e.g mailing job). Marking this as incomplete beats false success");
589 //
590 return;
591 if (in_array($Entity, $this->toBeImplemented['create'])) {
592 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
593 return;
594 }
595 $result = $this->callAPIFailure($Entity, 'Create', array());
596 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
597 }
598
599 /**
600 * @dataProvider entities_create
601 *
602 * Check that create doesn't work with an invalid
603 */
604 public function testInvalidID_create($Entity) {
605 // turn test off for noew
606 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
607 return;
608 if (in_array($Entity, $this->toBeImplemented['create'])) {
609 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
610 return;
611 }
612 $result = $this->callAPIFailure($Entity, 'Create', array('id' => 999));
613 }
614
615 /**
616 * @dataProvider entities
617 */
618 public function testCreateWrongTypeParamTag_create() {
619 $result = civicrm_api("Tag", 'Create', 'this is not a string');
620 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
621 $this->assertEquals("Input variable `params` is not an array", $result['error_message']);
622 }
623
624 /**
625 * @dataProvider entities_updatesingle
626 *
627 * limitations include the problem with avoiding loops when creating test objects -
628 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
629 * Currency - only seems to support US
630 */
631 public function testCreateSingleValueAlter($entityName) {
632 if (in_array($entityName, $this->toBeImplemented['create'])) {
633 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
634 return;
635 }
636
637 $baoString = _civicrm_api3_get_DAO($entityName);
638 $this->assertNotEmpty($baoString, $entityName);
639 $this->assertNotEmpty($entityName, $entityName);
640 $fieldsget = $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'get'));
641 if($entityName != 'Pledge'){
642 $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'create'));
643 }
644 $fields = $fields['values'];
645 $return = array_keys($fieldsget['values']);
646 $valuesNotToReturn = $this->getKnownUnworkablesUpdateSingle($entityName, 'break_return');
647 // these can't be requested as return values
648 $entityValuesThatDontWork = array_merge(
649 $this->getKnownUnworkablesUpdateSingle($entityName, 'cant_update'),
650 $this->getKnownUnworkablesUpdateSingle($entityName, 'cant_return'),
651 $valuesNotToReturn
652 );
653
654 $return = array_diff($return,$valuesNotToReturn);
655 $baoObj = new CRM_Core_DAO();
656 $baoObj->createTestObject($baoString, array('currency' => 'USD'), 2, 0);
657 $getentities = $this->callAPISuccess($entityName, 'get', array(
658 'sequential' => 1,
659 'return' => $return,
660 'options' => array(
661 'sort' => 'id DESC',
662 'limit' => 2,
663 ),
664 ));
665 // lets use first rather than assume only one exists
666 $entity = $getentities['values'][0];
667 $entity2 = $getentities['values'][1];
668 foreach ($fields as $field => $specs) {
669 $fieldName = $field;
670 if (!empty($specs['uniquename'])) {
671 $fieldName = $specs['uniquename'];
672 }
673 if ($field == 'currency' || $field == 'id' || $field == strtolower($entityName) . '_id'
674 || in_array($field,$entityValuesThatDontWork)) {
675 //@todo id & entity_id are correct but we should fix currency & frequency_day
676 continue;
677 }
678 switch ($specs['type']) {
679 case CRM_Utils_Type::T_DATE:
680 case CRM_Utils_Type::T_TIMESTAMP:
681 $entity[$fieldName] = '2012-05-20';
682 break;
683 //case CRM_Utils_Type::T_DATETIME:
684
685 case 12:
686 $entity[$fieldName] = '2012-05-20 03:05:20';
687 break;
688
689 case CRM_Utils_Type::T_STRING:
690 case CRM_Utils_Type::T_BLOB:
691 case CRM_Utils_Type::T_MEDIUMBLOB:
692 case CRM_Utils_Type::T_TEXT:
693 case CRM_Utils_Type::T_LONGTEXT:
694 case CRM_Utils_Type::T_EMAIL:
695 $entity[$fieldName] = substr('New String',0, CRM_Utils_Array::Value('maxlength',$specs,100));
696 break;
697
698 case CRM_Utils_Type::T_INT:
699 // probably created with a 1
700 $entity[$fieldName] = '6';
701 if (!empty($specs['FKClassName'])) {
702 if($specs['FKClassName'] == $baoString){
703 $entity[$fieldName] = (string) $entity2['id'];
704 }
705 else{
706 $uniqueName = CRM_Utils_Array::value('uniqueName', $specs);
707 $entity[$fieldName] = (string) empty($entity2[$field]) ? CRM_Utils_Array::value($uniqueName, $entity2) : $entity2[$field];
708 //todo - there isn't always something set here - & our checking on unset values is limited
709 if (empty($entity[$field])) {
710 unset($entity[$field]);
711 }
712 }
713 }
714 break;
715
716 case CRM_Utils_Type::T_BOOLEAN:
717 // probably created with a 1
718 $entity[$fieldName] = '0';
719 break;
720
721 case CRM_Utils_Type::T_FLOAT:
722 case CRM_Utils_Type::T_MONEY:
723 $entity[$field] = '222';
724 break;
725
726 case CRM_Utils_Type::T_URL:
727 $entity[$field] = 'warm.beer.com';
728 }
729 if (!empty($specs['pseudoconstant'])) {
730 $options = $this->callAPISuccess($entityName, 'getoptions', array('context' => 'create', 'field' => $field));
731 if (empty($options['values'])) {
732 }
733 $entity[$field] = array_rand($options['values']);
734 }
735 $updateParams = array(
736 'id' => $entity['id'],
737 $field => isset($entity[$field]) ? $entity[$field] : NULL,
738 );
739
740 $update = $this->callAPISuccess($entityName, 'create', $updateParams);
741 $checkParams = array(
742 'id' => $entity['id'],
743 'sequential' => 1,
744 'return' => $return,
745 'options' => array(
746 'sort' => 'id DESC',
747 'limit' => 2,
748 ),
749 );
750
751 $checkEntity = $this->callAPISuccess($entityName, 'getsingle', $checkParams);
752 $this->assertAPIArrayComparison($entity, $checkEntity, array(), "checking if $fieldName was correctly updaetd\n" . print_r(array('update-params' => $updateParams, 'update-result' => $update, 'getsingle-params' => $checkParams, 'getsingle-result' => $checkEntity, 'expected entity' => $entity), TRUE));
753 }
754 $baoObj->deleteTestObjects($baoString);
755 $baoObj->free();
756 }
757
758 /** testing the _getFields **/
759
760 /** testing the _delete **/
761
762 /**
763 * @dataProvider toBeSkipped_delete
764 entities that don't need a delete action
765 */
766 public function testNotImplemented_delete($Entity) {
767 $nonExistantID = 151416349;
768 $result = civicrm_api($Entity, 'Delete', array('version' => 3, 'id' => $nonExistantID));
769 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
770 $this->assertContains("API ($Entity, Delete) does not exist", $result['error_message']);
771 }
772
773 /**
774 * @dataProvider entities
775 * @expectedException PHPUnit_Framework_Error
776 */
777 public function testWithoutParam_delete($Entity) {
778 // should delete php complaining that a param is missing
779 $result = civicrm_api($Entity, 'Delete');
780 }
781
782 /**
783 * @dataProvider entities_delete
784 */
785 public function testEmptyParam_delete($Entity) {
786 if (in_array($Entity, $this->toBeImplemented['delete'])) {
787 // $this->markTestIncomplete("civicrm_api3_{$Entity}_delete to be implemented");
788 return;
789 }
790 $result = civicrm_api($Entity, 'Delete', array());
791 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
792 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
793 }
794 /**
795 * @dataProvider entities_delete
796 */
797 public function testInvalidID_delete($Entity) {
798 // turn test off for now
799 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
800 return;
801 if (in_array($Entity, $this->toBeImplemented['delete'])) {
802 // $this->markTestIncomplete("civicrm_api3_{$Entity}_delete to be implemented");
803 return;
804 }
805 $result = $this->callAPIFailure($Entity, 'Delete', array('id' => 999));
806 }
807 /**
808 * @dataProvider entities
809 */
810 public function testDeleteWrongTypeParamTag_delete() {
811 $result = civicrm_api("Tag", 'Delete', 'this is not a string');
812 $this->assertEquals(1, $result['is_error'], 'In line ' . __LINE__);
813 $this->assertEquals("Input variable `params` is not an array", $result['error_message']);
814 }
815
816 /**
817 * Create two entities and make sure delete action only deletes one!
818 *
819 * @dataProvider entities_delete
820 *
821 * limitations include the problem with avoiding loops when creating test objects -
822 * hence FKs only set by createTestObject when required. e.g parent_id on campaign is not being followed through
823 * Currency - only seems to support US
824 */
825 public function testByID_delete($entityName) {
826 // turn test off for noew
827 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
828 return;
829
830 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
831 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
832 return;
833 }
834 $startCount = $this->callAPISuccess($entityName, 'getcount', array());
835 $createcount = 2;
836 $baos = $this->getMockableBAOObjects($entityName, $createcount);
837 list($baoObj1, $baoObj2) = $baos;
838
839 // make sure exactly 2 exist
840 $result = $this->callAPISuccess($entityName, 'getcount', array(),
841 $createcount + $startCount
842 );
843
844 $this->callAPISuccess($entityName, 'delete', array('id' => $baoObj2->id));
845 //make sure 1 less exists now
846 $result = $this->callAPISuccess($entityName, 'getcount', array(),
847 ($createcount + $startCount) -1
848 );
849
850 //make sure id #1 exists
851 $result = $this->callAPISuccess($entityName, 'getcount', array('id' => $baoObj1->id),
852 1
853 );
854 //make sure id #2 desn't exist
855 $result = $this->callAPISuccess($entityName, 'getcount', array('id' => $baoObj2->id),
856 0
857 );
858 }
859
860 /**
861 * @param entityName
862 */private function getMockableBAOObjects($entityName, $count = 2) {
863 $baoString = _civicrm_api3_get_DAO($entityName);
864 if (empty($baoString)) {
865 $this->markTestIncomplete("Entity [$entityName] cannot be mocked - no known DAO");
866 return;
867 }
868 $baos = array();
869 $i = 0;
870 while($i < $count) {
871 // create entities
872 $baoObj = CRM_Core_DAO::createTestObject($baoString, array('currency' => 'USD'));
873 $this->assertTrue(is_integer($baoObj->id), 'check first id');
874 $this->deletableTestObjects[$baoString][] = $baoObj->id;
875 $baos[] = $baoObj;
876 $i ++;
877 }
878 return $baos;
879 }
880
881
882 /**
883 * Verify that HTML metacharacters provided as inputs appear consistently
884 * as outputs.
885 *
886 * At time of writing, the encoding scheme requires (for example) that an
887 * event title be partially-HTML-escaped before writing to DB. To provide
888 * consistency, the API must perform extra encoding and decoding on some
889 * fields.
890 *
891 * In this example, the event 'title' is subject to encoding, but the
892 * event 'description' is not.
893 */
894 public function testEncodeDecodeConsistency() {
895 // Create example
896 $createResult = civicrm_api('Event', 'Create', array(
897 'version' => 3,
898 'title' => 'CiviCRM <> TheRest',
899 'description' => 'TheRest <> CiviCRM',
900 'event_type_id' => 1,
901 'is_public' => 1,
902 'start_date' => 20081021,
903 ));
904 $this->assertAPISuccess($createResult);
905 $eventId = $createResult['id'];
906 $this->assertEquals('CiviCRM <> TheRest', $createResult['values'][$eventId]['title']);
907 $this->assertEquals('TheRest <> CiviCRM', $createResult['values'][$eventId]['description']);
908
909 // Verify "get" handles decoding in result value
910 $getByIdResult = civicrm_api('Event', 'Get', array(
911 'version' => 3,
912 'id' => $eventId,
913 ));
914 $this->assertAPISuccess($getByIdResult);
915 $this->assertEquals('CiviCRM <> TheRest', $getByIdResult['values'][$eventId]['title']);
916 $this->assertEquals('TheRest <> CiviCRM', $getByIdResult['values'][$eventId]['description']);
917
918 // Verify "get" handles encoding in search value
919 $getByTitleResult = civicrm_api('Event', 'Get', array(
920 'version' => 3,
921 'title' => 'CiviCRM <> TheRest',
922 ));
923 $this->assertAPISuccess($getByTitleResult);
924 $this->assertEquals('CiviCRM <> TheRest', $getByTitleResult['values'][$eventId]['title']);
925 $this->assertEquals('TheRest <> CiviCRM', $getByTitleResult['values'][$eventId]['description']);
926
927 // Verify that "getSingle" handles decoding
928 $getSingleResult = $this->callAPISuccess('Event', 'GetSingle', array(
929 'id' => $eventId,
930 ));
931
932 $this->assertEquals('CiviCRM <> TheRest', $getSingleResult['title']);
933 $this->assertEquals('TheRest <> CiviCRM', $getSingleResult['description']);
934
935 // Verify that chaining handles decoding
936 $chainResult = $this->callAPISuccess('Event', 'Get', array(
937 'id' => $eventId,
938 'api.event.get' => array(
939 ),
940 ));
941 $this->assertEquals('CiviCRM <> TheRest', $chainResult['values'][$eventId]['title']);
942 $this->assertEquals('TheRest <> CiviCRM', $chainResult['values'][$eventId]['description']);
943 $this->assertEquals('CiviCRM <> TheRest', $chainResult['values'][$eventId]['api.event.get']['values'][0]['title']);
944 $this->assertEquals('TheRest <> CiviCRM', $chainResult['values'][$eventId]['api.event.get']['values'][0]['description']);
945
946 // Verify that "setvalue" handles encoding for updates
947 $setValueTitleResult = civicrm_api('Event', 'setvalue', array(
948 'version' => 3,
949 'id' => $eventId,
950 'field' => 'title',
951 'value' => 'setValueTitle: CiviCRM <> TheRest',
952 ));
953 $this->assertAPISuccess($setValueTitleResult);
954 $this->assertEquals('setValueTitle: CiviCRM <> TheRest', $setValueTitleResult['values']['title']);
955 $setValueDescriptionResult = civicrm_api('Event', 'setvalue', array(
956 'version' => 3,
957 'id' => $eventId,
958 'field' => 'description',
959 'value' => 'setValueDescription: TheRest <> CiviCRM',
960 ));
961 $this->assertTrue((bool)$setValueDescriptionResult['is_error']); // not supported by setValue
962 //$this->assertEquals('setValueDescription: TheRest <> CiviCRM', $setValueDescriptionResult['values']['description']);
963 }
964
965 /**
966 * Verify that write operations (create/update) use partial HTML-encoding
967 *
968 * In this example, the event 'title' is subject to encoding, but the
969 * event 'description' is not.
970 */
971 public function testEncodeWrite() {
972 // Create example
973 $createResult = civicrm_api('Event', 'Create', array(
974 'version' => 3,
975 'title' => 'createNew: CiviCRM <> TheRest',
976 'description' => 'createNew: TheRest <> CiviCRM',
977 'event_type_id' => 1,
978 'is_public' => 1,
979 'start_date' => 20081021,
980 ));
981 $this->assertAPISuccess($createResult);
982 $eventId = $createResult['id'];
983 $this->assertDBQuery('createNew: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
984 1 => array($eventId, 'Integer')
985 ));
986 $this->assertDBQuery('createNew: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
987 1 => array($eventId, 'Integer')
988 ));
989
990 // Verify that "create" handles encoding for updates
991 $createWithIdResult = civicrm_api('Event', 'Create', array(
992 'version' => 3,
993 'id' => $eventId,
994 'title' => 'createWithId: CiviCRM <> TheRest',
995 'description' => 'createWithId: TheRest <> CiviCRM',
996 ));
997 $this->assertAPISuccess($createWithIdResult);
998 $this->assertDBQuery('createWithId: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
999 1 => array($eventId, 'Integer')
1000 ));
1001 $this->assertDBQuery('createWithId: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
1002 1 => array($eventId, 'Integer')
1003 ));
1004
1005 // Verify that "setvalue" handles encoding for updates
1006 $setValueTitleResult = civicrm_api('Event', 'setvalue', array(
1007 'version' => 3,
1008 'id' => $eventId,
1009 'field' => 'title',
1010 'value' => 'setValueTitle: CiviCRM <> TheRest',
1011 ));
1012 $this->assertAPISuccess($setValueTitleResult);
1013 $this->assertDBQuery('setValueTitle: CiviCRM &lt;&gt; TheRest', 'SELECT title FROM civicrm_event WHERE id = %1', array(
1014 1 => array($eventId, 'Integer')
1015 ));
1016 $setValueDescriptionResult = civicrm_api('Event', 'setvalue', array(
1017 'version' => 3,
1018 'id' => $eventId,
1019 'field' => 'description',
1020 'value' => 'setValueDescription: TheRest <> CiviCRM',
1021 ));
1022 $this->assertTrue((bool)$setValueDescriptionResult['is_error']); // not supported by setValue
1023 //$this->assertAPISuccess($setValueDescriptionResult);
1024 //$this->assertDBQuery('setValueDescription: TheRest <> CiviCRM', 'SELECT description FROM civicrm_event WHERE id = %1', array(
1025 // 1 => array($eventId, 'Integer')
1026 //));
1027 }
1028
1029 }