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