Keep similar-ai from tripping the global circuit on a lone URL 502, clamp Qdrant search to 100, and add Server-Timing plus slow-request logging. Studio shared props, Academy S3 exists caching, heat chunking, and Redis/scheduler hygiene stay in this rollout.
114 lines
3.7 KiB
PHP
114 lines
3.7 KiB
PHP
<?php
|
|
|
|
use App\Models\Artwork;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
function slowHttpLogPath(): string
|
|
{
|
|
return storage_path('logs/slow-http-test.log');
|
|
}
|
|
|
|
function configureSlowHttpLog(): void
|
|
{
|
|
config([
|
|
'http_observability.slow_request.enabled' => true,
|
|
'http_observability.slow_request.threshold_ms' => 750,
|
|
'logging.channels.slow-http.driver' => 'single',
|
|
'logging.channels.slow-http.path' => slowHttpLogPath(),
|
|
'logging.channels.slow-http.level' => 'info',
|
|
]);
|
|
Log::forgetChannel('slow-http');
|
|
File::delete(slowHttpLogPath());
|
|
}
|
|
|
|
afterEach(function (): void {
|
|
File::delete(slowHttpLogPath());
|
|
});
|
|
|
|
it('does not write a slow-http record for a fast request', function () {
|
|
configureSlowHttpLog();
|
|
config(['http_observability.slow_request.test_duration_ms' => 120]);
|
|
|
|
$this->get('/leaderboard')->assertOk();
|
|
|
|
expect(File::exists(slowHttpLogPath()))->toBeFalse();
|
|
});
|
|
|
|
it('writes one structured slow-http record using the route template', function () {
|
|
configureSlowHttpLog();
|
|
config(['http_observability.slow_request.test_duration_ms' => 900]);
|
|
|
|
$response = $this->get('/leaderboard');
|
|
$response->assertOk();
|
|
expect($response->headers->get('Server-Timing'))->toContain('app;desc="Laravel"');
|
|
|
|
$raw = File::get(slowHttpLogPath());
|
|
expect(substr_count(trim($raw), "\n"))->toBe(0);
|
|
|
|
$line = trim($raw);
|
|
preg_match('/\{.*\}\s*$/', $line, $matches);
|
|
expect($matches[0] ?? null)->not->toBeNull();
|
|
$json = json_decode($matches[0], true, flags: JSON_THROW_ON_ERROR);
|
|
|
|
expect($json['method'])->toBe('GET')
|
|
->and($json['status'])->toBe(200)
|
|
->and($json['duration_ms'])->toEqual(900)
|
|
->and($json['route_uri'])->not->toContain('?')
|
|
->and($json['route_uri'])->not->toContain('leaderboard/')
|
|
->and($json)->not->toHaveKey('query')
|
|
->and($json)->not->toHaveKey('ip')
|
|
->and($json)->not->toHaveKey('email')
|
|
->and($json)->not->toHaveKey('user_id')
|
|
->and($json['authenticated'])->toBeFalse();
|
|
});
|
|
|
|
it('does not log when slow-request logging is disabled', function () {
|
|
configureSlowHttpLog();
|
|
config([
|
|
'http_observability.slow_request.enabled' => false,
|
|
'http_observability.slow_request.test_duration_ms' => 5000,
|
|
]);
|
|
|
|
$this->get('/leaderboard')->assertOk()->assertHeader('Server-Timing');
|
|
|
|
expect(File::exists(slowHttpLogPath()))->toBeFalse();
|
|
});
|
|
|
|
it('does not break a missing-file download response and still sends Server-Timing', function () {
|
|
configureSlowHttpLog();
|
|
config(['http_observability.slow_request.test_duration_ms' => 100]);
|
|
|
|
$artwork = Artwork::factory()->create([
|
|
'is_public' => true,
|
|
'is_approved' => true,
|
|
'published_at' => now()->subDay(),
|
|
'file_ext' => 'jpg',
|
|
'hash' => str_repeat('cd', 32),
|
|
]);
|
|
|
|
$this->get('/download/artwork/'.$artwork->id)
|
|
->assertNotFound()
|
|
->assertHeader('Server-Timing');
|
|
});
|
|
|
|
it('does not log query strings or user identifiers on an authenticated slow request', function () {
|
|
configureSlowHttpLog();
|
|
config(['http_observability.slow_request.test_duration_ms' => 800]);
|
|
|
|
$user = User::factory()->create([
|
|
'email' => '[email protected]',
|
|
'username' => 'slowlogsecret',
|
|
]);
|
|
|
|
$this->actingAs($user)->get('/leaderboard?period=weekly&debug=1')->assertOk();
|
|
|
|
$raw = File::get(slowHttpLogPath());
|
|
expect($raw)->not->toContain('[email protected]')
|
|
->and($raw)->not->toContain('period=weekly')
|
|
->and($raw)->not->toContain('debug=1')
|
|
->and($raw)->not->toContain('"user_id"')
|
|
->and($raw)->not->toContain($user->email);
|
|
});
|