create(['portal' => Portal::Presseecho]); $category->translations()->create([ 'locale' => 'de', 'name' => 'Energie & Klima', 'slug' => 'energie-klima', ]); $company = Company::factory()->presseecho()->create(['name' => 'Boost Energie GmbH']); $newest = PressRelease::factory() ->published() ->forPortal(Portal::Presseecho) ->for($category) ->for($company) ->create([ 'title' => 'Neueste unbezahlte Meldung', 'slug' => 'neueste-unbezahlte-meldung', 'published_at' => now()->subHour(), ]); $boosted = PressRelease::factory() ->published() ->forPortal(Portal::Presseecho) ->for($category) ->for($company) ->create([ 'title' => 'Geboostete aeltere Meldung', 'slug' => 'geboostete-aeltere-meldung', 'published_at' => now()->subDays(5), // Grün klassifiziert: Voraussetzung fürs Boost-Gate im BoostService. 'classification' => PressReleaseClassification::Green->value, ]); return compact('category', 'company', 'newest', 'boosted'); } test('a boosted release takes the homepage lead ahead of newer releases and is labelled', function () { /** @var TestCase $this */ ['boosted' => $boosted, 'newest' => $newest] = boostPlacementFixture(); Boost::factory()->for($boosted)->create(); $data = app(HomePageData::class)->build(Portal::Presseecho); expect($data['leadRelease']->id)->toBe($boosted->id) ->and($data['boostedIds'])->toBe([$boosted->id]) // Die verdrängte neueste Meldung rückt in die Seitenleiste nach. ->and($data['sideReleases']->modelKeys())->toContain($newest->id); // Badge „Top-Platzierung" hängt am Hero-Lead (nach dem Ticker-Vorkommen). $this->get('https://presseecho.test/de') ->assertSuccessful() ->assertSeeTextInOrder(['Top-Platzierung', 'Geboostete aeltere Meldung']); }); test('an expired boost does not influence the homepage placement', function () { /** @var TestCase $this */ ['boosted' => $boosted, 'newest' => $newest] = boostPlacementFixture(); Boost::factory()->for($boosted)->expired()->create(); $data = app(HomePageData::class)->build(Portal::Presseecho); expect($data['leadRelease']->id)->toBe($newest->id) ->and($data['boostedIds'])->toBe([]); $this->get('https://presseecho.test/de') ->assertSuccessful() ->assertDontSeeText('Top-Platzierung'); }); test('booking a boost invalidates the cached start and category pages immediately', function () { /** @var TestCase $this */ ['boosted' => $boosted, 'newest' => $newest] = boostPlacementFixture(); $owner = User::factory()->create(['is_active' => true]); app(CreditWalletService::class)->credit($owner, 40); // Beide Seiten in den Cache legen — noch ohne Boost. expect(app(HomePageData::class)->build(Portal::Presseecho)['leadRelease']->id)->toBe($newest->id); $this->get('https://presseecho.test/de/category/energie-klima') ->assertSuccessful() ->assertDontSeeText('Top-Platzierung'); app(BoostService::class)->boost($owner, $boosted, 7); // Kein Warten auf die TTL: die Buchung verwirft die gecachten Seiten. expect(app(HomePageData::class)->build(Portal::Presseecho)['leadRelease']->id)->toBe($boosted->id); $this->get('https://presseecho.test/de/category/energie-klima') ->assertSuccessful() ->assertSeeText('Top-Platzierung'); }); test('a boosted release leads its category page and is labelled', function () { /** @var TestCase $this */ ['boosted' => $boosted] = boostPlacementFixture(); Boost::factory()->for($boosted)->create(); // Lead-Slot der Branchenseite: Badge und geboosteter Titel im Hero, // die neuere unbezahlte Meldung folgt dahinter in den Top-Meldungen. $this->get('https://presseecho.test/de/category/energie-klima') ->assertSuccessful() ->assertSeeTextInOrder([ 'Top-Platzierung', 'Geboostete aeltere Meldung', 'Neueste unbezahlte Meldung', ]); });