create(); $event = Event::factory()->for($tenant)->create(['status' => 'published']); $photo = Photo::factory()->for($event)->create([ 'status' => 'approved', ]); $token = app(EventJoinTokenService::class)->createToken($event)->plain_token; $response = $this->withHeaders([ 'X-Device-Id' => 'device-share', 'Accept' => 'application/json', ])->postJson("/api/v1/events/{$token}/photos/{$photo->id}/share"); $response->assertOk(); $response->assertJsonStructure(['slug', 'url', 'expires_at']); $this->assertDatabaseHas('photo_share_links', [ 'photo_id' => $photo->id, 'slug' => $response->json('slug'), ]); } public function test_share_link_cannot_be_created_for_unrelated_photo(): void { $event = Event::factory()->create(['status' => 'published']); $otherEvent = Event::factory()->create(['status' => 'published']); $photo = Photo::factory()->for($otherEvent)->create(['status' => 'approved']); $token = app(EventJoinTokenService::class)->createToken($event)->plain_token; $response = $this->withHeaders([ 'X-Device-Id' => 'device-share', 'Accept' => 'application/json', ])->postJson("/api/v1/events/{$token}/photos/{$photo->id}/share"); $response->assertNotFound(); } public function test_share_payload_exposes_public_photo_data(): void { Config::set('filesystems.default', 'public'); Storage::fake('public'); $tenant = Tenant::factory()->create(); $event = Event::factory()->for($tenant)->create(['status' => 'published']); $task = Task::factory()->for($tenant)->create(); $photo = Photo::factory()->for($event)->create([ 'status' => 'approved', 'task_id' => $task->id, 'file_path' => 'photos/share-test.jpg', 'thumbnail_path' => 'photos/thumbnails/share-test.jpg', ]); Storage::disk('public')->put('photos/share-test.jpg', 'photo'); $share = PhotoShareLink::factory()->for($photo)->create([ 'expires_at' => now()->addDay(), ]); $response = $this->getJson("/api/v1/photo-shares/{$share->slug}"); $response->assertOk(); $response->assertJsonPath('photo.id', $photo->id); $response->assertJsonPath('photo.image_urls.full', fn ($value) => str_contains($value, '/photo-shares/')); $response->assertJsonPath('event.id', $event->id); $response->assertJsonStructure([ 'branding' => [ 'primary_color', 'secondary_color', 'background_color', 'logo_mode', 'logo_value', 'palette', 'typography', 'logo', 'buttons', ], ]); $assetUrl = $response->json('photo.image_urls.full'); $this->assertIsString($assetUrl); $this->assertNotNull(parse_url($assetUrl, PHP_URL_SCHEME)); $parsed = parse_url($assetUrl); $path = (string) ($parsed['path'] ?? ''); $query = $parsed['query'] ?? null; $assetResponse = $this->get($path.($query ? "?{$query}" : '')); $assetResponse->assertOk(); } }