create(); $token = $user->createToken('Reporting Token', ['press-releases:read'])->accessToken; createApiUsageLog($user->id, $token->id, 'GET', '/api/v1/categories', 200, 12, now()->subMinutes(3)); createApiUsageLog($user->id, $token->id, 'GET', '/api/v1/categories', 200, 10, now()->subMinutes(2)); createApiUsageLog($user->id, $token->id, 'POST', '/api/v1/newsletter/subscribe', 422, 30, now()->subMinute()); createApiUsageLog(null, null, 'GET', '/api/v1/categories', 410, 5, now()); $report = app(ApiUsageReporter::class)->report(top: 5); expect($report['summary'])->toMatchArray([ 'total_requests' => 4, 'unique_users' => 1, 'unique_tokens' => 1, 'successful_requests' => 2, 'client_error_requests' => 2, 'server_error_requests' => 0, ]) ->and($report['summary']['average_duration_ms'])->toBe(14.25) ->and($report['top_paths'][0])->toMatchArray([ 'value' => '/api/v1/categories', 'requests' => 3, ]) ->and($report['status_codes'][0])->toMatchArray([ 'value' => 200, 'requests' => 2, ]); }); test('api usage report command renders summary', function () { /** @var TestCase $this */ $user = User::factory()->create(); createApiUsageLog($user->id, null, 'GET', '/api/v1/companies', 200, 20, now()); $this->artisan('api:usage-report', ['--no-report' => true]) ->expectsOutput('API-Usage-Report') ->expectsOutput('Requests: 1') ->expectsOutput('Eindeutige User: 1') ->assertSuccessful(); }); function createApiUsageLog(?int $userId, ?int $tokenId, string $method, string $path, int $statusCode, int $durationMs, DateTimeInterface $requestedAt): ApiUsageLog { return ApiUsageLog::query()->create([ 'user_id' => $userId, 'personal_access_token_id' => $tokenId, 'method' => $method, 'path' => $path, 'route_name' => null, 'status_code' => $statusCode, 'ip_address' => '127.0.0.1', 'user_agent' => 'Pest', 'duration_ms' => $durationMs, 'requested_at' => $requestedAt, ]); }