create([ 'status' => 'published', 'settings' => [ 'guest_download_variant' => 'preview', ], ]); $token = app(EventJoinTokenService::class)->createToken($event, ['label' => 'download-preview'])->plain_token; $storageTarget = $this->createPublicStorageTarget(); $originalPath = "events/{$event->id}/photos/original.jpg"; $previewPath = "events/{$event->id}/photos/previews/preview.jpg"; $thumbnailPath = "events/{$event->id}/photos/thumbs/thumb.jpg"; Storage::disk('public')->put($originalPath, 'original-content'); Storage::disk('public')->put($previewPath, 'preview-content'); Storage::disk('public')->put($thumbnailPath, 'thumb-content'); $photo = Photo::factory()->for($event)->create([ 'tenant_id' => $event->tenant_id, 'status' => 'approved', 'file_path' => $originalPath, 'thumbnail_path' => $thumbnailPath, ]); $originalAsset = EventMediaAsset::create([ 'event_id' => $event->id, 'media_storage_target_id' => $storageTarget->id, 'photo_id' => $photo->id, 'variant' => 'original', 'disk' => 'public', 'path' => $originalPath, 'status' => 'hot', 'processed_at' => now(), 'mime_type' => 'image/jpeg', ]); EventMediaAsset::create([ 'event_id' => $event->id, 'media_storage_target_id' => $storageTarget->id, 'photo_id' => $photo->id, 'variant' => 'preview', 'disk' => 'public', 'path' => $previewPath, 'status' => 'hot', 'processed_at' => now(), 'mime_type' => 'image/jpeg', ]); $photo->update(['media_asset_id' => $originalAsset->id]); $photosResponse = $this->getJson("/api/v1/gallery/{$token}/photos"); $downloadUrl = (string) $photosResponse->json('data.0.download_url'); $photosResponse->assertOk(); $this->assertNotEmpty($downloadUrl); $downloadResponse = $this->get($downloadUrl); $downloadResponse->assertOk(); $this->assertSame('preview-content', $downloadResponse->streamedContent()); $this->assertStringContainsString( 'attachment;', (string) $downloadResponse->headers->get('Content-Disposition') ); } public function test_gallery_download_falls_back_to_original_when_preview_is_missing(): void { Storage::fake('public'); $event = Event::factory()->create([ 'status' => 'published', 'settings' => [ 'guest_download_variant' => 'preview', ], ]); $token = app(EventJoinTokenService::class)->createToken($event, ['label' => 'download-fallback'])->plain_token; $storageTarget = $this->createPublicStorageTarget(); $originalPath = "events/{$event->id}/photos/original.jpg"; $thumbnailPath = "events/{$event->id}/photos/thumbs/thumb.jpg"; Storage::disk('public')->put($originalPath, 'original-content'); Storage::disk('public')->put($thumbnailPath, 'thumb-content'); $photo = Photo::factory()->for($event)->create([ 'tenant_id' => $event->tenant_id, 'status' => 'approved', 'file_path' => $originalPath, 'thumbnail_path' => $thumbnailPath, ]); $originalAsset = EventMediaAsset::create([ 'event_id' => $event->id, 'media_storage_target_id' => $storageTarget->id, 'photo_id' => $photo->id, 'variant' => 'original', 'disk' => 'public', 'path' => $originalPath, 'status' => 'hot', 'processed_at' => now(), 'mime_type' => 'image/jpeg', ]); $photo->update(['media_asset_id' => $originalAsset->id]); $photosResponse = $this->getJson("/api/v1/gallery/{$token}/photos"); $downloadUrl = (string) $photosResponse->json('data.0.download_url'); $photosResponse->assertOk(); $this->assertNotEmpty($downloadUrl); $downloadResponse = $this->get($downloadUrl); $downloadResponse->assertOk(); $this->assertSame('original-content', $downloadResponse->streamedContent()); } private function createPublicStorageTarget(): MediaStorageTarget { return MediaStorageTarget::create([ 'key' => 'public', 'name' => 'Public', 'driver' => 'local', 'is_hot' => true, 'is_default' => true, 'is_active' => true, ]); } }