bootApp(); $_SESSION['_user'] = (new UserContext(1, 'Admin', 'admin', null, UserContext::SOURCE_LOCAL))->toArray(); $_SESSION['_last_active'] = time(); $_SESSION['_authenticated_at'] = time(); } public function testRendersList(): void { $this->enqueueApiResponse(200, [ 'page' => 1, 'page_size' => 50, 'total' => 1, 'items' => [ [ 'id' => 42, 'occurred_at' => '2026-04-29T10:00:00Z', 'actor_kind' => 'user', 'actor_id' => '7', 'action' => 'manual_block.created', 'entity_type' => 'manual_block', 'entity_id' => '12', 'details' => ['ip' => '203.0.113.99', 'reason' => 'audit-test'], 'source_ip' => '127.0.0.1', ], ], ]); $resp = $this->request('GET', '/app/audit'); self::assertSame(200, $resp->getStatusCode()); $body = (string) $resp->getBody(); self::assertStringContainsString('manual_block.created', $body); self::assertStringContainsString('203.0.113.99', $body); self::assertStringContainsString('1 total', $body); } public function testRendersEmptyState(): void { $this->enqueueApiResponse(200, ['page' => 1, 'page_size' => 50, 'total' => 0, 'items' => []]); $resp = $this->request('GET', '/app/audit'); self::assertSame(200, $resp->getStatusCode()); self::assertStringContainsString('No events match', (string) $resp->getBody()); } public function testFilterRoundTrip(): void { $this->enqueueApiResponse(200, ['page' => 1, 'page_size' => 50, 'total' => 0, 'items' => []]); $resp = $this->request('GET', '/app/audit?action=token.created&actor_kind=user'); $body = (string) $resp->getBody(); self::assertSame(200, $resp->getStatusCode()); // The form preserves the user's selection. self::assertMatchesRegularExpression('/