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