[TASK] Hook up ContainerInterface in backend route dispatcher
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Http / RouteDispatcherTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Http;
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
17 use Prophecy\Argument;
18 use Psr\Container\ContainerInterface;
19 use Psr\Http\Message\ResponseInterface;
20 use Psr\Http\Message\ServerRequestInterface;
21 use TYPO3\CMS\Backend\Http\RouteDispatcher;
22 use TYPO3\CMS\Backend\Routing\Route;
23 use TYPO3\CMS\Backend\Routing\Router;
24 use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherClassFixture;
25 use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherClassInvokeFixture;
26 use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherClassWithoutInvokeFixture;
27 use TYPO3\CMS\Backend\Tests\Unit\Http\Fixtures\RouteDispatcherStaticClassFixture;
28 use TYPO3\CMS\Core\FormProtection\AbstractFormProtection;
29 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
32
33 /**
34 * Test case
35 */
36 class RouteDispatcherTest extends UnitTestCase
37 {
38 public function tearDown(): void
39 {
40 FormProtectionFactory::purgeInstances();
41 GeneralUtility::purgeInstances();
42 parent::tearDown();
43 }
44
45 /**
46 * @test
47 */
48 public function dispatchThrowsExceptionIfTargetIsNotCallable()
49 {
50 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
51 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
52 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
53
54 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
55 $responseProphecy = $this->prophesize(ResponseInterface::class);
56 $routerProphecy = $this->prophesize(Router::class);
57 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
58 $routeProphecy = $this->prophesize(Route::class);
59 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
60 $routeProphecy->getOption('access')->willReturn('public');
61 $routeProphecy->getOption('module')->willReturn(false);
62 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
63 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
64 $containerProphecy = $this->prophesize(ContainerInterface::class);
65 $containerProphecy->has(Argument::any())->willReturn(false);
66
67 $target = 42;
68 $routeProphecy->getOption('target')->willReturn($target);
69 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
70
71 $this->expectException(\InvalidArgumentException::class);
72 $this->expectExceptionCode(1425381442);
73
74 $subject = new RouteDispatcher($containerProphecy->reveal());
75 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
76 }
77
78 /**
79 * @test
80 */
81 public function dispatchCallsTargetIfTargetIsArray()
82 {
83 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
84 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
85 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
86
87 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
88 $responseProphecy = $this->prophesize(ResponseInterface::class);
89 $routerProphecy = $this->prophesize(Router::class);
90 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
91 $routeProphecy = $this->prophesize(Route::class);
92 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
93 $routeProphecy->getOption('access')->willReturn('public');
94 $routeProphecy->getOption('module')->willReturn(false);
95 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
96 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
97 $containerProphecy = $this->prophesize(ContainerInterface::class);
98 $containerProphecy->has(Argument::any())->willReturn(false);
99
100 $target = [
101 RouteDispatcherClassFixture::class,
102 'mainAction'
103 ];
104 $routeProphecy->getOption('target')->willReturn($target);
105 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
106
107 $this->expectException(\RuntimeException::class);
108 $this->expectExceptionCode(1520756142);
109
110 $subject = new RouteDispatcher($containerProphecy->reveal());
111 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
112 }
113
114 /**
115 * @test
116 */
117 public function dispatchCallsTargetIfTargetIsClosure()
118 {
119 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
120 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
121 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
122
123 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
124 $responseProphecy = $this->prophesize(ResponseInterface::class);
125 $routerProphecy = $this->prophesize(Router::class);
126 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
127 $routeProphecy = $this->prophesize(Route::class);
128 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
129 $routeProphecy->getOption('access')->willReturn('public');
130 $routeProphecy->getOption('module')->willReturn(false);
131 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
132 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
133 $containerProphecy = $this->prophesize(ContainerInterface::class);
134 $containerProphecy->has(Argument::any())->willReturn(false);
135
136 $target = function (ServerRequestInterface $request) {
137 throw new \RuntimeException('I have been called. Good!', 1520756466);
138 };
139 $routeProphecy->getOption('target')->willReturn($target);
140 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
141
142 $this->expectException(\RuntimeException::class);
143 $this->expectExceptionCode(1520756466);
144
145 $subject = new RouteDispatcher($containerProphecy->reveal());
146 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
147 }
148
149 /**
150 * @test
151 */
152 public function dispatchCallsTargetIfTargetIsClassImplementingInvoke()
153 {
154 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
155 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
156 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
157
158 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
159 $responseProphecy = $this->prophesize(ResponseInterface::class);
160 $routerProphecy = $this->prophesize(Router::class);
161 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
162 $routeProphecy = $this->prophesize(Route::class);
163 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
164 $routeProphecy->getOption('access')->willReturn('public');
165 $routeProphecy->getOption('module')->willReturn(false);
166 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
167 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
168 $containerProphecy = $this->prophesize(ContainerInterface::class);
169 $containerProphecy->has(Argument::any())->willReturn(false);
170
171 $target = RouteDispatcherClassInvokeFixture::class;
172 $routeProphecy->getOption('target')->willReturn($target);
173 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
174
175 $this->expectException(\RuntimeException::class);
176 $this->expectExceptionCode(1520756623);
177
178 $subject = new RouteDispatcher($containerProphecy->reveal());
179 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
180 }
181
182 /**
183 * @test
184 */
185 public function dispatchCallsTargetIfTargetIsInContainer()
186 {
187 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
188 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
189 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
190
191 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
192 $responseProphecy = $this->prophesize(ResponseInterface::class);
193 $routerProphecy = $this->prophesize(Router::class);
194 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
195 $routeProphecy = $this->prophesize(Route::class);
196 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
197 $routeProphecy->getOption('access')->willReturn('public');
198 $routeProphecy->getOption('module')->willReturn(false);
199 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
200 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
201
202 $target = 'routedispatcher.classinvokefixture';
203 $routeProphecy->getOption('target')->willReturn($target);
204 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
205 $containerProphecy = $this->prophesize(ContainerInterface::class);
206 $containerProphecy->has($target)->willReturn(true);
207 $containerProphecy->get($target)->willReturn(new RouteDispatcherClassInvokeFixture);
208
209 $this->expectException(\RuntimeException::class);
210 $this->expectExceptionCode(1520756623);
211
212 $subject = new RouteDispatcher($containerProphecy->reveal());
213 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
214 }
215
216 /**
217 * @test
218 */
219 public function dispatchThrowsExceptionIfTargetWithClassNameOnlyDoesNotImplementInvoke()
220 {
221 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
222 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
223 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
224
225 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
226 $responseProphecy = $this->prophesize(ResponseInterface::class);
227 $routerProphecy = $this->prophesize(Router::class);
228 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
229 $routeProphecy = $this->prophesize(Route::class);
230 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
231 $routeProphecy->getOption('access')->willReturn('public');
232 $routeProphecy->getOption('module')->willReturn(false);
233 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
234 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
235 $containerProphecy = $this->prophesize(ContainerInterface::class);
236 $containerProphecy->has(Argument::any())->willReturn(false);
237
238 $target = RouteDispatcherClassWithoutInvokeFixture::class;
239 $routeProphecy->getOption('target')->willReturn($target);
240 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
241
242 $this->expectException(\InvalidArgumentException::class);
243 $this->expectExceptionCode(1442431631);
244
245 $subject = new RouteDispatcher($containerProphecy->reveal());
246 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
247 }
248
249 /**
250 * @test
251 */
252 public function dispatchCallsClassMethodCombinationGivenAsString()
253 {
254 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
255 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
256 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
257
258 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
259 $responseProphecy = $this->prophesize(ResponseInterface::class);
260 $routerProphecy = $this->prophesize(Router::class);
261 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
262 $routeProphecy = $this->prophesize(Route::class);
263 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
264 $routeProphecy->getOption('access')->willReturn('public');
265 $routeProphecy->getOption('module')->willReturn(false);
266 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
267 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
268 $containerProphecy = $this->prophesize(ContainerInterface::class);
269 $containerProphecy->has(Argument::any())->willReturn(false);
270
271 $target = RouteDispatcherClassFixture::class . '::mainAction';
272 $routeProphecy->getOption('target')->willReturn($target);
273 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
274
275 $this->expectException(\RuntimeException::class);
276 $this->expectExceptionCode(1520756142);
277
278 $subject = new RouteDispatcher($containerProphecy->reveal());
279 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
280 }
281
282 /**
283 * @test
284 */
285 public function dispatchCallsStaticClassMethodCombinationGivenAsString()
286 {
287 $formProtectionProphecy = $this->prophesize(AbstractFormProtection::class);
288 $formProtectionProphecy->validateToken(Argument::cetera())->willReturn(true);
289 FormProtectionFactory::set('default', $formProtectionProphecy->reveal());
290
291 $requestProphecy = $this->prophesize(ServerRequestInterface::class);
292 $responseProphecy = $this->prophesize(ResponseInterface::class);
293 $routerProphecy = $this->prophesize(Router::class);
294 GeneralUtility::setSingletonInstance(Router::class, $routerProphecy->reveal());
295 $routeProphecy = $this->prophesize(Route::class);
296 $routerProphecy->matchRequest($requestProphecy->reveal())->willReturn($routeProphecy->reveal());
297 $routeProphecy->getOption('access')->willReturn('public');
298 $routeProphecy->getOption('module')->willReturn(false);
299 $requestProphecy->withAttribute('route', $routeProphecy->reveal())->willReturn($requestProphecy->reveal());
300 $requestProphecy->getAttribute('route')->willReturn($routeProphecy->reveal());
301 $containerProphecy = $this->prophesize(ContainerInterface::class);
302 $containerProphecy->has(Argument::any())->willReturn(false);
303
304 $target = RouteDispatcherStaticClassFixture::class . '::mainAction';
305 $routeProphecy->getOption('target')->willReturn($target);
306 $requestProphecy->withAttribute('target', $target)->willReturn($requestProphecy->reveal());
307
308 $this->expectException(\RuntimeException::class);
309 $this->expectExceptionCode(1520757000);
310
311 $subject = new RouteDispatcher($containerProphecy->reveal());
312 $subject->dispatch($requestProphecy->reveal(), $responseProphecy->reveal());
313 }
314 }