create([ 'live_show_token' => str_repeat('a', 64), ]); $first = Photo::factory()->for($event)->create([ 'status' => 'approved', 'live_status' => PhotoLiveStatus::APPROVED, 'live_approved_at' => now()->subMinutes(10), ]); $second = Photo::factory()->for($event)->create([ 'status' => 'approved', 'live_status' => PhotoLiveStatus::APPROVED, 'live_approved_at' => now()->subMinutes(3), ]); $response = $this->getJson("/api/v1/live-show/{$event->live_show_token}"); $response->assertOk(); $response->assertJsonPath('photos.0.id', $first->id); $response->assertJsonPath('photos.1.id', $second->id); $response->assertJsonPath('cursor.id', $second->id); $response->assertJsonStructure([ 'settings', 'settings_version', 'photos', 'cursor' => ['approved_at', 'id'], ]); } public function test_live_show_updates_respects_cursor(): void { $event = Event::factory()->create([ 'live_show_token' => str_repeat('b', 64), ]); $first = Photo::factory()->for($event)->create([ 'status' => 'approved', 'live_status' => PhotoLiveStatus::APPROVED, 'live_approved_at' => now()->subMinutes(9), ]); $second = Photo::factory()->for($event)->create([ 'status' => 'approved', 'live_status' => PhotoLiveStatus::APPROVED, 'live_approved_at' => now()->subMinutes(6), ]); $third = Photo::factory()->for($event)->create([ 'status' => 'approved', 'live_status' => PhotoLiveStatus::APPROVED, 'live_approved_at' => now()->subMinutes(1), ]); $query = http_build_query([ 'after_approved_at' => $second->live_approved_at->toAtomString(), 'after_id' => $second->id, ]); $response = $this->getJson("/api/v1/live-show/{$event->live_show_token}/updates?{$query}"); $response->assertOk(); $response->assertJsonCount(1, 'photos'); $response->assertJsonPath('photos.0.id', $third->id); $response->assertJsonPath('cursor.id', $third->id); $response->assertJsonMissing(['photos' => [['id' => $first->id]]]); } public function test_live_show_returns_404_for_invalid_token(): void { $response = $this->getJson('/api/v1/live-show/invalid-token'); $response->assertNotFound(); } public function test_live_show_updates_returns_settings_when_version_changes(): void { $event = Event::factory()->create([ 'live_show_token' => str_repeat('c', 64), 'settings' => [ 'live_show' => [ 'effect_preset' => 'film_cut', ], ], ]); $state = $this->getJson("/api/v1/live-show/{$event->live_show_token}"); $state->assertOk(); $version = $state->json('settings_version'); $updates = $this->getJson("/api/v1/live-show/{$event->live_show_token}/updates?settings_version={$version}"); $updates->assertOk(); $updates->assertJsonPath('settings', null); $event->forceFill([ 'settings' => [ 'live_show' => [ 'effect_preset' => 'shutter_flash', ], ], ])->save(); $updates = $this->getJson("/api/v1/live-show/{$event->live_show_token}/updates?settings_version={$version}"); $updates->assertOk(); $updates->assertJsonMissing(['settings' => null]); $updates->assertJsonPath('settings.effect_preset', 'shutter_flash'); } public function test_live_show_updates_rejects_invalid_cursor(): void { $event = Event::factory()->create([ 'live_show_token' => str_repeat('d', 64), ]); $response = $this->getJson("/api/v1/live-show/{$event->live_show_token}/updates?after_approved_at=invalid&after_id=1"); $response->assertStatus(422); $response->assertJsonPath('error', 'invalid_cursor'); } }