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