performance: Caching, deferred Tracking & externe Timeouts (H7, M12, M13)
- Navigations-Chrome (CategoryNavigation), Suche- und Newsroom-Seitendaten 300s gecacht; gemeinsamer WebPageCache-Helper (eine TTL/Key-Konvention). - Cache-Poisoning über ?page= geschlossen (Key an Paginator ausgerichtet, kein withQueryString im Cache); Newsrooms cachet nur den ungefilterten Fall und die ersten Seiten (kein unbegrenzter Key-Raum). - Detailseiten schreiben Hits/View-Events deferred nach der Response (M12); OpenAI-/Markt-/USt-ID-Aufrufe mit Connect-Timeouts und begrenzten Retries; Admin-Memo-Caches konsistent invalidiert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5a492926dd
commit
23cbd0bfe2
18 changed files with 419 additions and 39 deletions
|
|
@ -94,6 +94,117 @@ test('category page data is cached per portal, language and slug but 404s are no
|
|||
expect(Cache::has('web.category.businessportal24.de.gibt-es-nicht'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('search sidebar data is cached per portal and language', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/de/suche')->assertSuccessful();
|
||||
|
||||
expect(Cache::has('web.search.most-read.businessportal24-both.de'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('newsroom detail data is cached per portal language slug and page', function () {
|
||||
/** @var TestCase $this */
|
||||
$company = Company::factory()->businessportal24()->create([
|
||||
'slug' => 'cache-newsroom',
|
||||
'is_active' => true,
|
||||
]);
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->create([
|
||||
'company_id' => $company->id,
|
||||
'language' => 'de',
|
||||
'published_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/newsroom/cache-newsroom?page=2')
|
||||
->assertSuccessful();
|
||||
|
||||
expect(Cache::has('web.newsroom.businessportal24-both.de.cache-newsroom.page.2'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('newsroom detail cache key aligns with the paginator so an invalid page cannot poison another page', function () {
|
||||
/** @var TestCase $this */
|
||||
$company = Company::factory()->businessportal24()->create([
|
||||
'slug' => 'poison-newsroom',
|
||||
'is_active' => true,
|
||||
]);
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->create([
|
||||
'company_id' => $company->id,
|
||||
'language' => 'de',
|
||||
'published_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
// Ungültige Seitennummer: der Paginator rendert Seite 1, also darf der
|
||||
// Inhalt NICHT unter dem Key für Seite 2 landen (sonst Cache-Poisoning).
|
||||
$this->get('https://businessportal24.test/de/newsroom/poison-newsroom?page=2.5')
|
||||
->assertSuccessful();
|
||||
|
||||
expect(Cache::has('web.newsroom.businessportal24-both.de.poison-newsroom.page.1'))->toBeTrue();
|
||||
expect(Cache::has('web.newsroom.businessportal24-both.de.poison-newsroom.page.2'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('newsrooms unfiltered index is cached but filtered searches are not', function () {
|
||||
/** @var TestCase $this */
|
||||
$company = Company::factory()->businessportal24()->create([
|
||||
'name' => 'SolarTec Bestand',
|
||||
'is_active' => true,
|
||||
]);
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($company)
|
||||
->create(['language' => 'de', 'published_at' => now()->subDay()]);
|
||||
|
||||
// Ungefiltertes Verzeichnis (erste Seite) wird unter einem beschränkten Key gecacht.
|
||||
$this->get('https://businessportal24.test/de/newsrooms')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('SolarTec Bestand');
|
||||
|
||||
expect(Cache::has('web.newsrooms.businessportal24,both.de.page.1'))->toBeTrue();
|
||||
|
||||
// Gefilterte Suche wird NICHT gecacht (Schutz vor unbegrenztem Key-Raum):
|
||||
// eine nach dem ersten Aufruf angelegte Firma erscheint sofort.
|
||||
$newCompany = Company::factory()->businessportal24()->create([
|
||||
'name' => 'SolarTec Neu',
|
||||
'is_active' => true,
|
||||
]);
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($newCompany)
|
||||
->create(['language' => 'de', 'published_at' => now()->subHour()]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/newsrooms?q=solartec')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('SolarTec Neu');
|
||||
|
||||
$this->get('https://businessportal24.test/de/newsrooms?q=solartec')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('SolarTec Neu');
|
||||
});
|
||||
|
||||
test('deep newsrooms pages are not cached', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/de/newsrooms?page=25')->assertSuccessful();
|
||||
|
||||
expect(Cache::has('web.newsrooms.businessportal24,both.de.page.25'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('newsrooms cache does not retain unrelated query parameters in pagination links', function () {
|
||||
/** @var TestCase $this */
|
||||
$response = $this->get('https://businessportal24.test/de/newsrooms?q=SolarTec&utm_source=poison')
|
||||
->assertSuccessful();
|
||||
|
||||
$pageTwoUrl = $response->viewData('newsrooms')->url(2);
|
||||
|
||||
expect($pageTwoUrl)
|
||||
->toContain('q=SolarTec')
|
||||
->not->toContain('utm_source');
|
||||
});
|
||||
|
||||
test('subcategory counts are aggregated correctly in a single grouped query', function () {
|
||||
/** @var TestCase $this */
|
||||
$parent = makeCategoryWithSlug(Portal::Both, 'Wirtschaft', 'wirtschaft');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue