'Quarterly rotation', 'ip' => '127.0.0.1']), ); $entry2 = new SecurityAuditLogEntry( id: new SecurityAuditLogEntryId(Uuid::uuid7()), clientId: $clientId, clientIdentifier: 'arcadia_id_' . str_repeat('b', 32), eventType: 'client.status_changed', details: new SecurityAuditLogDetails([]), ); $output = new Output($since, $to, $entry1, $entry2); $presenter = new AuditLogsPresenter(); // WHEN $view = $presenter->present($output); // THEN expect($view)->toBeInstanceOf(AuditLogsView::class) ->and($view->isEmpty())->toBeFalse() ->and($view->total)->toBe(2) ->and($view->since)->toBe($since) ->and($view->to)->toBe($to) ->and($view->headers)->toBe(['Date (UTC)', 'Event', 'Client Identifier', 'Client ID (UUID)', 'Details']) ->and($view->rows)->toHaveCount(2) ->and($view->rows[0]->eventType)->toBe('client.secret_rotated') ->and($view->rows[0]->clientIdentifier)->toBe('arcadia_id_' . str_repeat('a', 32)) ->and($view->rows[0]->clientId)->toBe($clientId) ->and($view->rows[0]->details)->toBe('reason: Quarterly rotation, ip: 127.0.0.1') ->and($view->rows[1]->eventType)->toBe('client.status_changed') ->and($view->rows[1]->details)->toBe(''); }); it('identifies an empty view', function (): void { // GIVEN $since = new \DateTimeImmutable('2026-01-01 00:00:00'); $to = new \DateTimeImmutable('2026-01-31 23:59:59'); $output = new Output($since, $to); $presenter = new AuditLogsPresenter(); // WHEN $view = $presenter->present($output); // THEN expect($view->isEmpty())->toBeTrue() ->and($view->total)->toBe(0) ->and($view->rows)->toBeEmpty(); }); it('generates valid CSV content from AuditLogsView', function (): void { // GIVEN $since = new \DateTimeImmutable('2026-01-01 00:00:00'); $to = new \DateTimeImmutable('2026-01-31 23:59:59'); $clientId = Uuid::uuid7(); $entry = new SecurityAuditLogEntry( id: new SecurityAuditLogEntryId(Uuid::uuid7()), clientId: $clientId, clientIdentifier: 'arcadia_id_123', eventType: 'client.created', details: new SecurityAuditLogDetails(['name' => 'Test App']), ); $presenter = new AuditLogsPresenter(); $view = $presenter->present(new Output($since, $to, $entry)); // WHEN $csv = $view->toCsvContent(); // THEN expect($csv)->toContain('"Date (UTC)",Event,"Client Identifier","Client ID (UUID)",Details') ->and($csv)->toContain('client.created') ->and($csv)->toContain('arcadia_id_123') ->and($csv)->toContain($clientId->toString()) ->and($csv)->toContain('"name: Test App"'); }); });