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