[TASK] Re-work/simplify copyright header in PHP files - Part 4
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / DataHandling / DataHandlerTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\DataHandler;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * Test case
19 *
20 * @author Oliver Klee <typo3-coding@oliverklee.de>
21 * @author Tolleiv Nietsch <info@tolleiv.de>
22 */
23 class DataHandlerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
24
25 /**
26 * @var array A backup of registered singleton instances
27 */
28 protected $singletonInstances = array();
29
30 /**
31 * @var \TYPO3\CMS\Core\DataHandling\DataHandler
32 */
33 protected $subject;
34
35 /**
36 * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication a mock logged-in back-end user
37 */
38 protected $backEndUser;
39
40 public function setUp() {
41 $GLOBALS['TCA'] = array();
42 $this->singletonInstances = \TYPO3\CMS\Core\Utility\GeneralUtility::getSingletonInstances();
43 $this->backEndUser = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
44 $this->subject = new \TYPO3\CMS\Core\DataHandling\DataHandler();
45 $this->subject->start(array(), '', $this->backEndUser);
46 }
47
48 public function tearDown() {
49 \TYPO3\CMS\Core\Utility\GeneralUtility::resetSingletonInstances($this->singletonInstances);
50 parent::tearDown();
51 }
52
53 //////////////////////////////////////
54 // Tests for the basic functionality
55 //////////////////////////////////////
56 /**
57 * @test
58 */
59 public function fixtureCanBeCreated() {
60 $this->assertTrue($this->subject instanceof \TYPO3\CMS\Core\DataHandling\DataHandler);
61 }
62
63 //////////////////////////////////////////
64 // Test concerning checkModifyAccessList
65 //////////////////////////////////////////
66 /**
67 * @test
68 */
69 public function adminIsAllowedToModifyNonAdminTable() {
70 $this->subject->admin = TRUE;
71 $this->assertTrue($this->subject->checkModifyAccessList('tt_content'));
72 }
73
74 /**
75 * @test
76 */
77 public function nonAdminIsNorAllowedToModifyNonAdminTable() {
78 $this->subject->admin = FALSE;
79 $this->assertFalse($this->subject->checkModifyAccessList('tt_content'));
80 }
81
82 /**
83 * @test
84 */
85 public function nonAdminWithTableModifyAccessIsAllowedToModifyNonAdminTable() {
86 $this->subject->admin = FALSE;
87 $this->backEndUser->groupData['tables_modify'] = 'tt_content';
88 $this->assertTrue($this->subject->checkModifyAccessList('tt_content'));
89 }
90
91 /**
92 * @test
93 */
94 public function adminIsAllowedToModifyAdminTable() {
95 $this->subject->admin = TRUE;
96 $this->assertTrue($this->subject->checkModifyAccessList('be_users'));
97 }
98
99 /**
100 * @test
101 */
102 public function nonAdminIsNotAllowedToModifyAdminTable() {
103 $this->subject->admin = FALSE;
104 $this->assertFalse($this->subject->checkModifyAccessList('be_users'));
105 }
106
107 /**
108 * @test
109 */
110 public function nonAdminWithTableModifyAccessIsNotAllowedToModifyAdminTable() {
111 $tableName = uniqid('aTable');
112 $GLOBALS['TCA'] = array(
113 $tableName => array(
114 'ctrl' => array(
115 'adminOnly' => TRUE,
116 ),
117 ),
118 );
119 $this->subject->admin = FALSE;
120 $this->backEndUser->groupData['tables_modify'] = $tableName;
121 $this->assertFalse($this->subject->checkModifyAccessList($tableName));
122 }
123
124 /**
125 * @test
126 */
127 public function evalCheckValueDouble2() {
128 $testData = array(
129 '-0,5' => '-0.50',
130 '1000' => '1000.00',
131 '1000,10' => '1000.10',
132 '1000,0' => '1000.00',
133 '600.000.000,00' => '600000000.00',
134 '60aaa00' => '6000.00'
135 );
136 foreach ($testData as $value => $expectedReturnValue) {
137 $returnValue = $this->subject->checkValue_input_Eval($value, array('double2'), '');
138 $this->assertSame($returnValue['value'], $expectedReturnValue);
139 }
140 }
141
142 /**
143 * Data provider for inputValueCheckRecognizesStringValuesAsIntegerValuesCorrectly
144 *
145 * @return array
146 */
147 public function inputValuesStringsDataProvider() {
148 return array(
149 '"0" returns zero as integer' => array(
150 '0',
151 0
152 ),
153 '"-1999999" is interpreted correctly as -1999999 and is lot lower then -200000' => array(
154 '-1999999',
155 -1999999
156 ),
157 '"3000000" is interpreted correctly as 3000000 but is higher then 200000 and set to 200000' => array(
158 '3000000',
159 2000000
160 ),
161 );
162 }
163
164 /**
165 * @test
166 * @dataProvider inputValuesStringsDataProvider
167 */
168 public function inputValueCheckRecognizesStringValuesAsIntegerValuesCorrectly($value, $expectedReturnValue) {
169 $GLOBALS['TYPO3_DB'] = $this->getMock('TYPO3\\CMS\\Core\\Database\\DatabaseConnection', array(), array(), '', FALSE);
170 $tcaFieldConf = array(
171 'input' => array(),
172 'eval' => 'int',
173 'range' => array(
174 'lower' => '-2000000',
175 'upper' => '2000000'
176 )
177 );
178 $returnValue = $this->subject->checkValue_input(array(), $value, $tcaFieldConf, array());
179 $this->assertSame($returnValue['value'], $expectedReturnValue);
180 }
181
182 ///////////////////////////////////////////
183 // Tests concerning checkModifyAccessList
184 ///////////////////////////////////////////
185 //
186 /**
187 * Tests whether a wrong interface on the 'checkModifyAccessList' hook throws an exception.
188 *
189 * @test
190 * @expectedException UnexpectedValueException
191 */
192 public function doesCheckModifyAccessListThrowExceptionOnWrongHookInterface() {
193 $hookClass = uniqid('tx_coretest');
194 eval('class ' . $hookClass . ' {}');
195 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'][] = $hookClass;
196 $this->subject->checkModifyAccessList('tt_content');
197 }
198
199 /**
200 * Tests whether the 'checkModifyAccessList' hook is called correctly.
201 *
202 * @test
203 */
204 public function doesCheckModifyAccessListHookGetsCalled() {
205 $hookClass = uniqid('tx_coretest');
206 $hookMock = $this->getMock('TYPO3\\CMS\\Core\\DataHandling\\DataHandlerCheckModifyAccessListHookInterface', array('checkModifyAccessList'), array(), $hookClass);
207 $hookMock->expects($this->once())->method('checkModifyAccessList');
208 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'][] = $hookClass;
209 $GLOBALS['T3_VAR']['getUserObj'][$hookClass] = $hookMock;
210 $this->subject->checkModifyAccessList('tt_content');
211 }
212
213 /**
214 * Tests whether the 'checkModifyAccessList' hook modifies the $accessAllowed variable.
215 *
216 * @test
217 */
218 public function doesCheckModifyAccessListHookModifyAccessAllowed() {
219 $hookClass = uniqid('tx_coretest');
220 eval('
221 class ' . $hookClass . ' implements \\TYPO3\\CMS\\Core\\DataHandling\\DataHandlerCheckModifyAccessListHookInterface {
222 public function checkModifyAccessList(&$accessAllowed, $table, \\TYPO3\\CMS\\Core\\DataHandling\\DataHandler $parent) { $accessAllowed = TRUE; }
223 }
224 ');
225 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'][] = $hookClass;
226 $this->assertTrue($this->subject->checkModifyAccessList('tt_content'));
227 }
228
229 /////////////////////////////////////
230 // Tests concerning process_datamap
231 /////////////////////////////////////
232 /**
233 * @test
234 */
235 public function processDatamapForFrozenNonZeroWorkspaceReturnsFalse() {
236 $fixture = $this->getMock('TYPO3\\CMS\\Core\\DataHandling\\DataHandler', array('newlog'));
237 $this->backEndUser->workspace = 1;
238 $this->backEndUser->workspaceRec = array('freeze' => TRUE);
239 $fixture->BE_USER = $this->backEndUser;
240 $this->assertFalse($fixture->process_datamap());
241 }
242
243 /**
244 * @test
245 */
246 public function processDatamapWhenEditingRecordInWorkspaceCreatesNewRecordInWorkspace() {
247 // Unset possible hooks on method under test
248 // @TODO: Can be removed if unit test boostrap is fixed to not load LocalConfiguration anymore
249 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] = array();
250
251 $GLOBALS['TYPO3_DB'] = $this->getMock('TYPO3\\CMS\\Core\\Database\\DatabaseConnection');
252
253 $GLOBALS['TCA'] = array(
254 'pages' => array(
255 'columns' => array(),
256 ),
257 );
258
259 /** @var $subject \TYPO3\CMS\Core\DataHandling\DataHandler|\TYPO3\CMS\Core\Tests\UnitTestCase */
260 $subject = $this->getMock(
261 'TYPO3\\CMS\\Core\\DataHandling\\DataHandler',
262 array('newlog', 'checkModifyAccessList', 'tableReadOnly', 'checkRecordUpdateAccess')
263 );
264 $subject->bypassWorkspaceRestrictions = FALSE;
265 $subject->datamap = array(
266 'pages' => array(
267 '1' => array(
268 'header' => 'demo'
269 )
270 )
271 );
272 $subject->expects($this->once())->method('checkModifyAccessList')->with('pages')->will($this->returnValue(TRUE));
273 $subject->expects($this->once())->method('tableReadOnly')->with('pages')->will($this->returnValue(FALSE));
274 $subject->expects($this->once())->method('checkRecordUpdateAccess')->will($this->returnValue(TRUE));
275 $backEndUser = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
276 $backEndUser->workspace = 1;
277 $backEndUser->workspaceRec = array('freeze' => FALSE);
278 $backEndUser->expects($this->once())->method('workspaceAllowAutoCreation')->will($this->returnValue(TRUE));
279 $backEndUser->expects($this->once())->method('workspaceCannotEditRecord')->will($this->returnValue(TRUE));
280 $backEndUser->expects($this->once())->method('recordEditAccessInternals')->with('pages', 1)->will($this->returnValue(TRUE));
281 $subject->BE_USER = $backEndUser;
282 $createdTceMain = $this->getMock('TYPO3\\CMS\\Core\\DataHandling\\DataHandler', array());
283 $createdTceMain->expects($this->once())->method('start')->with(array(), array(
284 'pages' => array(
285 1 => array(
286 'version' => array(
287 'action' => 'new',
288 'treeLevels' => -1,
289 'label' => 'Auto-created for WS #1'
290 )
291 )
292 )
293 ));
294 $createdTceMain->expects($this->never())->method('process_datamap');
295 $createdTceMain->expects($this->once())->method('process_cmdmap');
296 \TYPO3\CMS\Core\Utility\GeneralUtility::addInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler', $createdTceMain);
297 $subject->process_datamap();
298 }
299
300 /**
301 * @test
302 */
303 public function doesCheckFlexFormValueHookGetsCalled() {
304 $GLOBALS['TYPO3_DB'] = $this->getMock('TYPO3\\CMS\\Core\\Database\\DatabaseConnection', array(), array(), '', FALSE);
305 $hookClass = uniqid('tx_coretest');
306 $hookMock = $this->getMock($hookClass, array('checkFlexFormValue_beforeMerge'));
307 $hookMock->expects($this->once())->method('checkFlexFormValue_beforeMerge');
308 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkFlexFormValue'][] = $hookClass;
309 $GLOBALS['T3_VAR']['getUserObj'][$hookClass] = $hookMock;
310 $this->subject->checkValue_flex(array(), array(), array(), array(), array(), '');
311 }
312
313 /////////////////////////////////////
314 // Tests concerning log
315 /////////////////////////////////////
316 /**
317 * @test
318 */
319 public function logCallsWriteLogOfBackendUserIfLoggingIsEnabled() {
320 $backendUser = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
321 $backendUser->expects($this->once())->method('writelog');
322 $this->subject->enableLogging = TRUE;
323 $this->subject->BE_USER = $backendUser;
324 $this->subject->log('', 23, 0, 42, 0, 'details');
325 }
326
327 /**
328 * @test
329 */
330 public function logDoesNotCallWriteLogOfBackendUserIfLoggingIsDisabled() {
331 $backendUser = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
332 $backendUser->expects($this->never())->method('writelog');
333 $this->subject->enableLogging = FALSE;
334 $this->subject->BE_USER = $backendUser;
335 $this->subject->log('', 23, 0, 42, 0, 'details');
336 }
337
338 /**
339 * @test
340 */
341 public function logAddsEntryToLocalErrorLogArray() {
342 $backendUser = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
343 $this->subject->BE_USER = $backendUser;
344 $this->subject->enableLogging = TRUE;
345 $this->subject->errorLog = array();
346 $logDetailsUnique = uniqid('details');
347 $this->subject->log('', 23, 0, 42, 1, $logDetailsUnique);
348 $this->assertStringEndsWith($logDetailsUnique, $this->subject->errorLog[0]);
349 }
350
351 /**
352 * @test
353 */
354 public function logFormatsDetailMessageWithAdditionalDataInLocalErrorArray() {
355 $backendUser = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
356 $this->subject->BE_USER = $backendUser;
357 $this->subject->enableLogging = TRUE;
358 $this->subject->errorLog = array();
359 $logDetails = uniqid('details');
360 $this->subject->log('', 23, 0, 42, 1, '%1s' . $logDetails . '%2s', -1, array('foo', 'bar'));
361 $expected = 'foo' . $logDetails . 'bar';
362 $this->assertStringEndsWith($expected, $this->subject->errorLog[0]);
363 }
364
365 /**
366 * @param boolean $expected
367 * @param string $submittedValue
368 * @param string $storedValue
369 * @param string $storedType
370 * @param boolean $allowNull
371 *
372 * @dataProvider equalSubmittedAndStoredValuesAreDeterminedDataProvider
373 * @test
374 */
375 public function equalSubmittedAndStoredValuesAreDetermined($expected, $submittedValue, $storedValue, $storedType, $allowNull) {
376 $result = $this->callInaccessibleMethod(
377 $this->subject,
378 'isSubmittedValueEqualToStoredValue',
379 $submittedValue, $storedValue, $storedType, $allowNull
380 );
381 $this->assertEquals($expected, $result);
382 }
383
384 /**
385 * @return array
386 */
387 public function equalSubmittedAndStoredValuesAreDeterminedDataProvider() {
388 return array(
389 // String
390 'string value "" vs. ""' => array(
391 TRUE,
392 '', '', 'string', FALSE
393 ),
394 'string value 0 vs. "0"' => array(
395 TRUE,
396 0, '0', 'string', FALSE
397 ),
398 'string value 1 vs. "1"' => array(
399 TRUE,
400 1, '1', 'string', FALSE
401 ),
402 'string value "0" vs. ""' => array(
403 FALSE,
404 '0', '', 'string', FALSE
405 ),
406 'string value 0 vs. ""' => array(
407 FALSE,
408 0, '', 'string', FALSE
409 ),
410 'string value null vs. ""' => array(
411 TRUE,
412 NULL, '', 'string', FALSE
413 ),
414 // Integer
415 'integer value 0 vs. 0' => array(
416 TRUE,
417 0, 0, 'int', FALSE
418 ),
419 'integer value "0" vs. "0"' => array(
420 TRUE,
421 '0', '0', 'int', FALSE
422 ),
423 'integer value 0 vs. "0"' => array(
424 TRUE,
425 0, '0', 'int', FALSE
426 ),
427 'integer value "" vs. "0"' => array(
428 TRUE,
429 '', '0', 'int', FALSE
430 ),
431 'integer value "" vs. 0' => array(
432 TRUE,
433 '', 0, 'int', FALSE
434 ),
435 'integer value "0" vs. 0' => array(
436 TRUE,
437 '0', 0, 'int', FALSE
438 ),
439 'integer value 1 vs. 1' => array(
440 TRUE,
441 1, 1, 'int', FALSE
442 ),
443 'integer value 1 vs. "1"' => array(
444 TRUE,
445 1, '1', 'int', FALSE
446 ),
447 'integer value "1" vs. "1"' => array(
448 TRUE,
449 '1', '1', 'int', FALSE
450 ),
451 'integer value "1" vs. 1' => array(
452 TRUE,
453 '1', 1, 'int', FALSE
454 ),
455 'integer value "0" vs. "1"' => array(
456 FALSE,
457 '0', '1', 'int', FALSE
458 ),
459 // String with allowed NULL values
460 'string with allowed null value "" vs. ""' => array(
461 TRUE,
462 '', '', 'string', TRUE
463 ),
464 'string with allowed null value 0 vs. "0"' => array(
465 TRUE,
466 0, '0', 'string', TRUE
467 ),
468 'string with allowed null value 1 vs. "1"' => array(
469 TRUE,
470 1, '1', 'string', TRUE
471 ),
472 'string with allowed null value "0" vs. ""' => array(
473 FALSE,
474 '0', '', 'string', TRUE
475 ),
476 'string with allowed null value 0 vs. ""' => array(
477 FALSE,
478 0, '', 'string', TRUE
479 ),
480 'string with allowed null value null vs. ""' => array(
481 FALSE,
482 NULL, '', 'string', TRUE
483 ),
484 'string with allowed null value "" vs. null' => array(
485 FALSE,
486 '', NULL, 'string', TRUE
487 ),
488 'string with allowed null value null vs. null' => array(
489 TRUE,
490 NULL, NULL, 'string', TRUE
491 ),
492 // Integer with allowed NULL values
493 'integer with allowed null value 0 vs. 0' => array(
494 TRUE,
495 0, 0, 'int', TRUE
496 ),
497 'integer with allowed null value "0" vs. "0"' => array(
498 TRUE,
499 '0', '0', 'int', TRUE
500 ),
501 'integer with allowed null value 0 vs. "0"' => array(
502 TRUE,
503 0, '0', 'int', TRUE
504 ),
505 'integer with allowed null value "" vs. "0"' => array(
506 TRUE,
507 '', '0', 'int', TRUE
508 ),
509 'integer with allowed null value "" vs. 0' => array(
510 TRUE,
511 '', 0, 'int', TRUE
512 ),
513 'integer with allowed null value "0" vs. 0' => array(
514 TRUE,
515 '0', 0, 'int', TRUE
516 ),
517 'integer with allowed null value 1 vs. 1' => array(
518 TRUE,
519 1, 1, 'int', TRUE
520 ),
521 'integer with allowed null value "1" vs. "1"' => array(
522 TRUE,
523 '1', '1', 'int', TRUE
524 ),
525 'integer with allowed null value "1" vs. 1' => array(
526 TRUE,
527 '1', 1, 'int', TRUE
528 ),
529 'integer with allowed null value 1 vs. "1"' => array(
530 TRUE,
531 1, '1', 'int', TRUE
532 ),
533 'integer with allowed null value "0" vs. "1"' => array(
534 FALSE,
535 '0', '1', 'int', TRUE
536 ),
537 'integer with allowed null value null vs. ""' => array(
538 FALSE,
539 NULL, '', 'int', TRUE
540 ),
541 'integer with allowed null value "" vs. null' => array(
542 FALSE,
543 '', NULL, 'int', TRUE
544 ),
545 'integer with allowed null value null vs. null' => array(
546 TRUE,
547 NULL, NULL, 'int', TRUE
548 ),
549 'integer with allowed null value null vs. "0"' => array(
550 FALSE,
551 NULL, '0', 'int', TRUE
552 ),
553 'integer with allowed null value null vs. 0' => array(
554 FALSE,
555 NULL, 0, 'int', TRUE
556 ),
557 'integer with allowed null value "0" vs. null' => array(
558 FALSE,
559 '0', NULL, 'int', TRUE
560 ),
561 );
562 }
563
564 /**
565 * @test
566 */
567 public function deleteRecord_procBasedOnFieldTypeRespectsEnableCascadingDelete() {
568 $table = uniqid('foo_');
569 $conf = array(
570 'type' => 'inline',
571 'foreign_table' => uniqid('foreign_foo_'),
572 'behaviour' => array(
573 'enableCascadingDelete' => 0,
574 )
575 );
576
577 /** @var \TYPO3\CMS\Core\Database\RelationHandler $mockRelationHandler */
578 $mockRelationHandler = $this->getMock('TYPO3\\CMS\\Core\\Database\\RelationHandler', array(), array(), '', FALSE);
579 $mockRelationHandler->itemArray = array(
580 '1' => array('table' => uniqid('bar_'), 'id' => 67)
581 );
582
583 /** @var \TYPO3\CMS\Core\DataHandling\DataHandler $mockDataHandler */
584 $mockDataHandler = $this->getAccessibleMock('TYPO3\\CMS\\Core\\DataHandling\\DataHandler', array('getInlineFieldType', 'deleteAction', 'createRelationHandlerInstance'), array(), '', FALSE);
585 $mockDataHandler->expects($this->once())->method('getInlineFieldType')->will($this->returnValue('field'));
586 $mockDataHandler->expects($this->once())->method('createRelationHandlerInstance')->will($this->returnValue($mockRelationHandler));
587 $mockDataHandler->expects($this->never())->method('deleteAction');
588 $mockDataHandler->deleteRecord_procBasedOnFieldType($table, 42, 'foo', 'bar', $conf);
589 }
590
591 /**
592 * @return array
593 */
594 public function checkValue_checkReturnsExpectedValuesDataProvider() {
595 return array(
596 'None item selected' => array(
597 0,
598 0
599 ),
600 'All items selected' => array(
601 7,
602 7
603 ),
604 'Item 1 and 2 are selected' => array(
605 3,
606 3
607 ),
608 'Value is higher than allowed' => array(
609 15,
610 7
611 ),
612 'Negative value' => array(
613 -5,
614 0
615 )
616 );
617 }
618
619 /**
620 * @param string $value
621 * @param string $expectedValue
622 *
623 * @dataProvider checkValue_checkReturnsExpectedValuesDataProvider
624 * @test
625 */
626 public function checkValue_checkReturnsExpectedValues($value, $expectedValue) {
627 $expectedResult = array(
628 'value' => $expectedValue
629 );
630 $result = array();
631 $tcaFieldConfiguration = array(
632 'items' => array(
633 array('Item 1', 0),
634 array('Item 2', 0),
635 array('Item 3', 0)
636 )
637 );
638 $this->assertSame($expectedResult, $this->subject->checkValue_check($result, $value, $tcaFieldConfiguration, array()));
639 }
640 }