presseportale/tests/Feature/PressReleaseAccessiblePageTest.php
Kevin Adametz 17b586f136 Perf: customer press-releases-Liste ueber UNION statt OR-Paginate
Die Uebersicht /admin/me/press-releases war auch fuer leere Konten ~2s: der OR-Filter in scopeAccessibleBy zwang die Paginator-count(*) zum Full-Scan (EXPLAIN type=ALL, ~174k Zeilen) und den Zeilen-Fetch bei 0 Treffern zum vollen created_at-Index-Scan (~1s). Neue PressRelease::accessiblePageFor() nutzt dieselbe index-freundliche UNION wie accessibleStatusCountsFor(): schmale UNION liefert die Seiten-IDs ueber user_id-/company_id-Index, danach nur 25 Modelle hydratisiert; Total via schnellem UNION-count. Messung: leer ~2000ms -> 167ms, groesstes Konto (20.830 PMs) ~740ms -> 468ms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:55:45 +00:00

79 lines
3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use App\Enums\PressReleaseStatus;
use App\Models\Company;
use App\Models\PressRelease;
use App\Models\User;
test('includes authored and company-linked releases and dedupes overlap', function () {
$user = User::factory()->create();
$company = Company::factory()->create();
$user->companies()->attach($company->id, ['role' => 'member']);
$authored = PressRelease::factory()->create(['user_id' => $user->id, 'company_id' => null]);
$viaCompany = PressRelease::factory()->create(['company_id' => $company->id]);
// erfüllt beide Zweige – darf nur einmal gezählt werden
$both = PressRelease::factory()->create(['user_id' => $user->id, 'company_id' => $company->id]);
$page = PressRelease::accessiblePageFor($user);
expect($page->total())->toBe(3)
->and($page->pluck('id')->sort()->values()->all())
->toBe(collect([$authored->id, $viaCompany->id, $both->id])->sort()->values()->all());
});
test('excludes releases the user cannot access', function () {
$user = User::factory()->create();
$stranger = PressRelease::factory()->create();
$page = PressRelease::accessiblePageFor($user);
expect($page->total())->toBe(0)
->and($page->contains($stranger))->toBeFalse();
});
test('applies the constrain closure to both union branches', function () {
$user = User::factory()->create();
PressRelease::factory()->create(['user_id' => $user->id, 'status' => PressReleaseStatus::Published->value]);
PressRelease::factory()->create(['user_id' => $user->id, 'status' => PressReleaseStatus::Draft->value]);
$page = PressRelease::accessiblePageFor(
$user,
fn ($query) => $query->where('status', PressReleaseStatus::Published->value),
);
expect($page->total())->toBe(1)
->and($page->first()->status)->toBe(PressReleaseStatus::Published);
});
test('respects sort column and direction', function () {
$user = User::factory()->create();
$a = PressRelease::factory()->create(['user_id' => $user->id, 'title' => 'Alpha']);
$z = PressRelease::factory()->create(['user_id' => $user->id, 'title' => 'Zulu']);
$asc = PressRelease::accessiblePageFor($user, null, 'title', 'asc');
$desc = PressRelease::accessiblePageFor($user, null, 'title', 'desc');
expect($asc->first()->id)->toBe($a->id)
->and($desc->first()->id)->toBe($z->id);
});
test('paginates with the given page size', function () {
$user = User::factory()->create();
PressRelease::factory()->count(7)->create(['user_id' => $user->id]);
$page = PressRelease::accessiblePageFor($user, null, 'created_at', 'desc', 5);
expect($page->total())->toBe(7)
->and($page->perPage())->toBe(5)
->and($page->count())->toBe(5);
});
test('falls back to created_at for an invalid sort column', function () {
$user = User::factory()->create();
PressRelease::factory()->create(['user_id' => $user->id]);
$page = PressRelease::accessiblePageFor($user, null, 'title; drop table', 'sideways');
expect($page->total())->toBe(1);
});