Files
SkinbaseNova/tests/Feature/Http/ArtworkDownloadM15CTest.php
T
klevze e9cf754b37 Enable nginx X-Accel for original artwork downloads.
Hand originals to nginx after auth so PHP is not in the byte path. Keep DOWNLOAD_ACCEL_ENABLED off until the internal location is verified.
2026-08-29 12:25:37 +02:00

132 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\Artwork;
use App\Models\User;
use App\Services\ArtworkStatsService;
use App\Services\Traffic\OnlineVisitorRepository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
beforeEach(function () {
$root = storage_path('framework/testing/artwork-downloads-m15c');
config([
'uploads.storage_root' => $root,
'uploads.local_originals_root' => $root,
'app.download_accel_enabled' => false,
'app.url' => 'https://skinbase.test',
'skinbase-sessions.enabled' => true,
'skinbase-sessions.debug_header' => true,
'skinbase-sessions.skip_anonymous_public_get' => true,
]);
if (File::exists($root)) {
File::deleteDirectory($root);
}
File::makeDirectory($root, 0755, true);
});
afterEach(function () {
$root = storage_path('framework/testing/artwork-downloads-m15c');
if (File::exists($root)) {
File::deleteDirectory($root);
}
});
function m15cOriginal(string $hash, string $ext, string $content = 'bytes'): string
{
$root = rtrim((string) config('uploads.local_originals_root'), DIRECTORY_SEPARATOR);
$dir = $root.DIRECTORY_SEPARATOR.substr($hash, 0, 2).DIRECTORY_SEPARATOR.substr($hash, 2, 2);
File::makeDirectory($dir, 0755, true, true);
$path = $dir.DIRECTORY_SEPARATOR.$hash.'.'.$ext;
File::put($path, $content);
return $path;
}
it('returns a cheap 404 for a nonexistent artwork without analytics', function () {
$this->mock(ArtworkStatsService::class, function ($mock): void {
$mock->shouldReceive('incrementDownloads')->never();
});
$this->get('/download/artwork/999999')->assertNotFound();
expect(DB::table('artwork_downloads')->count())->toBe(0);
});
it('returns a cheap 404 for a missing original without analytics', function () {
$artwork = Artwork::factory()->create([
'hash' => 'c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0',
'file_ext' => 'webp',
'file_name' => 'gone.webp',
]);
$this->mock(ArtworkStatsService::class, function ($mock): void {
$mock->shouldReceive('incrementDownloads')->never();
});
$this->get('/download/artwork/'.$artwork->id)->assertNotFound();
expect(DB::table('artwork_downloads')->where('artwork_id', $artwork->id)->count())->toBe(0);
});
it('keeps valid anonymous downloads and X-Accel-Redirect when enabled', function () {
config(['app.download_accel_enabled' => true, 'app.download_accel_path' => '/internal/originals']);
$hash = '2721722cb2f407cc53539ee8718cc89c4cdfc579';
m15cOriginal($hash, 'zip');
$artwork = Artwork::factory()->create([
'hash' => $hash,
'file_ext' => 'zip',
'file_name' => 'Anon File',
]);
$this->get('/download/artwork/'.$artwork->id)
->assertOk()
->assertHeader('X-Accel-Redirect', '/internal/originals/27/21/2721722cb2f407cc53539ee8718cc89c4cdfc579.zip');
});
it('still records ArtworkDownload.user_id for authenticated downloads', function () {
$hash = 'aa11bb22cc33dd44ee55ff6677889900aabbccdd';
m15cOriginal($hash, 'png');
$user = User::factory()->create();
$artwork = Artwork::factory()->create([
'hash' => $hash,
'file_ext' => 'png',
'file_name' => 'Auth File',
]);
$this->actingAs($user)->get('/download/artwork/'.$artwork->id)->assertOk();
$this->assertDatabaseHas('artwork_downloads', [
'artwork_id' => $artwork->id,
'user_id' => $user->id,
]);
});
it('skips session for anonymous download GET without a session cookie', function () {
$this->get('/download/artwork/999999')
->assertNotFound()
->assertHeader('X-Skinbase-Session', 'skipped');
});
it('starts session for download GET when a session cookie is already present', function () {
$cookie = (string) config('session.cookie');
$this->withCookie($cookie, 'existing-session-cookie')
->get('/download/artwork/999999')
->assertNotFound()
->assertHeader('X-Skinbase-Session', 'started');
});
it('does not record presence for artwork download requests', function () {
$this->mock(OnlineVisitorRepository::class, function ($mock): void {
$mock->shouldReceive('track')->never();
});
$this->get('/download/artwork/999999')->assertNotFound();
});