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