[TASK] Remove ext:dbal from installation steps
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Database / ConnectionTest.php
1 <?php
2 declare (strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\Database;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Doctrine\DBAL\Driver\Mysqli\MysqliConnection;
19 use Doctrine\DBAL\Statement;
20 use Prophecy\Prophecy\ObjectProphecy;
21 use TYPO3\CMS\Core\Database\Connection;
22 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
23 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
24 use TYPO3\CMS\Core\Tests\Unit\Database\Mocks\MockPlatform;
25 use TYPO3\CMS\Core\Tests\UnitTestCase;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27
28 /**
29 * Test case
30 *
31 */
32 class ConnectionTest extends UnitTestCase
33 {
34 /**
35 * @var Connection|\PHPUnit_Framework_MockObject_MockObject
36 */
37 protected $connection;
38
39 /**
40 * @var \Doctrine\DBAL\Platforms\AbstractPlatform
41 */
42 protected $platform;
43
44 /**
45 * @var string
46 */
47 protected $testTable = 'testTable';
48
49 /**
50 * Create a new database connection mock object for every test.
51 *
52 * @return void
53 */
54 protected function setUp()
55 {
56 parent::setUp();
57
58 $this->connection = $this->getMockBuilder(Connection::class)
59 ->disableOriginalConstructor()
60 ->setMethods(
61 [
62 'connect',
63 'executeQuery',
64 'executeUpdate',
65 'getDatabasePlatform',
66 'getDriver',
67 'getExpressionBuilder',
68 'getWrappedConnection',
69 ]
70 )
71 ->getMock();
72
73 $this->connection->expects($this->any())
74 ->method('getExpressionBuilder')
75 ->will($this->returnValue(GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection)));
76
77 $this->connection->expects($this->any())
78 ->method('connect');
79
80 $this->connection->expects($this->any())
81 ->method('getDatabasePlatform')
82 ->will($this->returnValue(new MockPlatform()));
83 }
84
85 /**
86 * @test
87 */
88 public function createQueryBuilderReturnsInstanceOfTypo3QueryBuilder()
89 {
90 $this->assertInstanceOf(QueryBuilder::class, $this->connection->createQueryBuilder());
91 }
92
93 /**
94 * @return array
95 */
96 public function quoteIdentifierDataProvider()
97 {
98 return [
99 'SQL star' => [
100 '*',
101 '*',
102 ],
103 'fieldname' => [
104 'aField',
105 '"aField"',
106 ],
107 'whitespace' => [
108 'with blanks',
109 '"with blanks"',
110 ],
111 'double quotes' => [
112 '"double" quotes',
113 '"""double"" quotes"',
114 ],
115 'single quotes' => [
116 "'single'",
117 '"\'single\'"',
118
119 ],
120 'multiple double quotes' => [
121 '""multiple""',
122 '"""""multiple"""""',
123 ],
124 'multiple single quotes' => [
125 "''multiple''",
126 '"\'\'multiple\'\'"',
127 ],
128 'backticks' => [
129 '`backticks`',
130 '"`backticks`"',
131 ],
132 'slashes' => [
133 '/slashes/',
134 '"/slashes/"',
135 ],
136 'backslashes' => [
137 '\\backslashes\\',
138 '"\\backslashes\\"',
139 ],
140 ];
141 }
142
143 /**
144 * @test
145 * @dataProvider quoteIdentifierDataProvider
146 * @param string $input
147 * @param string $expected
148 */
149 public function quoteIdentifier(string $input, string $expected)
150 {
151 $this->assertSame($expected, $this->connection->quoteIdentifier($input));
152 }
153
154 /**
155 * @test
156 */
157 public function quoteIdentifiers()
158 {
159 $input = [
160 'aField',
161 'anotherField',
162 ];
163
164 $expected = [
165 '"aField"',
166 '"anotherField"',
167 ];
168
169 $this->assertSame($expected, $this->connection->quoteIdentifiers($input));
170 }
171
172 /**
173 * @return array
174 */
175 public function insertQueriesDataProvider()
176 {
177 return [
178 'single value' => [
179 ['aTestTable', ['aField' => 'aValue']],
180 'INSERT INTO "aTestTable" ("aField") VALUES (?)',
181 ['aValue'],
182 [],
183 ],
184 'multiple values' => [
185 ['aTestTable', ['aField' => 'aValue', 'bField' => 'bValue']],
186 'INSERT INTO "aTestTable" ("aField", "bField") VALUES (?, ?)',
187 ['aValue', 'bValue'],
188 [],
189 ],
190 'with types' => [
191 ['aTestTable', ['aField' => 'aValue', 'bField' => 'bValue'], [Connection::PARAM_STR, Connection::PARAM_STR]],
192 'INSERT INTO "aTestTable" ("aField", "bField") VALUES (?, ?)',
193 ['aValue', 'bValue'],
194 [Connection::PARAM_STR, Connection::PARAM_STR],
195 ],
196 ];
197 }
198
199 /**
200 * @test
201 * @dataProvider insertQueriesDataProvider
202 * @param array $args
203 * @param string $expectedQuery
204 * @param array $expectedValues
205 * @param array $expectedTypes
206 */
207 public function insertQueries(array $args, string $expectedQuery, array $expectedValues, array $expectedTypes)
208 {
209 $this->connection->expects($this->once())
210 ->method('executeUpdate')
211 ->with($expectedQuery, $expectedValues, $expectedTypes)
212 ->will($this->returnValue(1));
213
214 $this->connection->insert(...$args);
215 }
216
217 /**
218 * @test
219 */
220 public function bulkInsert()
221 {
222 $this->connection->expects($this->once())
223 ->method('executeUpdate')
224 ->with('INSERT INTO "aTestTable" ("aField") VALUES (?), (?)', ['aValue', 'anotherValue'])
225 ->will($this->returnValue(2));
226
227 $this->connection->bulkInsert('aTestTable', [['aField' => 'aValue'], ['aField' => 'anotherValue']], ['aField']);
228 }
229
230 /**
231 * @return array
232 */
233 public function updateQueriesDataProvider()
234 {
235 return [
236 'single value' => [
237 ['aTestTable', ['aField' => 'aValue'], ['uid' => 1]],
238 'UPDATE "aTestTable" SET "aField" = ? WHERE "uid" = ?',
239 ['aValue', 1],
240 [],
241 ],
242 'multiple values' => [
243 ['aTestTable', ['aField' => 'aValue', 'bField' => 'bValue'], ['uid' => 1]],
244 'UPDATE "aTestTable" SET "aField" = ?, "bField" = ? WHERE "uid" = ?',
245 ['aValue', 'bValue', 1],
246 [],
247 ],
248 'with types' => [
249 ['aTestTable', ['aField' => 'aValue'], ['uid' => 1], [Connection::PARAM_STR]],
250 'UPDATE "aTestTable" SET "aField" = ? WHERE "uid" = ?',
251 ['aValue', 1],
252 [Connection::PARAM_STR],
253 ],
254 ];
255 }
256
257 /**
258 * @test
259 * @dataProvider updateQueriesDataProvider
260 * @param array $args
261 * @param string $expectedQuery
262 * @param array $expectedValues
263 * @param array $expectedTypes
264 */
265 public function updateQueries(array $args, string $expectedQuery, array $expectedValues, array $expectedTypes)
266 {
267 $this->connection->expects($this->once())
268 ->method('executeUpdate')
269 ->with($expectedQuery, $expectedValues, $expectedTypes)
270 ->will($this->returnValue(1));
271
272 $this->connection->update(...$args);
273 }
274
275 /**
276 * @return array
277 */
278 public function deleteQueriesDataProvider()
279 {
280 return [
281 'single condition' => [
282 ['aTestTable', ['aField' => 'aValue']],
283 'DELETE FROM "aTestTable" WHERE "aField" = ?',
284 ['aValue'],
285 [],
286 ],
287 'multiple conditions' => [
288 ['aTestTable', ['aField' => 'aValue', 'bField' => 'bValue']],
289 'DELETE FROM "aTestTable" WHERE "aField" = ? AND "bField" = ?',
290 ['aValue', 'bValue'],
291 [],
292 ],
293 'with types' => [
294 ['aTestTable', ['aField' => 'aValue'], [Connection::PARAM_STR]],
295 'DELETE FROM "aTestTable" WHERE "aField" = ?',
296 ['aValue'],
297 [Connection::PARAM_STR],
298 ],
299 ];
300 }
301
302 /**
303 * @test
304 * @dataProvider deleteQueriesDataProvider
305 * @param array $args
306 * @param string $expectedQuery
307 * @param array $expectedValues
308 * @param array $expectedTypes
309 */
310 public function deleteQueries(array $args, string $expectedQuery, array $expectedValues, array $expectedTypes)
311 {
312 $this->connection->expects($this->once())
313 ->method('executeUpdate')
314 ->with($expectedQuery, $expectedValues, $expectedTypes)
315 ->will($this->returnValue(1));
316
317 $this->connection->delete(...$args);
318 }
319
320 /**
321 * Data provider for select query tests
322 *
323 * Each array item consists of
324 * - array of parameters for select call
325 * - expected SQL string
326 * - expected named parameter values
327 *
328 * @return array
329 */
330 public function selectQueriesDataProvider()
331 {
332 return [
333 'all columns' => [
334 [['*'], 'aTable'],
335 'SELECT * FROM "aTable"',
336 [],
337 ],
338 'subset of columns' => [
339 [['aField', 'anotherField'], 'aTable'],
340 'SELECT "aField", "anotherField" FROM "aTable"',
341 [],
342 ],
343 'conditions' => [
344 [['*'], 'aTable', ['aField' => 'aValue']],
345 'SELECT * FROM "aTable" WHERE "aField" = :dcValue1',
346 ['dcValue1' => 'aValue'],
347 ],
348 'grouping' => [
349 [['*'], 'aTable', [], ['aField']],
350 'SELECT * FROM "aTable" GROUP BY "aField"',
351 [],
352 ],
353 'ordering' => [
354 [['*'], 'aTable', [], [], ['aField' => 'ASC']],
355 'SELECT * FROM "aTable" ORDER BY "aField" ASC',
356 [],
357 ],
358 'limit' => [
359 [['*'], 'aTable', [], [], [], 1],
360 'SELECT * FROM "aTable" LIMIT 1 OFFSET 0',
361 [],
362 ],
363 'offset' => [
364 [['*'], 'aTable', [], [], [], 1, 10],
365 'SELECT * FROM "aTable" LIMIT 1 OFFSET 10',
366 [],
367 ],
368 'everything' => [
369 [
370 ['aField', 'anotherField'],
371 'aTable',
372 ['aField' => 'aValue'],
373 ['anotherField'],
374 ['aField' => 'ASC'],
375 1,
376 10,
377 ],
378 'SELECT "aField", "anotherField" FROM "aTable" WHERE "aField" = :dcValue1 ' .
379 'GROUP BY "anotherField" ORDER BY "aField" ASC LIMIT 1 OFFSET 10',
380 ['dcValue1' => 'aValue'],
381 ],
382 ];
383 }
384
385 /**
386 * @test
387 * @dataProvider selectQueriesDataProvider
388 * @param array $args
389 * @param string $expectedQuery
390 * @param array $expectedParameters
391 */
392 public function selectQueries(array $args, string $expectedQuery, array $expectedParameters)
393 {
394 $resultStatement = $this->createMock(Statement::class);
395
396 $this->connection->expects($this->once())
397 ->method('executeQuery')
398 ->with($expectedQuery, $expectedParameters)
399 ->will($this->returnValue($resultStatement));
400
401 $this->connection->select(...$args);
402 }
403
404 /**
405 * Data provider for select query tests
406 *
407 * Each array item consists of
408 * - array of parameters for select call
409 * - expected SQL string
410 * - expected named parameter values
411 *
412 * @return array
413 */
414 public function countQueriesDataProvider()
415 {
416 return [
417 'all columns' => [
418 ['*', 'aTable', []],
419 'SELECT COUNT(*) FROM "aTable"',
420 [],
421 ],
422 'specified columns' => [
423 ['aField', 'aTable', []],
424 'SELECT COUNT("aField") FROM "aTable"',
425 [],
426 ],
427 'conditions' => [
428 ['aTable.aField', 'aTable', ['aField' => 'aValue']],
429 'SELECT COUNT("aTable"."aField") FROM "aTable" WHERE "aField" = :dcValue1',
430 ['dcValue1' => 'aValue'],
431 ],
432 ];
433 }
434
435 /**
436 * @test
437 * @dataProvider countQueriesDataProvider
438 * @param array $args
439 * @param string $expectedQuery
440 * @param array $expectedParameters
441 */
442 public function countQueries(array $args, string $expectedQuery, array $expectedParameters)
443 {
444 $resultStatement = $this->createMock(Statement::class);
445
446 $resultStatement->expects($this->once())
447 ->method('fetchColumn')
448 ->with(0)
449 ->will($this->returnValue(0));
450
451 $this->connection->expects($this->once())
452 ->method('executeQuery')
453 ->with($expectedQuery, $expectedParameters)
454 ->will($this->returnValue($resultStatement));
455
456 $this->connection->count(...$args);
457 }
458
459 /**
460 * @test
461 */
462 public function truncateQuery()
463 {
464 $this->connection->expects($this->once())
465 ->method('executeUpdate')
466 ->with('TRUNCATE "aTestTable"')
467 ->will($this->returnValue(0));
468
469 $this->connection->truncate('aTestTable', false);
470 }
471
472 /**
473 * @test
474 */
475 public function getServerVersionReportsPlatformVersion()
476 {
477 /** @var MysqliConnection|ObjectProphecy $driverProphet */
478 $driverProphet = $this->prophesize(\Doctrine\DBAL\Driver\Mysqli\Driver::class);
479 $driverProphet->willImplement(\Doctrine\DBAL\VersionAwarePlatformDriver::class);
480
481 /** @var MysqliConnection|ObjectProphecy $wrappedConnectionProphet */
482 $wrappedConnectionProphet = $this->prophesize(\Doctrine\DBAL\Driver\Mysqli\MysqliConnection::class);
483 $wrappedConnectionProphet->willImplement(\Doctrine\DBAL\Driver\ServerInfoAwareConnection::class);
484 $wrappedConnectionProphet->requiresQueryForServerVersion()->willReturn(false);
485 $wrappedConnectionProphet->getServerVersion()->willReturn('5.7.11');
486
487 $this->connection->expects($this->any())
488 ->method('getDriver')
489 ->willReturn($driverProphet->reveal());
490 $this->connection->expects($this->any())
491 ->method('getWrappedConnection')
492 ->willReturn($wrappedConnectionProphet->reveal());
493
494 $this->assertSame('mock 5.7.11', $this->connection->getServerVersion());
495 }
496 }