[TASK] typo3/testing-framework now flushes runtime caches
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Service / ExtensionServiceTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Extbase\Tests\Unit\Service;
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 use Doctrine\DBAL\Statement;
18 use Prophecy\Argument;
19 use Prophecy\Prophecy\ObjectProphecy;
20 use TYPO3\CMS\Core\Database\Connection;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
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\Utility\GeneralUtility;
26 use TYPO3\CMS\Extbase\Exception;
27 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
28
29 /**
30 * Test case
31 */
32 class ExtensionServiceTest extends UnitTestCase
33 {
34 /**
35 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
36 */
37 protected $mockConfigurationManager;
38
39 /**
40 * @var \TYPO3\CMS\Extbase\Service\ExtensionService
41 */
42 protected $extensionService;
43
44 /**
45 * Due to nested PageRepository / FrontendRestriction Container issues, the Context object is set
46 * @var bool
47 */
48 protected $resetSingletonInstances = true;
49
50 protected function setUp()
51 {
52 $GLOBALS['TSFE'] = new \stdClass();
53 $this->extensionService = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Service\ExtensionService::class, ['dummy']);
54 $this->mockConfigurationManager = $this->createMock(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::class);
55 $this->extensionService->_set('configurationManager', $this->mockConfigurationManager);
56 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'] = [
57 'ExtensionName' => [
58 'plugins' => [
59 'SomePlugin' => [
60 'controllers' => [
61 'ControllerName' => [
62 'actions' => ['index', 'otherAction']
63 ]
64 ]
65 ],
66 'ThirdPlugin' => [
67 'controllers' => [
68 'ControllerName' => [
69 'actions' => ['otherAction', 'thirdAction']
70 ]
71 ]
72 ]
73 ]
74 ],
75 'SomeOtherExtensionName' => [
76 'plugins' => [
77 'SecondPlugin' => [
78 'controllers' => [
79 'ControllerName' => [
80 'actions' => ['index', 'otherAction']
81 ],
82 'SecondControllerName' => [
83 'actions' => ['someAction', 'someOtherAction'],
84 'nonCacheableActions' => ['someOtherAction']
85 ]
86 ]
87 ]
88 ]
89 ]
90 ];
91 }
92
93 /**
94 * Setup and return a mocked database connection that allows
95 * the QueryBuilder to work.
96 *
97 * @return ObjectProphecy
98 */
99 protected function getMockDatabaseConnection(): ObjectProphecy
100 {
101 $connection = $this->prophesize(Connection::class);
102 $connection->getDatabasePlatform()->willReturn(new MockPlatform());
103 $connection->getExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal()));
104 $connection->quoteIdentifier(Argument::cetera())->willReturnArgument(0);
105
106 $queryBuilder = new QueryBuilder(
107 $connection->reveal(),
108 null,
109 new \Doctrine\DBAL\Query\QueryBuilder($connection->reveal())
110 );
111
112 $connectionPool = $this->prophesize(ConnectionPool::class);
113 $connectionPool->getQueryBuilderForTable('tt_content')->willReturn($queryBuilder);
114 GeneralUtility::addInstance(ConnectionPool::class, $connectionPool->reveal());
115
116 return $connection;
117 }
118
119 /**
120 * DataProvider for getPluginNamespaceByPluginSignatureTests()
121 *
122 * @return array
123 */
124 public function getPluginNamespaceDataProvider()
125 {
126 return [
127 ['SomeExtension', 'SomePlugin', 'tx_someextension_someplugin'],
128 ['NonExistingExtension', 'SomePlugin', 'tx_nonexistingextension_someplugin'],
129 ['Invalid', '', 'tx_invalid_']
130 ];
131 }
132
133 /**
134 * @test
135 * @dataProvider getPluginNamespaceDataProvider
136 * @param string $extensionName
137 * @param string $pluginName
138 * @param mixed $expectedResult
139 */
140 public function getPluginNamespaceTests($extensionName, $pluginName, $expectedResult)
141 {
142 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will($this->returnValue([]));
143 $actualResult = $this->extensionService->getPluginNamespace($extensionName, $pluginName);
144 $this->assertEquals($expectedResult, $actualResult, 'Failing for extension: "' . $extensionName . '", plugin: "' . $pluginName . '"');
145 }
146
147 /**
148 * @test
149 */
150 public function pluginNamespaceCanBeOverridden()
151 {
152 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->with(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, 'SomeExtension', 'SomePlugin')->will($this->returnValue(['view' => ['pluginNamespace' => 'overridden_plugin_namespace']]));
153 $expectedResult = 'overridden_plugin_namespace';
154 $actualResult = $this->extensionService->getPluginNamespace('SomeExtension', 'SomePlugin');
155 $this->assertEquals($expectedResult, $actualResult);
156 }
157
158 /**
159 * DataProvider for getPluginNameByActionTests()
160 *
161 * @return array
162 */
163 public function getPluginNameByActionDataProvider()
164 {
165 return [
166 ['ExtensionName', 'ControllerName', 'someNonExistingAction', null],
167 ['ExtensionName', 'ControllerName', 'index', 'SomePlugin'],
168 ['ExtensionName', 'ControllerName', 'thirdAction', 'ThirdPlugin'],
169 ['eXtEnSiOnNaMe', 'cOnTrOlLeRnAmE', 'thirdAction', null],
170 ['eXtEnSiOnNaMe', 'cOnTrOlLeRnAmE', 'ThIrDaCtIoN', null],
171 ['SomeOtherExtensionName', 'ControllerName', 'otherAction', 'SecondPlugin']
172 ];
173 }
174
175 /**
176 * @test
177 * @dataProvider getPluginNameByActionDataProvider
178 * @param string $extensionName
179 * @param string $controllerName
180 * @param string $actionName
181 * @param mixed $expectedResult
182 */
183 public function getPluginNameByActionTests($extensionName, $controllerName, $actionName, $expectedResult)
184 {
185 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->with(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK)->will($this->returnValue(['view' => ['pluginNamespace' => 'overridden_plugin_namespace']]));
186 $actualResult = $this->extensionService->getPluginNameByAction($extensionName, $controllerName, $actionName);
187 $this->assertEquals($expectedResult, $actualResult, 'Failing for $extensionName: "' . $extensionName . '", $controllerName: "' . $controllerName . '", $actionName: "' . $actionName . '" - ');
188 }
189
190 /**
191 * @test
192 */
193 public function getPluginNameByActionThrowsExceptionIfMoreThanOnePluginMatches()
194 {
195 $this->expectException(Exception::class);
196 $this->expectExceptionCode(1280825466);
197 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->with(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK)->will($this->returnValue(['view' => ['pluginNamespace' => 'overridden_plugin_namespace']]));
198 $this->extensionService->getPluginNameByAction('ExtensionName', 'ControllerName', 'otherAction');
199 }
200
201 /**
202 * @test
203 */
204 public function getPluginNameByActionReturnsCurrentIfItCanHandleTheActionEvenIfMoreThanOnePluginMatches()
205 {
206 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->with(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK)->will($this->returnValue(['extensionName' => 'CurrentExtension', 'pluginName' => 'CurrentPlugin', 'controllerConfiguration' => ['ControllerName' => ['actions' => ['otherAction']]]]));
207 $actualResult = $this->extensionService->getPluginNameByAction('CurrentExtension', 'ControllerName', 'otherAction');
208 $expectedResult = 'CurrentPlugin';
209 $this->assertEquals($expectedResult, $actualResult);
210 }
211
212 /**
213 * @test
214 */
215 public function isActionCacheableReturnsTrueByDefault()
216 {
217 $mockConfiguration = [];
218 $this->mockConfigurationManager->expects($this->any())->method('getConfiguration')->will($this->returnValue($mockConfiguration));
219 $actualResult = $this->extensionService->isActionCacheable('SomeExtension', 'SomePlugin', 'SomeController', 'someAction');
220 $this->assertTrue($actualResult);
221 }
222
223 /**
224 * @test
225 */
226 public function isActionCacheableReturnsFalseIfActionIsNotCacheable()
227 {
228 $mockConfiguration = [
229 'controllerConfiguration' => [
230 'SomeController' => [
231 'nonCacheableActions' => ['someAction']
232 ]
233 ]
234 ];
235 $this->mockConfigurationManager->expects($this->any())->method('getConfiguration')->will($this->returnValue($mockConfiguration));
236 $actualResult = $this->extensionService->isActionCacheable('SomeExtension', 'SomePlugin', 'SomeController', 'someAction');
237 $this->assertFalse($actualResult);
238 }
239
240 /**
241 * @test
242 */
243 public function getTargetPidByPluginSignatureReturnsNullIfConfigurationManagerIsNotInitialized()
244 {
245 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will($this->returnValue(null));
246 $this->assertNull($this->extensionService->getTargetPidByPlugin('ExtensionName', 'PluginName'));
247 }
248
249 /**
250 * @test
251 */
252 public function getTargetPidByPluginSignatureReturnsNullIfDefaultPidIsZero()
253 {
254 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will($this->returnValue(['view' => ['defaultPid' => 0]]));
255 $this->assertNull($this->extensionService->getTargetPidByPlugin('ExtensionName', 'PluginName'));
256 }
257
258 /**
259 * @test
260 */
261 public function getTargetPidByPluginSignatureReturnsTheConfiguredDefaultPid()
262 {
263 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will($this->returnValue(['view' => ['defaultPid' => 123]]));
264 $expectedResult = 123;
265 $actualResult = $this->extensionService->getTargetPidByPlugin('ExtensionName', 'SomePlugin');
266 $this->assertEquals($expectedResult, $actualResult);
267 }
268
269 /**
270 * @test
271 * @todo This should rather be a functional test since it needs a connection / querybuilder
272 */
273 public function getTargetPidByPluginSignatureDeterminesTheTargetPidIfDefaultPidIsAuto()
274 {
275 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will(
276 $this->returnValue(['view' => ['defaultPid' => 'auto']])
277 );
278 $expectedResult = 321;
279
280 $statement = $this->prophesize(Statement::class);
281 $statement->fetchAll()->shouldBeCalled()->willReturn([['pid' => (string)$expectedResult]]);
282
283 $connection = $this->getMockDatabaseConnection();
284 $connection->executeQuery(
285 'SELECT pid FROM tt_content WHERE (list_type = :dcValue1) AND (CType = :dcValue2) AND (sys_language_uid = :dcValue3) LIMIT 2',
286 ['dcValue1' => 'extensionname_someplugin', 'dcValue2' => 'list', 'dcValue3' => 0],
287 Argument::cetera()
288 )->shouldBeCalled()->willReturn($statement->reveal());
289
290 $actualResult = $this->extensionService->getTargetPidByPlugin('ExtensionName', 'SomePlugin');
291 $this->assertEquals($expectedResult, $actualResult);
292 }
293
294 /**
295 * @test
296 * @todo This should rather be a functional test since it needs a connection / querybuilder
297 */
298 public function getTargetPidByPluginSignatureReturnsNullIfTargetPidCouldNotBeDetermined()
299 {
300 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will(
301 $this->returnValue(['view' => ['defaultPid' => 'auto']])
302 );
303
304 $statement = $this->prophesize(Statement::class);
305 $statement->fetchAll()->shouldBeCalled()->willReturn([]);
306
307 $connection = $this->getMockDatabaseConnection();
308 $connection->executeQuery(
309 'SELECT pid FROM tt_content WHERE (list_type = :dcValue1) AND (CType = :dcValue2) AND (sys_language_uid = :dcValue3) LIMIT 2',
310 ['dcValue1' => 'extensionname_someplugin', 'dcValue2' => 'list', 'dcValue3' => 0],
311 Argument::cetera()
312 )->shouldBeCalled()->willReturn($statement->reveal());
313
314 $this->assertNull($this->extensionService->getTargetPidByPlugin('ExtensionName', 'SomePlugin'));
315 }
316
317 /**
318 * @test
319 * @todo This should rather be a functional test since it needs a connection / querybuilder
320 */
321 public function getTargetPidByPluginSignatureThrowsExceptionIfMoreThanOneTargetPidsWereFound()
322 {
323 $this->expectException(Exception::class);
324 $this->expectExceptionCode(1280773643);
325
326 $this->mockConfigurationManager->expects($this->once())->method('getConfiguration')->will(
327 $this->returnValue(['view' => ['defaultPid' => 'auto']])
328 );
329
330 $statement = $this->prophesize(Statement::class);
331 $statement->fetchAll()->shouldBeCalled()->willReturn([['pid' => 123], ['pid' => 124]]);
332
333 $connection = $this->getMockDatabaseConnection();
334 $connection->executeQuery(
335 'SELECT pid FROM tt_content WHERE (list_type = :dcValue1) AND (CType = :dcValue2) AND (sys_language_uid = :dcValue3) LIMIT 2',
336 ['dcValue1' => 'extensionname_someplugin', 'dcValue2' => 'list', 'dcValue3' => 0],
337 Argument::cetera()
338 )->shouldBeCalled()->willReturn($statement->reveal());
339
340 $this->expectException(\TYPO3\CMS\Extbase\Exception::class);
341 $this->expectExceptionCode(1280773643);
342
343 $this->extensionService->getTargetPidByPlugin('ExtensionName', 'SomePlugin');
344 }
345
346 /**
347 * @test
348 */
349 public function getDefaultControllerNameByPluginReturnsNullIfGivenExtensionCantBeFound()
350 {
351 $this->assertNull($this->extensionService->getDefaultControllerNameByPlugin('NonExistingExtensionName', 'SomePlugin'));
352 }
353
354 /**
355 * @test
356 */
357 public function getDefaultControllerNameByPluginReturnsNullIfGivenPluginCantBeFound()
358 {
359 $this->assertNull($this->extensionService->getDefaultControllerNameByPlugin('ExtensionName', 'NonExistingPlugin'));
360 }
361
362 /**
363 * @test
364 */
365 public function getDefaultControllerNameByPluginReturnsFirstControllerNameOfGivenPlugin()
366 {
367 $expectedResult = 'ControllerName';
368 $actualResult = $this->extensionService->getDefaultControllerNameByPlugin('ExtensionName', 'SomePlugin');
369 $this->assertEquals($expectedResult, $actualResult);
370 }
371
372 /**
373 * @test
374 */
375 public function getDefaultActionNameByPluginAndControllerReturnsNullIfGivenExtensionCantBeFound()
376 {
377 $this->assertNull($this->extensionService->getDefaultActionNameByPluginAndController('NonExistingExtensionName', 'SomePlugin', 'ControllerName'));
378 }
379
380 /**
381 * @test
382 */
383 public function getDefaultActionNameByPluginAndControllerReturnsNullIfGivenPluginCantBeFound()
384 {
385 $this->assertNull($this->extensionService->getDefaultActionNameByPluginAndController('ExtensionName', 'NonExistingPlugin', 'ControllerName'));
386 }
387
388 /**
389 * @test
390 */
391 public function getDefaultActionNameByPluginAndControllerReturnsNullIfGivenControllerCantBeFound()
392 {
393 $this->assertNull($this->extensionService->getDefaultActionNameByPluginAndController('ExtensionName', 'SomePlugin', 'NonExistingControllerName'));
394 }
395
396 /**
397 * @test
398 */
399 public function getDefaultActionNameByPluginAndControllerReturnsFirstActionNameOfGivenController()
400 {
401 $expectedResult = 'someAction';
402 $actualResult = $this->extensionService->getDefaultActionNameByPluginAndController('SomeOtherExtensionName', 'SecondPlugin', 'SecondControllerName');
403 $this->assertEquals($expectedResult, $actualResult);
404 }
405 }