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