[FEATURE] Add symfony dependency injection for core and extbase
[Packages/TYPO3.CMS.git] / typo3 / sysext / redirects / Tests / Unit / Service / RedirectServiceTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Redirects\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
18 use Prophecy\Argument;
19 use Prophecy\Prophecy\ObjectProphecy;
20 use Psr\Log\LoggerInterface;
21 use TYPO3\CMS\Core\Http\Uri;
22 use TYPO3\CMS\Core\LinkHandling\LinkService;
23 use TYPO3\CMS\Core\Resource\Exception\InvalidPathException;
24 use TYPO3\CMS\Core\Resource\File;
25 use TYPO3\CMS\Core\Resource\Folder;
26 use TYPO3\CMS\Core\Site\Entity\Site;
27 use TYPO3\CMS\Redirects\Service\RedirectCacheService;
28 use TYPO3\CMS\Redirects\Service\RedirectService;
29 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
30
31 class RedirectServiceTest extends UnitTestCase
32 {
33 /**
34 * @var bool Reset singletons created by subject
35 */
36 protected $resetSingletonInstances = true;
37
38 /**
39 * @var RedirectCacheService|ObjectProphecy
40 */
41 protected $redirectCacheServiceProphecy;
42
43 /**
44 * @var LinkService|ObjectProphecy
45 */
46 protected $linkServiceProphecy;
47
48 /**
49 * @var RedirectService
50 */
51 protected $redirectService;
52
53 protected function setUp(): void
54 {
55 parent::setUp();
56 $loggerProphecy = $this->prophesize(LoggerInterface::class);
57 $this->redirectCacheServiceProphecy = $this->prophesize(RedirectCacheService::class);
58 $this->linkServiceProphecy = $this->prophesize(LinkService::class);
59
60 $this->redirectService = new RedirectService($this->redirectCacheServiceProphecy->reveal(), $this->linkServiceProphecy->reveal());
61 $this->redirectService->setLogger($loggerProphecy->reveal());
62
63 $GLOBALS['SIM_ACCESS_TIME'] = 42;
64 }
65
66 /**
67 * @test
68 */
69 public function matchRedirectReturnsNullIfNoRedirectsExist()
70 {
71 $this->redirectCacheServiceProphecy->getRedirects()->willReturn([]);
72
73 $result = $this->redirectService->matchRedirect('example.com', 'foo');
74
75 self::assertNull($result);
76 }
77
78 /**
79 * @test
80 */
81 public function matchRedirectReturnsRedirectOnFlatMatch()
82 {
83 $row = [
84 'target' => 'https://example.com',
85 'force_https' => '0',
86 'keep_query_parameters' => '0',
87 'target_statuscode' => '307',
88 'disabled' => '0',
89 'starttime' => '0',
90 'endtime' => '0'
91 ];
92 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
93 [
94 'example.com' => [
95 'flat' => [
96 'foo/' => [
97 1 => $row,
98 ],
99 ],
100 ],
101 ]
102 );
103
104 $result = $this->redirectService->matchRedirect('example.com', 'foo');
105
106 self::assertSame($row, $result);
107 }
108
109 /**
110 * @test
111 */
112 public function matchRedirectReturnsRedirectOnRespectQueryParametersMatch()
113 {
114 $row = [
115 'target' => 'https://example.com',
116 'force_https' => '0',
117 'keep_query_parameters' => '0',
118 'respect_query_parameters' => '1',
119 'target_statuscode' => '307',
120 'disabled' => '0',
121 'starttime' => '0',
122 'endtime' => '0'
123 ];
124 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
125 [
126 'example.com' => [
127 'respect_query_parameters' => [
128 'index.php?id=123' => [
129 1 => $row,
130 ],
131 ],
132 ],
133 ]
134 );
135
136 $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123');
137
138 self::assertSame($row, $result);
139 }
140
141 /**
142 * @test
143 */
144 public function matchRedirectReturnsRedirectOnRespectQueryParametersMatchWithSlash()
145 {
146 $row = [
147 'target' => 'https://example.com',
148 'force_https' => '0',
149 'keep_query_parameters' => '0',
150 'respect_query_parameters' => '1',
151 'target_statuscode' => '307',
152 'disabled' => '0',
153 'starttime' => '0',
154 'endtime' => '0'
155 ];
156 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
157 [
158 'example.com' => [
159 'respect_query_parameters' => [
160 'index.php/?id=123' => [
161 1 => $row,
162 ],
163 ],
164 ],
165 ]
166 );
167
168 $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123');
169
170 self::assertSame($row, $result);
171 }
172
173 /**
174 * @test
175 */
176 public function matchRedirectReturnsRedirectOnFullRespectQueryParametersMatch()
177 {
178 $row = [
179 'target' => 'https://example.com/target',
180 'force_https' => '0',
181 'keep_query_parameters' => '0',
182 'respect_query_parameters' => '1',
183 'target_statuscode' => '307',
184 'disabled' => '0',
185 'starttime' => '0',
186 'endtime' => '0'
187 ];
188 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
189 [
190 'example.com' => [
191 'respect_query_parameters' => [
192 'index.php?id=123&a=b' => [
193 1 => $row,
194 ],
195 ],
196 ],
197 ]
198 );
199
200 $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123&a=b');
201
202 self::assertSame($row, $result);
203 }
204
205 /**
206 * @test
207 */
208 public function matchRedirectReturnsNullOnPartialRespectQueryParametersMatch()
209 {
210 $row = [
211 'target' => 'https://example.com/target',
212 'force_https' => '0',
213 'keep_query_parameters' => '0',
214 'respect_query_parameters' => '1',
215 'target_statuscode' => '307',
216 'disabled' => '0',
217 'starttime' => '0',
218 'endtime' => '0'
219 ];
220 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
221 [
222 'example.com' => [
223 'respect_query_parameters' => [
224 'index.php?id=123&a=b' => [
225 1 => $row,
226 ],
227 ],
228 ],
229 ]
230 );
231
232 $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123&a=a');
233
234 self::assertSame(null, $result);
235 }
236
237 /**
238 * @test
239 */
240 public function matchRedirectReturnsMatchingRedirectWithMatchingQueryParametersOverMatchingPath()
241 {
242 $row1 = [
243 'target' => 'https://example.com/no-promotion',
244 'force_https' => '0',
245 'keep_query_parameters' => '0',
246 'respect_query_parameters' => '0',
247 'target_statuscode' => '307',
248 'disabled' => '0',
249 'starttime' => '0',
250 'endtime' => '0'
251 ];
252 $row2 = [
253 'target' => 'https://example.com/promotion',
254 'force_https' => '0',
255 'keep_query_parameters' => '0',
256 'respect_query_parameters' => '1',
257 'target_statuscode' => '307',
258 'disabled' => '0',
259 'starttime' => '0',
260 'endtime' => '0'
261 ];
262 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
263 [
264 'example.com' => [
265 'flat' => [
266 'special/page/' =>
267 [
268 1 => $row1,
269 ]
270 ],
271 'respect_query_parameters' => [
272 'special/page?key=998877' => [
273 1 => $row2,
274 ],
275 ],
276 ],
277 ]
278 );
279
280 $result = $this->redirectService->matchRedirect('example.com', 'special/page', 'key=998877');
281
282 self::assertSame($row2, $result);
283 }
284
285 /**
286 * @test
287 */
288 public function matchRedirectReturnsRedirectSpecificToDomainOnFlatMatchIfSpecificAndNonSpecificExist()
289 {
290 $row1 = [
291 'target' => 'https://example.com',
292 'force_https' => '0',
293 'keep_query_parameters' => '0',
294 'target_statuscode' => '307',
295 'disabled' => '0',
296 'starttime' => '0',
297 'endtime' => '0'
298 ];
299 $row2 = [
300 'target' => 'https://example.net',
301 'force_https' => '0',
302 'keep_query_parameters' => '0',
303 'target_statuscode' => '307',
304 'disabled' => '0',
305 'starttime' => '0',
306 'endtime' => '0'
307 ];
308 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
309 [
310 'example.com' => [
311 'flat' => [
312 'foo/' => [
313 1 => $row1,
314 ],
315 ],
316 ],
317 '*' => [
318 'flat' => [
319 'foo/' => [
320 2 => $row2,
321 ],
322 ],
323 ],
324 ]
325 );
326
327 $result = $this->redirectService->matchRedirect('example.com', 'foo');
328
329 self::assertSame($row1, $result);
330 }
331
332 /**
333 * @test
334 */
335 public function matchRedirectReturnsRedirectOnRegexMatch()
336 {
337 $row = [
338 'target' => 'https://example.com',
339 'force_https' => '0',
340 'keep_query_parameters' => '0',
341 'target_statuscode' => '307',
342 'disabled' => '0',
343 'starttime' => '0',
344 'endtime' => '0'
345 ];
346 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
347 [
348 'example.com' => [
349 'regexp' => [
350 '/f.*?/' => [
351 1 => $row,
352 ],
353 ],
354 ],
355 ]
356 );
357
358 $result = $this->redirectService->matchRedirect('example.com', 'foo');
359
360 self::assertSame($row, $result);
361 }
362
363 /**
364 * @test
365 */
366 public function matchRedirectReturnsOnlyActiveRedirects()
367 {
368 $row1 = [
369 'target' => 'https://example.com',
370 'force_https' => '0',
371 'keep_query_parameters' => '0',
372 'target_statuscode' => '307',
373 'starttime' => '0',
374 'endtime' => '0',
375 'disabled' => '1'
376 ];
377 $row2 = [
378 'target' => 'https://example.net',
379 'force_https' => '0',
380 'keep_query_parameters' => '0',
381 'target_statuscode' => '307',
382 'starttime' => '0',
383 'endtime' => '0',
384 'disabled' => '0'
385 ];
386 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
387 [
388 'example.com' => [
389 'flat' => [
390 'foo/' => [
391 1 => $row1,
392 2 => $row2
393 ],
394 ],
395 ],
396 ]
397 );
398
399 $result = $this->redirectService->matchRedirect('example.com', 'foo');
400
401 self::assertSame($row2, $result);
402 }
403
404 /**
405 * @test
406 */
407 public function getTargetUrlReturnsNullIfUrlCouldNotBeResolved()
408 {
409 $this->linkServiceProphecy->resolve(Argument::any())->willThrow(new InvalidPathException('', 1516531195));
410
411 $result = $this->redirectService->getTargetUrl(['target' => 'invalid'], [], new Site('dummy', 13, []));
412
413 self::assertNull($result);
414 }
415
416 /**
417 * @test
418 */
419 public function getTargetUrlReturnsUrlForTypeUrl()
420 {
421 $redirectTargetMatch = [
422 'target' => 'https://example.com',
423 'force_https' => '0',
424 'keep_query_parameters' => '0'
425 ];
426 $linkDetails = [
427 'type' => LinkService::TYPE_URL,
428 'url' => 'https://example.com/'
429 ];
430 $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
431
432 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new Site('dummy', 13, []));
433
434 $uri = new Uri('https://example.com/');
435 self::assertEquals($uri, $result);
436 }
437
438 /**
439 * @test
440 */
441 public function getTargetUrlReturnsUrlForTypeFile()
442 {
443 $fileProphecy = $this->prophesize(File::class);
444 $fileProphecy->getPublicUrl()->willReturn('https://example.com/file.txt');
445 $redirectTargetMatch = [
446 'target' => 'https://example.com',
447 'force_https' => '0',
448 'keep_query_parameters' => '0',
449 ];
450 $linkDetails = [
451 'type' => LinkService::TYPE_FILE,
452 'file' => $fileProphecy->reveal()
453 ];
454 $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
455
456 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new Site('dummy', 13, []));
457
458 $uri = new Uri('https://example.com/file.txt');
459 self::assertEquals($uri, $result);
460 }
461
462 /**
463 * @test
464 */
465 public function getTargetUrlReturnsUrlForTypeFolder()
466 {
467 $folderProphecy = $this->prophesize(Folder::class);
468 $folderProphecy->getPublicUrl()->willReturn('https://example.com/folder/');
469 $redirectTargetMatch = [
470 'target' => 'https://example.com',
471 'force_https' => '0',
472 'keep_query_parameters' => '0',
473 ];
474 $folder = $folderProphecy->reveal();
475 $linkDetails = [
476 'type' => LinkService::TYPE_FOLDER,
477 'folder' => $folder
478 ];
479 $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
480
481 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new Site('dummy', 13, []));
482
483 $uri = new Uri('https://example.com/folder/');
484 self::assertEquals($uri, $result);
485 }
486
487 /**
488 * @test
489 */
490 public function getTargetUrlRespectsForceHttps()
491 {
492 $redirectTargetMatch = [
493 'target' => 'https://example.com',
494 'keep_query_parameters' => '0',
495 'force_https' => '1',
496 ];
497 $linkDetails = [
498 'type' => LinkService::TYPE_URL,
499 'url' => 'http://example.com'
500 ];
501 $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
502
503 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new Site('dummy', 13, []));
504
505 $uri = new Uri('https://example.com');
506 self::assertEquals($uri, $result);
507 }
508
509 /**
510 * @test
511 */
512 public function getTargetUrlAddsExistingQueryParams()
513 {
514 $redirectTargetMatch = [
515 'target' => 'https://example.com',
516 'force_https' => '0',
517 'keep_query_parameters' => '1'
518 ];
519 $linkDetails = [
520 'type' => LinkService::TYPE_URL,
521 'url' => 'https://example.com/?foo=1&bar=2'
522 ];
523 $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
524
525 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, ['bar' => 3, 'baz' => 4], new Site('dummy', 13, []));
526
527 $uri = new Uri('https://example.com/?bar=2&baz=4&foo=1');
528 self::assertEquals($uri, $result);
529 }
530 }