7d2621017c64d6ab2c88e20d8ba174f671884943
[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\Utility\GeneralUtility;
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 * Subject is not notice free, disable E_NOTICES
35 */
36 protected static $suppressNotices = true;
37
38 /**
39 * @var RedirectCacheService|ObjectProphecy
40 */
41 protected $redirectCacheServiceProphecy;
42
43 /**
44 * @var RedirectService
45 */
46 protected $redirectService;
47
48 protected $singletonInstances = [];
49
50 protected function setUp()
51 {
52 parent::setUp();
53 $this->singletonInstances = GeneralUtility::getSingletonInstances();
54 $loggerProphecy = $this->prophesize(LoggerInterface::class);
55 $this->redirectCacheServiceProphecy = $this->prophesize(RedirectCacheService::class);
56 $this->redirectService = new RedirectService();
57 $this->redirectService->setLogger($loggerProphecy->reveal());
58 }
59
60 /**
61 * @test
62 */
63 public function matchRedirectReturnsNullIfNoRedirectsExist()
64 {
65 $this->redirectCacheServiceProphecy->getRedirects()->willReturn([]);
66 GeneralUtility::addInstance(RedirectCacheService::class, $this->redirectCacheServiceProphecy->reveal());
67
68 $result = $this->redirectService->matchRedirect('example.com', 'foo');
69
70 self::assertNull($result);
71 }
72
73 /**
74 * @test
75 */
76 public function matchRedirectReturnsRedirectOnFlatMatch()
77 {
78 $row = [
79 'target' => 'https://example.com',
80 'force_https' => '0',
81 'keep_query_parameters' => '0',
82 'target_statuscode' => '307'
83 ];
84 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
85 [
86 'example.com' => [
87 'flat' => [
88 'foo/' => [
89 1 => $row,
90 ],
91 ],
92 ],
93 ]
94 );
95 GeneralUtility::addInstance(RedirectCacheService::class, $this->redirectCacheServiceProphecy->reveal());
96
97 $result = $this->redirectService->matchRedirect('example.com', 'foo');
98
99 self::assertSame($row, $result);
100 }
101
102 /**
103 * @test
104 */
105 public function matchRedirectReturnsRedirectSpecificToDomainOnFlatMatchIfSpecificAndNonSpecificExist()
106 {
107 $row1 = [
108 'target' => 'https://example.com',
109 'force_https' => '0',
110 'keep_query_parameters' => '0',
111 'target_statuscode' => '307'
112 ];
113 $row2 = [
114 'target' => 'https://example.net',
115 'force_https' => '0',
116 'keep_query_parameters' => '0',
117 'target_statuscode' => '307'
118 ];
119 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
120 [
121 'example.com' => [
122 'flat' => [
123 'foo/' => [
124 1 => $row1,
125 ],
126 ],
127 ],
128 '*' => [
129 'flat' => [
130 'foo/' => [
131 2 => $row2,
132 ],
133 ],
134 ],
135 ]
136 );
137 GeneralUtility::addInstance(RedirectCacheService::class, $this->redirectCacheServiceProphecy->reveal());
138
139 $result = $this->redirectService->matchRedirect('example.com', 'foo');
140
141 self::assertSame($row1, $result);
142 }
143
144 /**
145 * @test
146 */
147 public function matchRedirectReturnsRedirectOnRegexMatch()
148 {
149 $row = [
150 'target' => 'https://example.com',
151 'force_https' => '0',
152 'keep_query_parameters' => '0',
153 'target_statuscode' => '307'
154 ];
155 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
156 [
157 'example.com' => [
158 'regexp' => [
159 '/f.*?/' => [
160 1 => $row,
161 ],
162 ],
163 ],
164 ]
165 );
166 GeneralUtility::addInstance(RedirectCacheService::class, $this->redirectCacheServiceProphecy->reveal());
167
168 $result = $this->redirectService->matchRedirect('example.com', 'foo');
169
170 self::assertSame($row, $result);
171 }
172
173 /**
174 * @test
175 */
176 public function matchRedirectReturnsOnlyActiveRedirects()
177 {
178 $row1 = [
179 'target' => 'https://example.com',
180 'force_https' => '0',
181 'keep_query_parameters' => '0',
182 'target_statuscode' => '307',
183 'disabled' => '1'
184 ];
185 $row2 = [
186 'target' => 'https://example.net',
187 'force_https' => '0',
188 'keep_query_parameters' => '0',
189 'target_statuscode' => '307',
190 'disabled' => '0'
191 ];
192 $this->redirectCacheServiceProphecy->getRedirects()->willReturn(
193 [
194 'example.com' => [
195 'flat' => [
196 'foo/' => [
197 1 => $row1,
198 2 => $row2
199 ],
200 ],
201 ],
202 ]
203 );
204 GeneralUtility::addInstance(RedirectCacheService::class, $this->redirectCacheServiceProphecy->reveal());
205
206 $result = $this->redirectService->matchRedirect('example.com', 'foo');
207
208 self::assertSame($row2, $result);
209 }
210
211 /**
212 * @test
213 */
214 public function getTargetUrlReturnsNullIfUrlCouldNotBeResolved()
215 {
216 $linkServiceProphecy = $this->prophesize(LinkService::class);
217 $linkServiceProphecy->resolve(Argument::any())->willThrow(new InvalidPathException('', 1516531195));
218 GeneralUtility::setSingletonInstance(LinkService::class, $linkServiceProphecy->reveal());
219
220 $result = $this->redirectService->getTargetUrl(['target' => 'invalid'], []);
221
222 self::assertNull($result);
223 }
224
225 /**
226 * @test
227 */
228 public function getTargetUrlReturnsUrlForTypeUrl()
229 {
230 $linkServiceProphecy = $this->prophesize(LinkService::class);
231 $redirectTargetMatch = [
232 'target' => 'https://example.com',
233 ];
234 $linkDetails = [
235 'type' => LinkService::TYPE_URL,
236 'url' => 'https://example.com/'
237 ];
238 $linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
239 GeneralUtility::setSingletonInstance(LinkService::class, $linkServiceProphecy->reveal());
240
241 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, []);
242
243 $uri = new Uri('https://example.com/');
244 self::assertEquals($uri, $result);
245 }
246
247 /**
248 * @test
249 */
250 public function getTargetUrlReturnsUrlForTypeFile()
251 {
252 $linkServiceProphecy = $this->prophesize(LinkService::class);
253 $fileProphecy = $this->prophesize(File::class);
254 $fileProphecy->getPublicUrl()->willReturn('https://example.com/file.txt');
255 $redirectTargetMatch = [
256 'target' => 'https://example.com',
257 ];
258 $linkDetails = [
259 'type' => LinkService::TYPE_FILE,
260 'file' => $fileProphecy->reveal()
261 ];
262 $linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
263 GeneralUtility::setSingletonInstance(LinkService::class, $linkServiceProphecy->reveal());
264
265 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, []);
266
267 $uri = new Uri('https://example.com/file.txt');
268 self::assertEquals($uri, $result);
269 }
270
271 /**
272 * @test
273 */
274 public function getTargetUrlReturnsUrlForTypeFolder()
275 {
276 $linkServiceProphecy = $this->prophesize(LinkService::class);
277 $folderProphecy = $this->prophesize(Folder::class);
278 $folderProphecy->getPublicUrl()->willReturn('https://example.com/folder/');
279 $redirectTargetMatch = [
280 'target' => 'https://example.com',
281 ];
282 $folder = $folderProphecy->reveal();
283 $linkDetails = [
284 'type' => LinkService::TYPE_FOLDER,
285 'folder' => $folder
286 ];
287 $linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
288 GeneralUtility::setSingletonInstance(LinkService::class, $linkServiceProphecy->reveal());
289
290 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, []);
291
292 $uri = new Uri('https://example.com/folder/');
293 self::assertEquals($uri, $result);
294 }
295
296 /**
297 * @test
298 */
299 public function getTargetUrlRespectsForceHttps()
300 {
301 $linkServiceProphecy = $this->prophesize(LinkService::class);
302 $redirectTargetMatch = [
303 'target' => 'https://example.com',
304 'force_https' => '1'
305 ];
306 $linkDetails = [
307 'type' => LinkService::TYPE_URL,
308 'url' => 'http://example.com'
309 ];
310 $linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
311 GeneralUtility::setSingletonInstance(LinkService::class, $linkServiceProphecy->reveal());
312
313 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, []);
314
315 $uri = new Uri('https://example.com');
316 self::assertEquals($uri, $result);
317 }
318
319 /**
320 * @test
321 */
322 public function getTargetUrlAddsExistingQueryParams()
323 {
324 $linkServiceProphecy = $this->prophesize(LinkService::class);
325 $redirectTargetMatch = [
326 'target' => 'https://example.com',
327 'keep_query_parameters' => '1'
328 ];
329 $linkDetails = [
330 'type' => LinkService::TYPE_URL,
331 'url' => 'https://example.com/?foo=1&bar=2'
332 ];
333 $linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails);
334 GeneralUtility::setSingletonInstance(LinkService::class, $linkServiceProphecy->reveal());
335
336 $result = $this->redirectService->getTargetUrl($redirectTargetMatch, ['bar' => 3, 'baz' => 4]);
337
338 $uri = new Uri('https://example.com/?bar=2&baz=4&foo=1');
339 self::assertEquals($uri, $result);
340 }
341
342 /**
343 * Tear down
344 */
345 public function tearDown()
346 {
347 GeneralUtility::resetSingletonInstances($this->singletonInstances);
348 parent::tearDown();
349 }
350 }