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