[BUGFIX] Add missing namespace parts
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / Database / DatabaseConnectionTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Functional\Database;
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 use TYPO3\CMS\Core\Database\DatabaseConnection;
17 use TYPO3\Components\TestingFramework\Core\AccessibleObjectInterface;
18
19 /**
20 * Test case for \TYPO3\CMS\Core\Database\DatabaseConnection
21 */
22 class DatabaseConnectionTest extends \TYPO3\Components\TestingFramework\Core\Functional\FunctionalTestCase
23 {
24 /**
25 * @var DatabaseConnection
26 */
27 protected $subject = null;
28
29 /**
30 * @var string
31 */
32 protected $testTable = 'test_database_connection';
33
34 /**
35 * @var string
36 */
37 protected $testField = 'test_field';
38
39 /**
40 * @var string
41 */
42 protected $anotherTestField = 'another_test_field';
43
44 /**
45 * Set the test up
46 *
47 * @return void
48 */
49 protected function setUp()
50 {
51 parent::setUp();
52 $this->subject = $GLOBALS['TYPO3_DB'];
53 $this->subject->sql_query(
54 "CREATE TABLE {$this->testTable} (" .
55 ' id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,' .
56 " {$this->testField} MEDIUMBLOB," .
57 " {$this->anotherTestField} MEDIUMBLOB," .
58 ' PRIMARY KEY (id)' .
59 ') ENGINE=MyISAM DEFAULT CHARSET=utf8;'
60 );
61 }
62
63 /**
64 * Tear the test down
65 *
66 * @return void
67 */
68 protected function tearDown()
69 {
70 $this->subject->sql_query("DROP TABLE {$this->testTable};");
71 unset($this->subject);
72 }
73
74 /**
75 * @test
76 *
77 * @return void
78 */
79 public function selectDbReturnsTrue()
80 {
81 $this->assertTrue($this->subject->sql_select_db());
82 }
83
84 /**
85 * @test
86 * @return void
87 */
88 public function selectDbReturnsFalse()
89 {
90 $this->expectException(\RuntimeException::class);
91 $this->expectExceptionCode(1270853883);
92 $this->expectExceptionMessage('TYPO3 Fatal Error: Cannot connect to the current database, "Foo"!');
93
94 $this->subject->setDatabaseName('Foo');
95 $this->assertFalse($this->subject->sql_select_db());
96 }
97
98 /**
99 * @test
100 *
101 * @return void
102 */
103 public function sqlAffectedRowsReturnsCorrectAmountOfRows()
104 {
105 $this->subject->exec_INSERTquery($this->testTable, [$this->testField => 'test']);
106 $this->assertEquals(1, $this->subject->sql_affected_rows());
107 }
108
109 /**
110 * @test
111 *
112 * @return void
113 */
114 public function sqlInsertIdReturnsCorrectId()
115 {
116 $this->subject->exec_INSERTquery($this->testTable, [$this->testField => 'test']);
117 $this->assertEquals(1, $this->subject->sql_insert_id());
118 }
119
120 /**
121 * @test
122 *
123 * @return void
124 */
125 public function noSqlError()
126 {
127 $this->subject->exec_INSERTquery($this->testTable, [$this->testField => 'test']);
128 $this->assertEquals('', $this->subject->sql_error());
129 }
130
131 /**
132 * @test
133 *
134 * @return void
135 */
136 public function sqlErrorWhenInsertIntoInexistentField()
137 {
138 $this->subject->exec_INSERTquery($this->testTable, ['test' => 'test']);
139 $this->assertEquals('Unknown column \'test\' in \'field list\'', $this->subject->sql_error());
140 }
141
142 /**
143 * @test
144 *
145 * @return void
146 */
147 public function noSqlErrorCode()
148 {
149 $this->subject->exec_INSERTquery($this->testTable, [$this->testField => 'test']);
150 $this->assertEquals(0, $this->subject->sql_errno());
151 }
152
153 /**
154 * @test
155 *
156 * @return void
157 */
158 public function sqlErrorNoWhenInsertIntoInexistentField()
159 {
160 $this->subject->exec_INSERTquery($this->testTable, ['test' => 'test']);
161 $this->assertEquals(1054, $this->subject->sql_errno());
162 }
163
164 /**
165 * @test
166 */
167 public function sqlPconnectReturnsInstanceOfMySqli()
168 {
169 $this->assertInstanceOf('mysqli', $this->subject->sql_pconnect());
170 }
171
172 /**
173 * @test
174 */
175 public function connectDbThrowsExeptionsWhenNoDatabaseIsGiven()
176 {
177 $this->expectException(\RuntimeException::class);
178 $this->expectExceptionCode(1270853882);
179
180 /** @var DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface $subject */
181 $subject = $this->getAccessibleMock(DatabaseConnection::class, ['dummy'], [], '', false);
182 $subject->connectDB();
183 }
184
185 /**
186 * @test
187 *
188 * @return void
189 */
190 public function connectDbConnectsToDatabaseWithoutErrors()
191 {
192 $this->subject->connectDB();
193 $this->assertTrue($this->subject->isConnected());
194 }
195
196 /**
197 * @test
198 *
199 * @return void
200 */
201 public function disconnectIfConnectedDisconnects()
202 {
203 $this->assertTrue($this->subject->isConnected());
204 $this->subject->setDatabaseHost('127.0.0.1');
205 $this->assertFalse($this->subject->isConnected());
206 }
207
208 /**
209 * Data Provider for fullQuoteStrReturnsQuotedString()
210 *
211 * @see fullQuoteStrReturnsQuotedString()
212 *
213 * @return array
214 */
215 public function fullQuoteStrReturnsQuotedStringDataProvider()
216 {
217 return [
218 'NULL string with ReturnNull is allowed' => [
219 [null, true],
220 'NULL',
221 ],
222 'NULL string with ReturnNull is false' => [
223 [null, false],
224 "''",
225 ],
226 'Normal string' => [
227 ['Foo', false],
228 "'Foo'",
229 ],
230 'Single quoted string' => [
231 ["'Hello'", false],
232 "'\\'Hello\\''",
233 ],
234 'Double quoted string' => [
235 ['"Hello"', false],
236 "'\\\"Hello\\\"'",
237 ],
238 'String with internal single tick' => [
239 ['It\'s me', false],
240 "'It\\'s me'",
241 ],
242 'Slashes' => [
243 ['/var/log/syslog.log', false],
244 "'/var/log/syslog.log'",
245 ],
246 'Backslashes' => [
247 ['\\var\\log\\syslog.log', false],
248 "'\\\\var\\\\log\\\\syslog.log'",
249 ],
250 ];
251 }
252
253 /**
254 * @test
255 * @dataProvider fullQuoteStrReturnsQuotedStringDataProvider
256 *
257 * @param string $values
258 * @param string $expectedResult
259 *
260 * @return void
261 */
262 public function fullQuoteStrReturnsQuotedString($values, $expectedResult)
263 {
264 /** @var DatabaseConnection $subject */
265 $quotedStr = $this->subject->fullQuoteStr($values[0], 'tt_content', $values[1]);
266 $this->assertEquals($expectedResult, $quotedStr);
267 }
268
269 /**
270 * Data Provider for fullQuoteArrayQuotesArray()
271 *
272 * @see fullQuoteArrayQuotesArray()
273 *
274 * @return array
275 */
276 public function fullQuoteArrayQuotesArrayDataProvider()
277 {
278 return [
279 'NULL array with ReturnNull is allowed' => [
280 [
281 [null, null],
282 false,
283 true,
284 ],
285 ['NULL', 'NULL'],
286 ],
287
288 'NULL array with ReturnNull is false' => [
289 [
290 [null, null],
291 false,
292 false,
293 ],
294 ["''", "''"],
295 ],
296
297 'Strings in array' => [
298 [
299 ['Foo', 'Bar'],
300 false,
301 false,
302 ],
303 ["'Foo'", "'Bar'"],
304 ],
305
306 'Single quotes in array' => [
307 [
308 ["'Hello'"],
309 false,
310 false,
311 ],
312 ["'\\'Hello\\''"],
313 ],
314
315 'Double quotes in array' => [
316 [
317 ['"Hello"'],
318 false,
319 false,
320 ],
321 ["'\\\"Hello\\\"'"],
322 ],
323
324 'Slashes in array' => [
325 [
326 ['/var/log/syslog.log'],
327 false,
328 false,
329 ],
330 ["'/var/log/syslog.log'"],
331 ],
332
333 'Backslashes in array' => [
334 [
335 ['\var\log\syslog.log'],
336 false,
337 false,
338 ],
339 ["'\\\\var\\\\log\\\\syslog.log'"],
340 ],
341
342 'Strings with internal single tick' => [
343 [
344 ['Hey!', 'It\'s me'],
345 false,
346 false,
347 ],
348 ["'Hey!'", "'It\\'s me'"],
349 ],
350
351 'no quotes strings from array' => [
352 [
353 [
354 'First' => 'Hey!',
355 'Second' => 'It\'s me',
356 'Third' => 'O\' Reily',
357 ],
358 ['First', 'Third'],
359 false,
360 ],
361 ['First' => 'Hey!', 'Second' => "'It\\'s me'", 'Third' => "O' Reily"],
362 ],
363
364 'no quotes strings from string' => [
365 [
366 [
367 'First' => 'Hey!',
368 'Second' => 'It\'s me',
369 'Third' => 'O\' Reily',
370 ],
371 'First,Third',
372 false,
373 ],
374 ['First' => 'Hey!', 'Second' => "'It\\'s me'", 'Third' => "O' Reily"],
375 ],
376 ];
377 }
378
379 /**
380 * @test
381 * @dataProvider fullQuoteArrayQuotesArrayDataProvider
382 *
383 * @param string $values
384 * @param string $expectedResult
385 *
386 * @return void
387 */
388 public function fullQuoteArrayQuotesArray($values, $expectedResult)
389 {
390 $quotedResult = $this->subject->fullQuoteArray($values[0], $this->testTable, $values[1], $values[2]);
391 $this->assertSame($expectedResult, $quotedResult);
392 }
393
394 /**
395 * Data Provider for quoteStrQuotesDoubleQuotesCorrectly()
396 *
397 * @see quoteStrQuotesDoubleQuotesCorrectly()
398 *
399 * @return array
400 */
401 public function quoteStrQuotesCorrectlyDataProvider()
402 {
403 return [
404 'Double Quotes' => [
405 '"Hello"',
406 '\\"Hello\\"'
407 ],
408 'Single Quotes' => [
409 '\'Hello\'',
410 "\\'Hello\\'"
411 ],
412 'Slashes' => [
413 '/var/log/syslog.log',
414 '/var/log/syslog.log'
415 ],
416 'Literal Backslashes' => [
417 '\\var\\log\\syslog.log',
418 '\\\\var\\\\log\\\\syslog.log'
419 ],
420 'Fallback Literal Backslashes' => [
421 '\var\log\syslog.log',
422 '\\\\var\\\\log\\\\syslog.log'
423 ],
424 ];
425 }
426
427 /**
428 * @test
429 * @dataProvider quoteStrQuotesCorrectlyDataProvider
430 *
431 * @param string $string String to quote
432 * @param string $expectedResult Quoted string we expect
433 *
434 * @return void
435 */
436 public function quoteStrQuotesDoubleQuotesCorrectly($string, $expectedResult)
437 {
438 $quotedString = $this->subject->quoteStr($string, $this->testTable);
439 $this->assertSame($expectedResult, $quotedString);
440 }
441
442 /**
443 * @test
444 *
445 * @return void
446 */
447 public function adminQueryReturnsTrueForInsertQuery()
448 {
449 $this->assertTrue(
450 $this->subject->admin_query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('foo')")
451 );
452 }
453
454 /**
455 * @test
456 *
457 * @return void
458 */
459 public function adminQueryReturnsTrueForUpdateQuery()
460 {
461 $this->assertTrue(
462 $this->subject->admin_query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('foo')")
463 );
464 $id = $this->subject->sql_insert_id();
465 $this->assertTrue(
466 $this->subject->admin_query("UPDATE {$this->testTable} SET {$this->testField}='bar' WHERE id={$id}")
467 );
468 }
469
470 /**
471 * @test
472 *
473 * @return void
474 */
475 public function adminQueryReturnsTrueForDeleteQuery()
476 {
477 $this->assertTrue(
478 $this->subject->admin_query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('foo')")
479 );
480 $id = $this->subject->sql_insert_id();
481 $this->assertTrue($this->subject->admin_query("DELETE FROM {$this->testTable} WHERE id={$id}"));
482 }
483
484 /**
485 * @test
486 *
487 * @return void
488 */
489 public function adminQueryReturnsResultForSelectQuery()
490 {
491 $this->assertTrue(
492 $this->subject->admin_query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('foo')")
493 );
494 $res = $this->subject->admin_query("SELECT {$this->testField} FROM {$this->testTable}");
495 $this->assertInstanceOf('mysqli_result', $res);
496 $result = $res->fetch_assoc();
497 $this->assertEquals('foo', $result[$this->testField]);
498 }
499
500 /**
501 * @test
502 *
503 * @return void
504 */
505 public function adminGetCharsetsReturnsArrayWithCharsets()
506 {
507 $columnsRes = $this->subject->admin_query('SHOW CHARACTER SET');
508 $result = $this->subject->admin_get_charsets();
509 $this->assertEquals(count($result), $columnsRes->num_rows);
510
511 /** @var array $row */
512 while (($row = $columnsRes->fetch_assoc())) {
513 $this->assertArrayHasKey($row['Charset'], $result);
514 }
515 }
516
517 /**
518 * @test
519 *
520 * @return void
521 */
522 public function adminGetKeysReturnIndexKeysOfTable()
523 {
524 $result = $this->subject->admin_get_keys($this->testTable);
525 $this->assertEquals('id', $result[0]['Column_name']);
526 }
527
528 /**
529 * @test
530 *
531 * @return void
532 */
533 public function adminGetFieldsReturnFieldInformationsForTable()
534 {
535 $result = $this->subject->admin_get_fields($this->testTable);
536 $this->assertArrayHasKey('id', $result);
537 $this->assertArrayHasKey($this->testField, $result);
538 }
539
540 /**
541 * @test
542 *
543 * @return void
544 */
545 public function adminGetTablesReturnAllTablesFromDatabase()
546 {
547 $result = $this->subject->admin_get_tables();
548 $this->assertArrayHasKey('tt_content', $result);
549 $this->assertArrayHasKey('pages', $result);
550 }
551
552 /**
553 * @test
554 *
555 * @return void
556 */
557 public function adminGetDbsReturnsAllDatabases()
558 {
559 /** @noinspection SqlResolve */
560 $databases = $this->subject->admin_query('SELECT SCHEMA_NAME FROM information_schema.SCHEMATA');
561 $result = $this->subject->admin_get_dbs();
562 $this->assertSame(count($result), $databases->num_rows);
563
564 $i = 0;
565 while ($database = $databases->fetch_assoc()) {
566 $this->assertSame($database['SCHEMA_NAME'], $result[$i]);
567 $i++;
568 }
569 }
570
571 /**
572 * Data Provider for sqlNumRowsReturnsCorrectAmountOfRows()
573 *
574 * @see sqlNumRowsReturnsCorrectAmountOfRows()
575 *
576 * @return array
577 */
578 public function sqlNumRowsReturnsCorrectAmountOfRowsProvider()
579 {
580 $sql1 = "SELECT * FROM {$this->testTable} WHERE {$this->testField}='baz'";
581 $sql2 = "SELECT * FROM {$this->testTable} WHERE {$this->testField}='baz' OR {$this->testField}='bar'";
582 $sql3 = "SELECT * FROM {$this->testTable} WHERE {$this->testField} IN ('baz', 'bar', 'foo')";
583
584 return [
585 'One result' => [$sql1, 1],
586 'Two results' => [$sql2, 2],
587 'Three results' => [$sql3, 3],
588 ];
589 }
590
591 /**
592 * @test
593 * @dataProvider sqlNumRowsReturnsCorrectAmountOfRowsProvider
594 *
595 * @param string $sql
596 * @param string $expectedResult
597 *
598 * @return void
599 */
600 public function sqlNumRowsReturnsCorrectAmountOfRows($sql, $expectedResult)
601 {
602 $this->assertTrue(
603 $this->subject->admin_query(
604 "INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('foo'), ('bar'), ('baz')"
605 )
606 );
607
608 $res = $this->subject->admin_query($sql);
609 $numRows = $this->subject->sql_num_rows($res);
610 $this->assertSame($expectedResult, $numRows);
611 }
612
613 /**
614 * @test
615 *
616 * @return void
617 */
618 public function sqlNumRowsReturnsFalse()
619 {
620 $res = $this->subject->admin_query("SELECT * FROM {$this->testTable} WHERE test='baz'");
621 $numRows = $this->subject->sql_num_rows($res);
622 $this->assertFalse($numRows);
623 }
624
625 /**
626 * Prepares the test table for the fetch* Tests
627 *
628 * @return void
629 */
630 protected function prepareTableForFetchTests()
631 {
632 $this->assertTrue(
633 $this->subject->sql_query(
634 "ALTER TABLE {$this->testTable} " .
635 'ADD name mediumblob, ' .
636 'ADD deleted int, ' .
637 'ADD street varchar(100), ' .
638 'ADD city varchar(50), ' .
639 'ADD country varchar(100)'
640 )
641 );
642
643 $this->assertTrue(
644 $this->subject->admin_query(
645 "INSERT INTO {$this->testTable} (name,street,city,country,deleted) VALUES " .
646 "('Mr. Smith','Oakland Road','Los Angeles','USA',0)," .
647 "('Ms. Smith','Oakland Road','Los Angeles','USA',0)," .
648 "('Alice im Wunderland','Große Straße','Königreich der Herzen','Wunderland',0)," .
649 "('Agent Smith','Unbekannt','Unbekannt','Matrix',1)"
650 )
651 );
652 }
653
654 /**
655 * @test
656 *
657 * @return void
658 */
659 public function sqlFetchAssocReturnsAssocArray()
660 {
661 $this->prepareTableForFetchTests();
662
663 $res = $this->subject->admin_query("SELECT * FROM {$this->testTable} ORDER BY id");
664 $expectedResult = [
665 [
666 'id' => '1',
667 $this->testField => null,
668 $this->anotherTestField => null,
669 'name' => 'Mr. Smith',
670 'deleted' => '0',
671 'street' => 'Oakland Road',
672 'city' => 'Los Angeles',
673 'country' => 'USA',
674 ],
675 [
676 'id' => '2',
677 $this->testField => null,
678 $this->anotherTestField => null,
679 'name' => 'Ms. Smith',
680 'deleted' => '0',
681 'street' => 'Oakland Road',
682 'city' => 'Los Angeles',
683 'country' => 'USA',
684 ],
685 [
686 'id' => '3',
687 $this->testField => null,
688 $this->anotherTestField => null,
689 'name' => 'Alice im Wunderland',
690 'deleted' => '0',
691 'street' => 'Große Straße',
692 'city' => 'Königreich der Herzen',
693 'country' => 'Wunderland',
694 ],
695 [
696 'id' => '4',
697 $this->testField => null,
698 $this->anotherTestField => null,
699 'name' => 'Agent Smith',
700 'deleted' => '1',
701 'street' => 'Unbekannt',
702 'city' => 'Unbekannt',
703 'country' => 'Matrix',
704 ],
705 ];
706 $i = 0;
707 while ($row = $this->subject->sql_fetch_assoc($res)) {
708 $this->assertSame($expectedResult[$i], $row);
709 $i++;
710 }
711 }
712
713 /**
714 * @test
715 *
716 * @return void
717 */
718 public function sqlFetchRowReturnsNumericArray()
719 {
720 $this->prepareTableForFetchTests();
721 $res = $this->subject->admin_query("SELECT * FROM {$this->testTable} ORDER BY id");
722 $expectedResult = [
723 ['1', null, null, 'Mr. Smith', '0', 'Oakland Road', 'Los Angeles', 'USA'],
724 ['2', null, null, 'Ms. Smith', '0', 'Oakland Road', 'Los Angeles', 'USA'],
725 ['3', null, null, 'Alice im Wunderland', '0', 'Große Straße', 'Königreich der Herzen', 'Wunderland'],
726 ['4', null, null, 'Agent Smith', '1', 'Unbekannt', 'Unbekannt', 'Matrix'],
727 ];
728 $i = 0;
729 while ($row = $this->subject->sql_fetch_row($res)) {
730 $this->assertSame($expectedResult[$i], $row);
731 $i++;
732 }
733 }
734
735 /**
736 * @test
737 *
738 * @return void
739 */
740 public function sqlFreeResultReturnsFalseOnFailure()
741 {
742 $this->assertTrue(
743 $this->subject->admin_query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('baz')")
744 );
745 $res = $this->subject->admin_query("SELECT * FROM {$this->testTable} WHERE {$this->testField}=baz");
746 $this->assertFalse($this->subject->sql_free_result($res));
747 }
748
749 /**
750 * @test
751 *
752 * @return void
753 */
754 public function sqlFreeResultReturnsTrueOnSuccess()
755 {
756 $this->assertTrue(
757 $this->subject->admin_query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('baz')")
758 );
759 $res = $this->subject->admin_query("SELECT * FROM {$this->testTable} WHERE {$this->testField}='baz'");
760 $this->assertTrue($this->subject->sql_free_result($res));
761 }
762 }