‪TYPO3CMS  ‪main
SiteBaseRedirectResolverTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use Psr\Http\Message\ResponseInterface;
23 use Psr\Http\Message\ServerRequestInterface;
24 use Psr\Http\Server\RequestHandlerInterface;
34 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
35 
36 final class ‪SiteBaseRedirectResolverTest extends UnitTestCase
37 {
38  protected bool ‪$resetSingletonInstances = true;
39 
40  protected RequestHandlerInterface ‪$siteFoundRequestHandler;
41 
42  protected function ‪setUp(): void
43  {
44  parent::setUp();
45  // A request handler which expects a site to be found.
46  $this->siteFoundRequestHandler = new class () implements RequestHandlerInterface {
47  public function handle(ServerRequestInterface $request): ResponseInterface
48  {
49  $site = $request->getAttribute('site', false);
50  $language = $request->getAttribute('language', false);
51  if ($site && $language) {
52  return new ‪JsonResponse(
53  [
54  'site' => $site->getIdentifier(),
55  'language-id' => $language->getLanguageId(),
56  'language-base' => (string)$language->getBase(),
57  'rootpage' => $site->getRootPageId(),
58  ]
59  );
60  }
61  return new ‪NullResponse();
62  }
63  };
64  }
65 
67  {
68  $site1 = new ‪Site('outside-site', 13, [
69  'base' => 'https://twenty.one/',
70  'languages' => [
71  0 => [
72  'languageId' => 0,
73  'locale' => 'en_US.UTF-8',
74  'base' => '/en/',
75  ],
76  1 => [
77  'languageId' => 1,
78  'locale' => 'fr_CA.UTF-8',
79  'base' => '/fr',
80  ],
81  ],
82  ]);
83  $site2 = new ‪Site('sub-site', 14, [
84  'base' => 'https://twenty.one/mysubsite/',
85  'languages' => [
86  2 => [
87  'languageId' => 2,
88  'locale' => 'it_IT.UTF-8',
89  'base' => '/',
90  ],
91  ],
92  ]);
93 
94  return [
95  'redirect to first language' => [
96  'https://twenty.one/',
97  'https://twenty.one/en/',
98  $site1,
99  null,
100  '',
101  ],
102  'redirect to first language adding the slash' => [
103  'https://twenty.one/en',
104  'https://twenty.one/en/',
105  $site1,
106  null,
107  '',
108  ],
109  'redirect to second language removing a slash' => [
110  'https://twenty.one/fr/',
111  'https://twenty.one/fr',
112  $site1,
113  $site1->getLanguageById(1),
114  '/',
115  ],
116  'redirect to subsite by adding a slash' => [
117  'https://twenty.one/mysubsite',
118  'https://twenty.one/mysubsite/',
119  $site2,
120  null,
121  '',
122  ],
123  'redirect to first language and remove nested arguments' => [
124  'https://twenty.one/?foo[bar]=foobar&bar=foo',
125  'https://twenty.one/en/',
126  $site1,
127  null,
128  '',
129  ],
130  'redirect to second language removing a slash but keeping the nested arguments' => [
131  'https://twenty.one/fr/?foo[bar]=foobar&bar=foo',
132  'https://twenty.one/fr?foo%5Bbar%5D=foobar&bar=foo',
133  $site1,
134  $site1->getLanguageById(1),
135  '/',
136  ],
137  ];
138  }
139 
140  #[DataProvider('doRedirectOnMissingOrSuperfluousRequestUrlDataProvider')]
141  #[Test]
143  string $incomingUrl,
144  string $expectedRedirectUrl,
145  ‪Site $site,
146  ?‪SiteLanguage $language,
147  string $tail
148  ): void {
149  $routeResult = new ‪SiteRouteResult(new ‪Uri($incomingUrl), $site, $language, $tail);
150  $request = new ‪ServerRequest($incomingUrl, 'GET');
151  $request = $request->withAttribute('site', $site);
152  $request = $request->withAttribute('language', $language);
153  $request = $request->withAttribute('routing', $routeResult);
154 
155  $subject = new ‪SiteBaseRedirectResolver();
156  $response = $subject->process($request, $this->siteFoundRequestHandler);
157  self::assertEquals(307, $response->getStatusCode());
158  self::assertEquals($expectedRedirectUrl, $response->getHeader('Location')[0] ?? '');
159  }
160 
162  {
163  return [
164  'disabled site language' => ['https://twenty.one/en/pilots/', 404, 0],
165  'enabled site language' => ['https://twenty.one/fr/pilots/', 200, 1],
166  ];
167  }
168 
169  #[DataProvider('checkIf404IsSiteLanguageIsDisabledInFrontendDataProvider')]
170  #[Test]
172  string ‪$url,
173  int $expectedStatusCode,
174  int $languageId
175  ): void {
176  $site = new ‪Site('mixed-site', 13, [
177  'base' => '/',
178  'errorHandling' => [
179  [
180  'errorCode' => 404,
181  'errorHandler' => 'PHP',
182  'errorPhpClassFQCN' => PhpError::class,
183  ],
184  ],
185  'languages' => [
186  0 => [
187  'languageId' => 0,
188  'locale' => 'en_US.UTF-8',
189  'base' => '/en/',
190  'enabled' => false,
191  ],
192  1 => [
193  'languageId' => 1,
194  'locale' => 'fr_CA.UTF-8',
195  'base' => '/fr/',
196  'enabled' => true,
197  ],
198  ],
199  ]);
200 
201  // Request to default page
202  $request = new ‪ServerRequest(‪$url, 'GET');
203  $request = $request->withAttribute('site', $site);
204  $request = $request->withAttribute('language', $site->getLanguageById($languageId));
205  $subject = new ‪SiteBaseRedirectResolver();
206  $response = $subject->process($request, $this->siteFoundRequestHandler);
207  self::assertEquals($expectedStatusCode, $response->getStatusCode());
208  }
209 
210  public static function ‪doNotRedirectOnBaseWithoutQueryDataProvider(): array
211  {
212  $site1 = new ‪Site('outside-site', 13, [
213  'base' => 'https://twenty.one/',
214  'languages' => [
215  0 => [
216  'languageId' => 0,
217  'locale' => 'en_US.UTF-8',
218  'base' => '/en/',
219  ],
220  1 => [
221  'languageId' => 1,
222  'locale' => 'fr_CA.UTF-8',
223  'base' => '/fr',
224  ],
225  ],
226  ]);
227  return [
228  'no redirect for base' => [
229  'https://twenty.one/en/',
230  $site1,
231  $site1->getLanguageById(0),
232  '',
233  ],
234  'no redirect for base when ID is given' => [
235  'https://twenty.one/index.php?id=2',
236  $site1,
237  $site1->getLanguageById(0),
238  '',
239  ],
240  'no redirect for base and nested arguments' => [
241  'https://twenty.one/en/?foo[bar]=foobar&bar=foo',
242  $site1,
243  $site1->getLanguageById(0),
244  '',
245  ],
246  ];
247  }
248 
249  #[DataProvider('doNotRedirectOnBaseWithoutQueryDataProvider')]
250  #[Test]
252  string $incomingUrl,
253  ‪Site $site,
254  ?‪SiteLanguage $language,
255  string $tail
256  ): void {
257  $routeResult = new ‪SiteRouteResult(new ‪Uri($incomingUrl), $site, $language, $tail);
258  $request = new ‪ServerRequest($incomingUrl, 'GET');
259  $request = $request->withAttribute('site', $site);
260  $request = $request->withAttribute('language', $language);
261  $request = $request->withAttribute('routing', $routeResult);
262 
263  $subject = new ‪SiteBaseRedirectResolver();
264  $response = $subject->process($request, $this->siteFoundRequestHandler);
265  self::assertEquals(200, $response->getStatusCode());
266  }
267 
268  #[Test]
270  {
271  $incomingUrl = 'https://twenty.one/';
272  $site = new ‪Site('random-site', 13, [
273  'base' => 'https://twenty.one/',
274  'languages' => [
275  0 => [
276  'languageId' => 0,
277  'locale' => 'en_US.UTF-8',
278  'base' => '/en/',
279  ],
280  1 => [
281  'languageId' => 1,
282  'locale' => 'fr_FR.UTF-8',
283  'base' => '/fr',
284  ],
285  2 => [
286  'languageId' => 2,
287  'locale' => 'fr_CA.UTF-8',
288  'base' => '/fr_ca',
289  ],
290  ],
291  ]);
292 
293  $routeResult = new ‪SiteRouteResult(new ‪Uri($incomingUrl), $site);
294  $request = new ‪ServerRequest($incomingUrl, 'GET');
295  $request = $request->withAttribute('site', $site);
296  $request = $request->withAttribute('routing', $routeResult);
297 
298  $subject = new ‪SiteBaseRedirectResolver();
299  $response = $subject->process($request, $this->siteFoundRequestHandler);
300  self::assertEquals('https://twenty.one/en/', $response->getHeader('Location')[0]);
301  }
302 
303  #[Test]
305  {
306  $incomingUrl = 'https://twenty.one/';
307  $site = new ‪Site('random-site', 13, [
308  'base' => 'https://twenty.one/',
309  'languages' => [
310  0 => [
311  'languageId' => 0,
312  'enabled' => false,
313  'locale' => 'en_US.UTF-8',
314  'base' => '/en/',
315  ],
316  1 => [
317  'languageId' => 1,
318  'enabled' => false,
319  'locale' => 'fr_FR.UTF-8',
320  'base' => '/fr',
321  ],
322  2 => [
323  'languageId' => 2,
324  'locale' => 'fr_CA.UTF-8',
325  'base' => '/fr_ca',
326  ],
327  ],
328  ]);
329 
330  $routeResult = new ‪SiteRouteResult(new ‪Uri($incomingUrl), $site);
331  $request = new ‪ServerRequest($incomingUrl, 'GET');
332  $request = $request->withAttribute('site', $site);
333  $request = $request->withAttribute('routing', $routeResult);
334 
335  $subject = new ‪SiteBaseRedirectResolver();
336  $response = $subject->process($request, $this->siteFoundRequestHandler);
337  self::assertEquals('https://twenty.one/fr_ca', $response->getHeader('Location')[0]);
338  }
339 }
‪TYPO3\CMS\Frontend\Middleware\SiteBaseRedirectResolver
Definition: SiteBaseRedirectResolver.php:42
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\checkIf404IsSiteLanguageIsDisabledInFrontend
‪checkIf404IsSiteLanguageIsDisabledInFrontend(string $url, int $expectedStatusCode, int $languageId)
Definition: SiteBaseRedirectResolverTest.php:171
‪TYPO3\CMS\Core\Tests\Functional\Fixtures\Frontend\PhpError
Definition: PhpError.php:29
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest
Definition: SiteBaseRedirectResolverTest.php:37
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\$siteFoundRequestHandler
‪RequestHandlerInterface $siteFoundRequestHandler
Definition: SiteBaseRedirectResolverTest.php:40
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\doNotRedirectOnBaseWithoutQueryDataProvider
‪static doNotRedirectOnBaseWithoutQueryDataProvider()
Definition: SiteBaseRedirectResolverTest.php:210
‪TYPO3\CMS\Core\Http\NullResponse
Definition: NullResponse.php:26
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\setUp
‪setUp()
Definition: SiteBaseRedirectResolverTest.php:42
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: SiteBaseRedirectResolverTest.php:38
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\useDefaultLanguageIfNoLanguageIsGiven
‪useDefaultLanguageIfNoLanguageIsGiven()
Definition: SiteBaseRedirectResolverTest.php:269
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:30
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:27
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\doRedirectOnMissingOrSuperfluousRequestUrl
‪doRedirectOnMissingOrSuperfluousRequestUrl(string $incomingUrl, string $expectedRedirectUrl, Site $site, ?SiteLanguage $language, string $tail)
Definition: SiteBaseRedirectResolverTest.php:142
‪TYPO3\CMS\Core\Routing\SiteRouteResult
Definition: SiteRouteResult.php:30
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware
Definition: PageArgumentValidatorTest.php:18
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\useFirstAvailableLanguageIfDefaultLanguageIsNotEnabledAndLanguageIsGiven
‪useFirstAvailableLanguageIfDefaultLanguageIsNotEnabledAndLanguageIsGiven()
Definition: SiteBaseRedirectResolverTest.php:304
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:39
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\doNotRedirectOnBaseWithoutQuery
‪doNotRedirectOnBaseWithoutQuery(string $incomingUrl, Site $site, ?SiteLanguage $language, string $tail)
Definition: SiteBaseRedirectResolverTest.php:251
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\checkIf404IsSiteLanguageIsDisabledInFrontendDataProvider
‪static checkIf404IsSiteLanguageIsDisabledInFrontendDataProvider()
Definition: SiteBaseRedirectResolverTest.php:161
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:28
‪TYPO3\CMS\Frontend\Tests\Unit\Middleware\SiteBaseRedirectResolverTest\doRedirectOnMissingOrSuperfluousRequestUrlDataProvider
‪static doRedirectOnMissingOrSuperfluousRequestUrlDataProvider()
Definition: SiteBaseRedirectResolverTest.php:66