CRM-14181, remove special handing for enums
[civicrm-core.git] / tests / phpunit / api / v3 / SyntaxConformanceTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
232624b1 4 | CiviCRM version 4.4 |
6a488035
TO
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
28require_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
8086a0bf 38class api_v3_SyntaxConformanceTest extends CiviUnitTestCase {
18eee50e 39 protected $_apiversion = 3;
6a488035 40
4a97890c
TO
41 /**
42 * @var array e.g. $this->deletes['CRM_Contact_DAO_Contact'][] = $contactID;
43 */
44 protected $deletableTestObjects;
45
afb0ff51 46 /** This test case doesn't require DB reset */
6a488035 47 public $DBResetRequired = FALSE;
6ead217b 48
6a488035
TO
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)
18eee50e 55 */
56 function setUp() {
6a488035 57 parent::setUp();
6ead217b 58 $this->enableCiviCampaign();
0121533e 59 $this->toBeImplemented['get'] = array('Profile', 'CustomValue', 'Constant', 'CustomSearch', 'Extension', 'ReportTemplate', 'System', 'Setting');
6a488035
TO
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');
4a97890c 64 $this->deletableTestObjects = array();
6a488035
TO
65 }
66
4a97890c
TO
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 }
6a488035 74
6a488035 75 public static function entities($skip = NULL) {
dcf56200
TO
76 // To only test specific entities, call phpunit with SYNTAX_CONFORMANCE_ENTITIES="TheEntityName"
77 // or uncomment this line:
6a488035 78 //return array(array ('Tag'), array ('Activity') );
dcf56200
TO
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
6a488035
TO
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
97715495 102 return static::entities(static::toBeSkipped_get(TRUE));
6a488035
TO
103 }
104
105 public static function entities_create() {
97715495 106 return static::entities(static::toBeSkipped_create(TRUE));
6a488035
TO
107 }
108
109 public static function entities_updatesingle() {
97715495 110 return static::entities(static::toBeSkipped_updatesingle(TRUE));
6a488035
TO
111 }
112
113 public static function entities_delete() {
97715495 114 return static::entities(static::toBeSkipped_delete(TRUE));
6a488035
TO
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 }
b14ce773 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 }
6a488035
TO
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) {
ea28d184 152 $entitiesWithout = array('MailingGroup', 'Constant', 'Entity', 'Location', 'Domain', 'Profile', 'CustomValue');
6a488035
TO
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 }
faacb3e4 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 */
b07a3bf9 167 public static function toBeSkipped_automock($sequential = FALSE) {
dcf56200 168 $entitiesWithoutGet = array('MailingContact', 'EntityTag', 'Participant', 'ParticipantPayment', 'Setting', 'SurveyRespondant', 'MailingRecipients', 'CustomSearch', 'Extension', 'ReportTemplate', 'System');
b07a3bf9
TO
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
6a488035
TO
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',
faacb3e4 243 'PaymentProcessor',
6a488035
TO
244 'MailSettings',
245 'Setting',
b14ce773 246 'MailingContact',
6a488035
TO
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(
03fe1a00
TO
264 'ActionSchedule' => array(
265 'cant_update' => array(
266 'group_id',
267 ),
268 ),
6a488035
TO
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 ),
6ead217b
E
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 ),
6a488035
TO
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__);
6a67518d 343 $this->assertContains("API ($Entity, Get) does not exist", $result['error_message']);
6a488035
TO
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 }
d0e1eff2 392 $result = $this->callAPIFailure($Entity, 'Get', 'string');
6a488035
TO
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");
6a488035
TO
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;
b14ce773 425 if (in_array($Entity, $this->toBeImplemented['get'])
426 || in_array($Entity, $this->toBeSkipped_getByID())
427 ) {
6a488035
TO
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
c679daca 436 $result = civicrm_api($Entity, 'Get', array('version' => 3, 'id' => $nonExistantID));
6a488035
TO
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 /**
4a97890c
TO
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) {
b07a3bf9 457 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
4a97890c
TO
458 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
459 return;
460 }
461
18eee50e 462 $baos = $this->getMockableBAOObjects($entityName);
463 list($baoObj1, $baoObj2) = $baos;
4a97890c
TO
464
465 // fetch first by ID
6ead217b 466 $result = $this->callAPISuccess($entityName, 'get', array(
4a97890c
TO
467 'id' => $baoObj1->id,
468 ));
6ead217b 469
4a97890c
TO
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
6ead217b 475 $result = $this->callAPISuccess($entityName, 'get', array(
4a97890c
TO
476 'id' => $baoObj2->id,
477 ));
4a97890c
TO
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
afb0ff51
TO
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) {
c4de8b59 494 if (in_array($entityName, self::toBeSkipped_automock(TRUE))) {
afb0ff51
TO
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
c4de8b59
TO
505 $idFieldName = _civicrm_api_get_entity_name_from_camel($entityName) . '_id';
506
afb0ff51
TO
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,
c4de8b59 518 $idFieldName => $baoObj1->id,
afb0ff51
TO
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,
c4de8b59 528 $idFieldName => $baoObj2->id,
afb0ff51
TO
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 /**
6a488035
TO
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__);
6a67518d 570 $this->assertContains("API ($Entity, Create) does not exist", $result['error_message']);
6a488035
TO
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) {
f27f2724 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
18eee50e 588 have a damn good crack at it (e.g mailing job). Marking this as incomplete beats false success");
f27f2724 589 //
18eee50e 590 return;
6a488035
TO
591 if (in_array($Entity, $this->toBeImplemented['create'])) {
592 // $this->markTestIncomplete("civicrm_api3_{$Entity}_create to be implemented");
593 return;
594 }
18eee50e 595 $result = $this->callAPIFailure($Entity, 'Create', array());
6a488035
TO
596 $this->assertContains("Mandatory key(s) missing from params array", $result['error_message']);
597 }
598
18eee50e 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
6ead217b 606 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
18eee50e 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
6a488035
TO
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);
6ead217b 640 $fieldsget = $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'get'));
6a488035 641 if($entityName != 'Pledge'){
6ead217b 642 $fields = $this->callAPISuccess($entityName, 'getfields', array('action' => 'create'));
6a488035
TO
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);
6ead217b
E
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 ));
6a488035
TO
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';
a7488080 701 if (!empty($specs['FKClassName'])) {
6a488035
TO
702 if($specs['FKClassName'] == $baoString){
703 $entity[$fieldName] = (string) $entity2['id'];
704 }
705 else{
6ead217b
E
706 $uniqueName = CRM_Utils_Array::value('uniqueName', $specs);
707 $entity[$fieldName] = (string) empty($entity2[$field]) ? CRM_Utils_Array::value($uniqueName, $entity2) : $entity2[$field];
6a488035
TO
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
6a488035
TO
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 }
4b5ff63c 729 if (!empty($specs['pseudoconstant'])) {
6ead217b 730 $options = $this->callAPISuccess($entityName, 'getoptions', array('context' => 'create', 'field' => $field));
3d3ef918 731 if (empty($options['values'])) {
3d3ef918
CW
732 }
733 $entity[$field] = array_rand($options['values']);
6a488035
TO
734 }
735 $updateParams = array(
6a488035 736 'id' => $entity['id'],
6ead217b 737 $field => isset($entity[$field]) ? $entity[$field] : NULL,
6a488035
TO
738 );
739
f27f2724 740 $update = $this->callAPISuccess($entityName, 'create', $updateParams);
6a488035
TO
741 $checkParams = array(
742 'id' => $entity['id'],
6a488035
TO
743 'sequential' => 1,
744 'return' => $return,
745 'options' => array(
746 'sort' => 'id DESC',
747 'limit' => 2,
748 ),
749 );
750
f27f2724 751 $checkEntity = $this->callAPISuccess($entityName, 'getsingle', $checkParams);
dcf56200 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));
6a488035
TO
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__);
6a67518d 770 $this->assertContains("API ($Entity, Delete) does not exist", $result['error_message']);
6a488035
TO
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 }
18eee50e 794 /**
795 * @dataProvider entities_delete
796 */
797 public function testInvalidID_delete($Entity) {
6ead217b
E
798 // turn test off for now
799 $this->markTestIncomplete("Entity [ $Entity ] cannot be mocked - no known DAO");
18eee50e 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 }
6a488035
TO
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
18eee50e 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();
6ead217b 869 $i = 0;
18eee50e 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
6a488035
TO
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
6ead217b 928 $getSingleResult = $this->callAPISuccess('Event', 'GetSingle', array(
6a488035
TO
929 'id' => $eventId,
930 ));
931
6a488035
TO
932 $this->assertEquals('CiviCRM <> TheRest', $getSingleResult['title']);
933 $this->assertEquals('TheRest <> CiviCRM', $getSingleResult['description']);
934
935 // Verify that chaining handles decoding
6ead217b 936 $chainResult = $this->callAPISuccess('Event', 'Get', array(
6a488035
TO
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
6a488035
TO
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}