presseportale/tests/Feature/Admin/SystemHealthTest.php
Kevin Adametz bbc2b6048f fix: Imagick in der Betriebsüberwachung, Scanner-Anfragen an Livewire als 400, abgebrochene Alpine-Übergänge nicht mehr melden
Auf Live fehlte die PHP-Erweiterung imagick: Herkunftsprüfung und
KI-Vorab-Check der Bilder scheiterten still und ließen jedes Bild durch.
AdminSystemHealth zeigt jetzt die Zeile „PHP-Erweiterung Imagick“ (rot bei
eingeschaltetem Vorab-Check, sonst gelb).

GlitchTip server-pressekonto, Issues 38 und 39: gebaute Livewire-Anfragen
setzen gesperrte Properties (portal/language im Meldungs-Feed und in der
Suche) oder schicken Uploads an Komponenten ohne Upload. Livewire weist sie
korrekt ab; statt 500 bzw. 419 (das als „Seite abgelaufen“ gemeldet wurde)
antworten sie jetzt mit 400 und werden nicht mehr gemeldet.

Browser (Issue 18): Alpine bricht x-transition-Übergänge mit
{isFromCancelledTransition: true} ab — keine Fehlermeldung mehr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:54:14 +00:00

151 lines
5.5 KiB
PHP

<?php
use App\Enums\Portal;
use App\Models\PressRelease;
use App\Models\PressReleaseImage;
use App\Models\User;
use App\Services\Admin\AdminSystemHealth;
use Database\Seeders\RolesAndPermissionsSeeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\PermissionRegistrar;
use Tests\TestCase;
beforeEach(function () {
$this->seed(RolesAndPermissionsSeeder::class);
// In der Testumgebung ist die Queue `sync`; für die Queue-Metriken den
// Produktions-Treiber (database) verwenden, der die jobs-Tabellen liest.
config()->set('queue.default', 'database');
});
test('editor cannot open the system health report', function () {
/** @var TestCase $this */
$editor = User::factory()->create(['is_active' => true]);
$editor->assignRole('editor');
$this->actingAs($editor);
$this->get('https://pressekonto.test/admin/reports/system-health')->assertForbidden();
});
test('admin can open the system health report', function () {
/** @var TestCase $this */
$admin = User::factory()->create(['is_active' => true]);
$admin->assignRole('admin');
$this->actingAs($admin);
$this->get('https://pressekonto.test/admin/reports/system-health')
->assertSuccessful()
->assertSee('Betriebsüberwachung')
->assertSee('Queue-Worker');
});
test('the system health report shows the deployed version', function () {
/** @var TestCase $this */
config()->set('app.version', 'v1.1.0');
$admin = User::factory()->create(['is_active' => true]);
$admin->assignRole('admin');
$this->actingAs($admin);
$this->get('https://pressekonto.test/admin/reports/system-health')
->assertSuccessful()
->assertSee('Version: v1.1.0');
});
test('snapshot reports an ok queue when there are no waiting jobs', function () {
/** @var TestCase $this */
$snapshot = app(AdminSystemHealth::class)->snapshot();
expect($snapshot['queue']['pending'])->toBe(0)
->and($snapshot['queue']['worker_running'])->toBeTrue()
->and($snapshot['queue']['status'])->toBe('ok');
});
test('snapshot flags a stalled worker when a job has waited too long', function () {
/** @var TestCase $this */
DB::table('jobs')->insert([
'queue' => 'default',
'payload' => json_encode(['displayName' => 'App\\Jobs\\Example']),
'attempts' => 0,
'reserved_at' => null,
'available_at' => now()->subMinutes(10)->getTimestamp(),
'created_at' => now()->subMinutes(10)->getTimestamp(),
]);
$snapshot = app(AdminSystemHealth::class)->snapshot();
expect($snapshot['queue']['status'])->toBe('critical')
->and($snapshot['queue']['worker_running'])->toBeFalse()
->and($snapshot['overall'])->toBe('critical');
});
test('snapshot surfaces recent failed jobs', function () {
/** @var TestCase $this */
DB::table('failed_jobs')->insert([
'uuid' => (string) Str::uuid(),
'connection' => 'database',
'queue' => 'default',
'payload' => json_encode(['displayName' => 'App\\Jobs\\Broken']),
'exception' => "RuntimeException: boom\nStack trace...",
'failed_at' => now(),
]);
$snapshot = app(AdminSystemHealth::class)->snapshot();
expect($snapshot['queue']['failed_recent'])->toBe(1)
->and($snapshot['queue']['recent_failures'][0]['job'])->toBe('App\\Jobs\\Broken')
->and($snapshot['queue']['recent_failures'][0]['error'])->toContain('boom')
->and($snapshot['queue']['status'])->toBe('warn');
});
test('snapshot flags missing permissions as critical', function () {
/** @var TestCase $this */
Permission::query()->where('name', 'billing:manage')->delete();
app(PermissionRegistrar::class)->forgetCachedPermissions();
$checks = collect(app(AdminSystemHealth::class)->snapshot()['checks'])
->keyBy('key');
expect($checks['permissions']['status'])->toBe('critical')
->and($checks['permissions']['detail'])->toContain('billing:manage');
});
test('snapshot flags pending media privatization', function () {
/** @var TestCase $this */
$pr = PressRelease::factory()->forPortal(Portal::Presseecho)->create();
PressReleaseImage::query()->create([
'press_release_id' => $pr->id,
'disk' => 'public',
'path' => 'press-releases/'.$pr->id.'/images/legacy.jpg',
]);
$checks = collect(app(AdminSystemHealth::class)->snapshot()['checks'])->keyBy('key');
expect($checks['media_privatization']['status'])->toBe('warn')
->and($checks['media_privatization']['detail'])->toContain('1');
});
test('snapshot flags a missing imagick extension', function (bool $precheckEnabled, string $status) {
config()->set('images.precheck.enabled', $precheckEnabled);
app()->bind(AdminSystemHealth::class, fn () => new class extends AdminSystemHealth
{
protected function imagickAvailable(): bool
{
return false;
}
});
$checks = collect(app(AdminSystemHealth::class)->snapshot()['checks'])->keyBy('key');
expect($checks['imagick']['status'])->toBe($status)
->and($checks['imagick']['detail'])->toContain('php8.4-imagick');
})->with([
'precheck off' => [false, 'warn'],
'precheck on' => [true, 'critical'],
]);
test('snapshot reports imagick as ok when it is loaded', function () {
$checks = collect(app(AdminSystemHealth::class)->snapshot()['checks'])->keyBy('key');
expect($checks['imagick']['status'])->toBe(class_exists(Imagick::class) ? 'ok' : 'warn');
});