[TASK] Add frontend functional tests for pages having slugs
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Functional / SiteHandling / SlugSiteRequestTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Tests\Functional\SiteHandling;
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 TYPO3\CMS\Core\Core\Bootstrap;
19 use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
20 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
21 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
22 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
23 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequestContext;
24 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\ResponseContent;
25
26 /**
27 * Test case for frontend requests having site handling configured
28 */
29 class SlugSiteRequestTest extends AbstractTestCase
30 {
31 /**
32 * @var string
33 */
34 private $siteTitle = 'A Company that Manufactures Everything Inc';
35
36 /**
37 * @var InternalRequestContext
38 */
39 private $internalRequestContext;
40
41 public static function setUpBeforeClass()
42 {
43 parent::setUpBeforeClass();
44 static::initializeDatabaseSnapshot();
45 }
46
47 public static function tearDownAfterClass()
48 {
49 static::destroyDatabaseSnapshot();
50 parent::tearDownAfterClass();
51 }
52
53 protected function setUp()
54 {
55 parent::setUp();
56
57 // these settings are forwarded to the frontend sub-request as well
58 $this->internalRequestContext = (new InternalRequestContext())
59 ->withGlobalSettings(['TYPO3_CONF_VARS' => static::TYPO3_CONF_VARS]);
60
61 $this->withDatabaseSnapshot(function () {
62 $this->setUpDatabase();
63 });
64 }
65
66 protected function setUpDatabase()
67 {
68 $backendUser = $this->setUpBackendUserFromFixture(1);
69 Bootstrap::initializeLanguageObject();
70
71 $scenarioFile = __DIR__ . '/Fixtures/SlugScenario.yaml';
72 $factory = DataHandlerFactory::fromYamlFile($scenarioFile);
73 $writer = DataHandlerWriter::withBackendUser($backendUser);
74 $writer->invokeFactory($factory);
75 static::failIfArrayIsNotEmpty(
76 $writer->getErrors()
77 );
78
79 $this->setUpFrontendRootPage(
80 1000,
81 [
82 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.typoscript',
83 'typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/JsonRenderer.typoscript',
84 ],
85 [
86 'title' => 'ACME Root',
87 'sitetitle' => $this->siteTitle,
88 ]
89 );
90 }
91
92 protected function tearDown()
93 {
94 unset($this->internalRequestContext);
95 parent::tearDown();
96 }
97
98 /**
99 * @return array
100 */
101 public function requestsAreRedirectedWithoutHavingDefaultSiteLanguageDataProvider(): array
102 {
103 $domainPaths = [
104 'https://website.local/',
105 'https://website.local/?',
106 ];
107
108 return $this->wrapInArray(
109 $this->keysFromValues($domainPaths)
110 );
111 }
112
113 /**
114 * @param string $uri
115 *
116 * @test
117 * @dataProvider requestsAreRedirectedWithoutHavingDefaultSiteLanguageDataProvider
118 */
119 public function requestsAreRedirectedWithoutHavingDefaultSiteLanguage(string $uri)
120 {
121 $this->writeSiteConfiguration(
122 'website-local',
123 $this->buildSiteConfiguration(1000, 'https://website.local/')
124 );
125
126 $expectedStatusCode = 307;
127 $expectedHeaders = ['location' => ['https://website.local/welcome']];
128
129 $response = $this->executeFrontendRequest(
130 new InternalRequest($uri),
131 $this->internalRequestContext
132 );
133 static::assertSame($expectedStatusCode, $response->getStatusCode());
134 static::assertSame($expectedHeaders, $response->getHeaders());
135 }
136
137 /**
138 * @return array
139 */
140 public function shortcutsAreRedirectedDataProvider(): array
141 {
142 $domainPaths = [
143 'https://website.local/',
144 'https://website.local/?',
145 'https://website.local/welcome',
146 ];
147
148 return $this->wrapInArray(
149 $this->keysFromValues($domainPaths)
150 );
151 }
152
153 /**
154 * @param string $uri
155 *
156 * @test
157 * @dataProvider shortcutsAreRedirectedDataProvider
158 */
159 public function shortcutsAreRedirectedToDefaultSiteLanguage(string $uri)
160 {
161 $this->writeSiteConfiguration(
162 'website-local',
163 $this->buildSiteConfiguration(1000, 'https://website.local/'),
164 [
165 $this->buildDefaultLanguageConfiguration('EN', '/en-en/'),
166 ]
167 );
168
169 $expectedStatusCode = 307;
170 $expectedHeaders = [
171 'location' => ['https://website.local/en-en/'],
172 ];
173
174 $response = $this->executeFrontendRequest(
175 new InternalRequest($uri),
176 $this->internalRequestContext
177 );
178 static::assertSame($expectedStatusCode, $response->getStatusCode());
179 static::assertSame($expectedHeaders, $response->getHeaders());
180 }
181
182 /**
183 * @param string $uri
184 *
185 * @test
186 * @dataProvider shortcutsAreRedirectedDataProvider
187 */
188 public function shortcutsAreRedirectedAndRenderFirstSubPage(string $uri)
189 {
190 $this->writeSiteConfiguration(
191 'website-local',
192 $this->buildSiteConfiguration(1000, 'https://website.local/'),
193 [
194 $this->buildDefaultLanguageConfiguration('EN', '/en-en/'),
195 ]
196 );
197
198 $expectedStatusCode = 200;
199 $expectedPageTitle = 'EN: Welcome';
200
201 $response = $this->executeFrontendRequest(
202 new InternalRequest($uri),
203 $this->internalRequestContext,
204 true
205 );
206 $responseStructure = ResponseContent::fromString(
207 (string)$response->getBody()
208 );
209
210 static::assertSame(
211 $expectedStatusCode,
212 $response->getStatusCode()
213 );
214 static::assertSame(
215 $this->siteTitle,
216 $responseStructure->getScopePath('template/sitetitle')
217 );
218 static::assertSame(
219 $expectedPageTitle,
220 $responseStructure->getScopePath('page/title')
221 );
222 }
223
224 /**
225 * @test
226 */
227 public function invalidSiteResultsInNotFoundResponse()
228 {
229 $this->writeSiteConfiguration(
230 'website-local',
231 $this->buildSiteConfiguration(1000, 'https://website.local/'),
232 [
233 $this->buildDefaultLanguageConfiguration('EN', '/en-en/'),
234 ],
235 $this->buildErrorHandlingConfiguration('Fluid', [404])
236 );
237
238 // @todo Expected page not found response (404) instead
239 $this->expectException(\TYPO3\CMS\Core\Error\Http\ServiceUnavailableException::class);
240 $this->expectExceptionCode(1294587218);
241
242 $uri = 'https://website.other/any/invalid/slug';
243 $this->executeFrontendRequest(
244 new InternalRequest($uri),
245 $this->internalRequestContext
246 );
247 }
248
249 /**
250 * @test
251 */
252 public function invalidSlugOutsideSiteLanguageResultsInNotFoundResponse()
253 {
254 $this->writeSiteConfiguration(
255 'website-local',
256 $this->buildSiteConfiguration(1000, 'https://website.local/'),
257 [
258 $this->buildDefaultLanguageConfiguration('EN', '/en-en/')
259 ],
260 $this->buildErrorHandlingConfiguration('Fluid', [404])
261 );
262
263 $expectedStatusCode = 307;
264 $expectedHeaders = ['location' => ['https://website.local/en-en/']];
265
266 $uri = 'https://website.local/any/invalid/slug';
267 $response = $this->executeFrontendRequest(
268 new InternalRequest($uri),
269 $this->internalRequestContext
270 );
271
272 static::assertSame(
273 $expectedStatusCode,
274 $response->getStatusCode()
275 );
276 static::assertSame(
277 $expectedHeaders,
278 $response->getHeaders()
279 );
280 // @todo Expected page not found response (404) instead
281 // static::assertContains(
282 // 'message: The requested page does not exist',
283 // (string)$response->getBody()
284 // );
285 }
286 /**
287 * @test
288 */
289 public function invalidSlugInsideSiteLanguageResultsInNotFoundResponse()
290 {
291 $this->writeSiteConfiguration(
292 'website-local',
293 $this->buildSiteConfiguration(1000, 'https://website.local/'),
294 [
295 $this->buildDefaultLanguageConfiguration('EN', '/en-en/')
296 ],
297 $this->buildErrorHandlingConfiguration('Fluid', [404])
298 );
299
300 $uri = 'https://website.local/en-en/any/invalid/slug';
301 $response = $this->executeFrontendRequest(
302 new InternalRequest($uri),
303 $this->internalRequestContext
304 );
305
306 static::assertSame(
307 404,
308 $response->getStatusCode()
309 );
310 static::assertContains(
311 'message: The requested page does not exist',
312 (string)$response->getBody()
313 );
314 }
315
316 /**
317 * @return array
318 */
319 public function pageIsRenderedWithPathsDataProvider(): array
320 {
321 $domainPaths = [
322 'https://website.local/en-en/welcome',
323 'https://website.local/fr-fr/bienvenue',
324 'https://website.local/fr-ca/bienvenue',
325 ];
326
327 return array_map(
328 function (string $uri) {
329 if (strpos($uri, '/fr-fr/') !== false) {
330 $expectedPageTitle = 'FR: Welcome';
331 } elseif (strpos($uri, '/fr-ca/') !== false) {
332 $expectedPageTitle = 'FR-CA: Welcome';
333 } else {
334 $expectedPageTitle = 'EN: Welcome';
335 }
336 return [$uri, $expectedPageTitle];
337 },
338 $this->keysFromValues($domainPaths)
339 );
340 }
341
342 /**
343 * @param string $uri
344 * @param string $expectedPageTitle
345 *
346 * @test
347 * @dataProvider pageIsRenderedWithPathsDataProvider
348 */
349 public function pageIsRenderedWithPaths(string $uri, string $expectedPageTitle)
350 {
351 $this->writeSiteConfiguration(
352 'website-local',
353 $this->buildSiteConfiguration(1000, 'https://website.local/'),
354 [
355 $this->buildDefaultLanguageConfiguration('EN', '/en-en/'),
356 $this->buildLanguageConfiguration('FR', '/fr-fr/', ['EN']),
357 $this->buildLanguageConfiguration('FR-CA', '/fr-ca/', ['FR', 'EN']),
358 ]
359 );
360
361 $response = $this->executeFrontendRequest(
362 new InternalRequest($uri),
363 $this->internalRequestContext
364 );
365 $responseStructure = ResponseContent::fromString(
366 (string)$response->getBody()
367 );
368
369 static::assertSame(
370 200,
371 $response->getStatusCode()
372 );
373 static::assertSame(
374 $this->siteTitle,
375 $responseStructure->getScopePath('template/sitetitle')
376 );
377 static::assertSame(
378 $expectedPageTitle,
379 $responseStructure->getScopePath('page/title')
380 );
381 }
382
383 /**
384 * @return array
385 */
386 public function pageIsRenderedWithDomainsDataProvider(): array
387 {
388 $domainPaths = [
389 'https://website.us/welcome',
390 'https://website.fr/bienvenue',
391 'https://website.ca/bienvenue',
392 ];
393
394 return array_map(
395 function (string $uri) {
396 if (strpos($uri, '.fr/') !== false) {
397 $expectedPageTitle = 'FR: Welcome';
398 } elseif (strpos($uri, '.ca/') !== false) {
399 $expectedPageTitle = 'FR-CA: Welcome';
400 } else {
401 $expectedPageTitle = 'EN: Welcome';
402 }
403 return [$uri, $expectedPageTitle];
404 },
405 $this->keysFromValues($domainPaths)
406 );
407 }
408
409 /**
410 * @param string $uri
411 * @param string $expectedPageTitle
412 *
413 * @test
414 * @dataProvider pageIsRenderedWithDomainsDataProvider
415 */
416 public function pageIsRenderedWithDomains(string $uri, string $expectedPageTitle)
417 {
418 $this->writeSiteConfiguration(
419 'website-local',
420 $this->buildSiteConfiguration(1000, 'https://website.local/'),
421 [
422 $this->buildDefaultLanguageConfiguration('EN', 'https://website.us/'),
423 $this->buildLanguageConfiguration('FR', 'https://website.fr/', ['EN']),
424 $this->buildLanguageConfiguration('FR-CA', 'https://website.ca/', ['FR', 'EN']),
425 ]
426 );
427
428 $response = $this->executeFrontendRequest(
429 new InternalRequest($uri),
430 $this->internalRequestContext
431 );
432 $responseStructure = ResponseContent::fromString(
433 (string)$response->getBody()
434 );
435
436 static::assertSame(
437 200,
438 $response->getStatusCode()
439 );
440 static::assertSame(
441 $this->siteTitle,
442 $responseStructure->getScopePath('template/sitetitle')
443 );
444 static::assertSame(
445 $expectedPageTitle,
446 $responseStructure->getScopePath('page/title')
447 );
448 }
449
450 /**
451 * @return array
452 */
453 public function restrictedPageIsRenderedDataProvider(): array
454 {
455 $instructions = [
456 // frontend user 1
457 ['https://website.local/my-acme/whitepapers', 1, 'Whitepapers'],
458 ['https://website.local/my-acme/whitepapers/products', 1, 'Products'],
459 ['https://website.local/my-acme/whitepapers/solutions', 1, 'Solutions'],
460 // frontend user 2
461 ['https://website.local/my-acme/whitepapers', 2, 'Whitepapers'],
462 ['https://website.local/my-acme/whitepapers/products', 2, 'Products'],
463 ['https://website.local/my-acme/whitepapers/research', 2, 'Research'],
464 ['https://website.local/my-acme/forecasts', 2, 'Forecasts'],
465 ['https://website.local/my-acme/forecasts/current-year', 2, 'Current Year'],
466 // frontend user 3
467 ['https://website.local/my-acme/whitepapers', 3, 'Whitepapers'],
468 ['https://website.local/my-acme/whitepapers/products', 3, 'Products'],
469 ['https://website.local/my-acme/whitepapers/solutions', 3, 'Solutions'],
470 ['https://website.local/my-acme/whitepapers/research', 3, 'Research'],
471 ['https://website.local/my-acme/forecasts', 3, 'Forecasts'],
472 ['https://website.local/my-acme/forecasts/current-year', 3, 'Current Year'],
473 ];
474
475 return $this->keysFromTemplate($instructions, '%1$s (user:%2$s)');
476 }
477
478 /**
479 * @param string $uri
480 * @param int $frontendUserId
481 * @param string $expectedPageTitle
482 *
483 * @test
484 * @dataProvider restrictedPageIsRenderedDataProvider
485 */
486 public function restrictedPageIsRendered(string $uri, int $frontendUserId, string $expectedPageTitle)
487 {
488 $this->writeSiteConfiguration(
489 'website-local',
490 $this->buildSiteConfiguration(1000, 'https://website.local/')
491 );
492
493 $response = $this->executeFrontendRequest(
494 new InternalRequest($uri),
495 $this->internalRequestContext
496 ->withFrontendUserId($frontendUserId)
497 );
498 $responseStructure = ResponseContent::fromString(
499 (string)$response->getBody()
500 );
501
502 static::assertSame(
503 200,
504 $response->getStatusCode()
505 );
506 static::assertSame(
507 $this->siteTitle,
508 $responseStructure->getScopePath('template/sitetitle')
509 );
510 static::assertSame(
511 $expectedPageTitle,
512 $responseStructure->getScopePath('page/title')
513 );
514 }
515
516 /**
517 * @return array
518 */
519 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider(): array
520 {
521 $instructions = [
522 // no frontend user given
523 ['https://website.local/my-acme/whitepapers', 0],
524 // ['https://website.local/my-acme/whitepapers/products', 0], // @todo extendToSubpages currently missing
525 ['https://website.local/my-acme/whitepapers/solutions', 0],
526 ['https://website.local/my-acme/whitepapers/research', 0],
527 ['https://website.local/my-acme/forecasts', 0],
528 // ['https://website.local/my-acme/forecasts/current-year', 0], // @todo extendToSubpages currently missing
529 // frontend user 1
530 ['https://website.local/my-acme/whitepapers/research', 1],
531 ['https://website.local/my-acme/forecasts', 1],
532 // ['https://website.local/my-acme/forecasts/current-year', 1], // @todo extendToSubpages currently missing
533 // frontend user 2
534 ['https://website.local/my-acme/whitepapers/solutions', 2],
535 ];
536
537 return $this->keysFromTemplate($instructions, '%1$s (user:%2$s)');
538 }
539
540 /**
541 * @param string $uri
542 * @param int $frontendUserId
543 *
544 * @test
545 * @dataProvider restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider
546 */
547 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorWithoutHavingErrorHandling(string $uri, int $frontendUserId)
548 {
549 $this->writeSiteConfiguration(
550 'website-local',
551 $this->buildSiteConfiguration(1000, 'https://website.local/')
552 );
553
554 $response = $this->executeFrontendRequest(
555 new InternalRequest($uri),
556 $this->internalRequestContext
557 ->withFrontendUserId($frontendUserId)
558 );
559
560 static::assertSame(
561 403,
562 $response->getStatusCode()
563 );
564 static::assertThat(
565 (string)$response->getBody(),
566 static::logicalOr(
567 static::stringContains('Reason: ID was not an accessible page'),
568 static::stringContains('Reason: Subsection was found and not accessible')
569 )
570 );
571 }
572
573 /**
574 * @param string $uri
575 * @param int $frontendUserId
576 *
577 * @test
578 * @dataProvider restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider
579 */
580 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorWithHavingFluidErrorHandling(string $uri, int $frontendUserId)
581 {
582 $this->writeSiteConfiguration(
583 'website-local',
584 $this->buildSiteConfiguration(1000, 'https://website.local/'),
585 [],
586 $this->buildErrorHandlingConfiguration('Fluid', [403])
587 );
588
589 $response = $this->executeFrontendRequest(
590 new InternalRequest($uri),
591 $this->internalRequestContext
592 ->withFrontendUserId($frontendUserId)
593 );
594
595 static::assertSame(
596 403,
597 $response->getStatusCode()
598 );
599 static::assertContains(
600 'reasons: code,fe_group',
601 (string)$response->getBody()
602 );
603 static::assertThat(
604 (string)$response->getBody(),
605 static::logicalOr(
606 static::stringContains('message: ID was not an accessible page'),
607 static::stringContains('message: Subsection was found and not accessible')
608 )
609 );
610 }
611
612 /**
613 * @param string $uri
614 * @param int $frontendUserId
615 *
616 * @test
617 * @dataProvider restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider
618 * @todo Response body cannot be asserted since PageContentErrorHandler::handlePageError executes request via HTTP (not internally)
619 */
620 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorWithHavingPageErrorHandling(string $uri, int $frontendUserId)
621 {
622 $this->markTestSkipped('Skipped until PageContentErrorHandler::handlePageError does not use HTTP anymore');
623
624 $this->writeSiteConfiguration(
625 'website-local',
626 $this->buildSiteConfiguration(1000, 'https://website.local/'),
627 [],
628 $this->buildErrorHandlingConfiguration('Page', [403])
629 );
630
631 $response = $this->executeFrontendRequest(
632 new InternalRequest($uri),
633 $this->internalRequestContext
634 ->withFrontendUserId($frontendUserId)
635 );
636
637 static::assertSame(
638 403,
639 $response->getStatusCode()
640 );
641 }
642
643 /**
644 * @param string $uri
645 * @param int $frontendUserId
646 *
647 * @test
648 * @dataProvider restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider
649 */
650 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorWithHavingPhpErrorHandling(string $uri, int $frontendUserId)
651 {
652 $this->writeSiteConfiguration(
653 'website-local',
654 $this->buildSiteConfiguration(1000, 'https://website.local/'),
655 [],
656 $this->buildErrorHandlingConfiguration('PHP', [403])
657 );
658
659 $response = $this->executeFrontendRequest(
660 new InternalRequest($uri),
661 $this->internalRequestContext
662 ->withFrontendUserId($frontendUserId)
663 );
664 $json = json_decode((string)$response->getBody(), true);
665
666 static::assertSame(
667 403,
668 $response->getStatusCode()
669 );
670 static::assertThat(
671 $json['message'] ?? null,
672 static::logicalOr(
673 static::identicalTo('ID was not an accessible page'),
674 static::identicalTo('Subsection was found and not accessible')
675 )
676 );
677 }
678
679 /**
680 * @return array
681 */
682 public function pageRenderingStopsWithInvalidCacheHashDataProvider(): array
683 {
684 $domainPaths = [
685 'https://website.local/',
686 ];
687
688 $queries = [
689 '',
690 'welcome',
691 ];
692
693 $customQueries = [
694 '?testing[value]=1',
695 '?testing[value]=1&cHash=',
696 '?testing[value]=1&cHash=WRONG',
697 ];
698
699 return $this->wrapInArray(
700 $this->keysFromValues(
701 $this->meltStrings([$domainPaths, $queries, $customQueries])
702 )
703 );
704 }
705
706 /**
707 * @param string $uri
708 *
709 * @test
710 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
711 */
712 public function pageRequestThrowsExceptionWithInvalidCacheHashWithoutHavingErrorHandling(string $uri)
713 {
714 $this->writeSiteConfiguration(
715 'website-local',
716 $this->buildSiteConfiguration(1000, 'https://website.local/')
717 );
718
719 $this->expectExceptionCode(1518472189);
720 $this->expectException(PageNotFoundException::class);
721
722 $this->executeFrontendRequest(
723 new InternalRequest($uri),
724 $this->internalRequestContext
725 );
726 }
727
728 /**
729 * @param string $uri
730 *
731 * @test
732 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
733 */
734 public function pageRequestSendsNotFoundResponseWithInvalidCacheHash(string $uri)
735 {
736 $response = $this->executeFrontendRequest(
737 new InternalRequest($uri),
738 $this->internalRequestContext->withMergedGlobalSettings([
739 'TYPO3_CONF_VARS' => [
740 'FE' => [
741 'pageNotFound_handling' => 'READFILE:typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/PageError.txt',
742 ]
743 ]
744 ])
745 );
746
747 static::assertSame(
748 404,
749 $response->getStatusCode()
750 );
751 static::assertThat(
752 (string)$response->getBody(),
753 static::logicalOr(
754 static::stringContains('reason: Request parameters could not be validated (&amp;cHash empty)'),
755 static::stringContains('reason: Request parameters could not be validated (&amp;cHash comparison failed)')
756 )
757 );
758 }
759
760 /**
761 * @param string $uri
762 *
763 * @test
764 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
765 */
766 public function pageRequestSendsNotFoundResponseWithInvalidCacheHashWithHavingFluidErrorHandling(string $uri)
767 {
768 $this->writeSiteConfiguration(
769 'website-local',
770 $this->buildSiteConfiguration(1000, 'https://website.local/'),
771 [],
772 $this->buildErrorHandlingConfiguration('Fluid', [404])
773 );
774
775 $response = $this->executeFrontendRequest(
776 new InternalRequest($uri),
777 $this->internalRequestContext
778 );
779
780 static::assertSame(
781 404,
782 $response->getStatusCode()
783 );
784 static::assertThat(
785 (string)$response->getBody(),
786 static::logicalOr(
787 static::stringContains('message: Request parameters could not be validated (&amp;cHash empty)'),
788 static::stringContains('message: Request parameters could not be validated (&amp;cHash comparison failed)')
789 )
790 );
791 }
792
793 /**
794 * @param string $uri
795 *
796 * @test
797 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
798 * @todo Response body cannot be asserted since PageContentErrorHandler::handlePageError executes request via HTTP (not internally)
799 */
800 public function pageRequestSendsNotFoundResponseWithInvalidCacheHashWithHavingPageErrorHandling(string $uri)
801 {
802 $this->markTestSkipped('Skipped until PageContentErrorHandler::handlePageError does not use HTTP anymore');
803
804 $this->writeSiteConfiguration(
805 'website-local',
806 $this->buildSiteConfiguration(1000, 'https://website.local/'),
807 [],
808 $this->buildErrorHandlingConfiguration('Page', [404])
809 );
810
811 $response = $this->executeFrontendRequest(
812 new InternalRequest($uri),
813 $this->internalRequestContext
814 );
815
816 static::assertSame(
817 404,
818 $response->getStatusCode()
819 );
820 }
821
822 /**
823 * @param string $uri
824 *
825 * @test
826 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
827 */
828 public function pageRequestSendsNotFoundResponseWithInvalidCacheHashWithHavingPhpErrorHandling(string $uri)
829 {
830 $this->writeSiteConfiguration(
831 'website-local',
832 $this->buildSiteConfiguration(1000, 'https://website.local/'),
833 [],
834 $this->buildErrorHandlingConfiguration('PHP', [404])
835 );
836
837 $response = $this->executeFrontendRequest(
838 new InternalRequest($uri),
839 $this->internalRequestContext
840 );
841 $json = json_decode((string)$response->getBody(), true);
842
843 static::assertSame(
844 404,
845 $response->getStatusCode()
846 );
847 static::assertThat(
848 $json['message'] ?? null,
849 static::logicalOr(
850 static::identicalTo('Request parameters could not be validated (&cHash empty)'),
851 static::identicalTo('Request parameters could not be validated (&cHash comparison failed)')
852 )
853 );
854 }
855
856 /**
857 * @return array
858 */
859 public function pageIsRenderedWithValidCacheHashDataProvider(): array
860 {
861 $domainPaths = [
862 'https://website.local/',
863 ];
864
865 // cHash has been calculated with encryption key set to
866 // '4408d27a916d51e624b69af3554f516dbab61037a9f7b9fd6f81b4d3bedeccb6'
867 $queries = [
868 // @todo Currently fails since cHash is verified after(!) redirect to page 1100
869 // '?cHash=7d1f13fa91159dac7feb3c824936b39d',
870 // '?cHash=7d1f13fa91159dac7feb3c824936b39d',
871 'welcome?cHash=f42b850e435f0cedd366f5db749fc1af',
872 ];
873
874 $customQueries = [
875 '&testing[value]=1',
876 ];
877
878 $dataSet = $this->wrapInArray(
879 $this->keysFromValues(
880 $this->meltStrings([$domainPaths, $queries, $customQueries])
881 )
882 );
883
884 return $dataSet;
885 }
886
887 /**
888 * @param string $uri
889 *
890 * @test
891 * @dataProvider pageIsRenderedWithValidCacheHashDataProvider
892 */
893 public function pageIsRenderedWithValidCacheHash($uri)
894 {
895 $this->writeSiteConfiguration(
896 'website-local',
897 $this->buildSiteConfiguration(1000, 'https://website.local/')
898 );
899
900 $response = $this->executeFrontendRequest(
901 new InternalRequest($uri),
902 $this->internalRequestContext
903 );
904 $responseStructure = ResponseContent::fromString(
905 (string)$response->getBody()
906 );
907 static::assertSame(
908 '1',
909 $responseStructure->getScopePath('getpost/testing.value')
910 );
911 }
912 }