addDays(1)->startOfDay(); $event = Event::factory()->create(['date' => $eventDate]); $token = $event->ensureLiveShowToken(); $this->assertIsString($token); $this->assertSame(64, strlen($token)); $this->assertSame($token, $event->refresh()->live_show_token); $this->assertNotNull($event->live_show_token_rotated_at); $this->assertSame($eventDate->copy()->addDay()->endOfDay()->toIso8601String(), $event->live_show_token_expires_at?->toIso8601String()); $rotated = $event->rotateLiveShowToken(); $this->assertIsString($rotated); $this->assertSame(64, strlen($rotated)); $this->assertNotSame($token, $rotated); $this->assertSame($rotated, $event->refresh()->live_show_token); $this->assertSame($eventDate->copy()->addDay()->endOfDay()->toIso8601String(), $event->live_show_token_expires_at?->toIso8601String()); } public function test_live_show_token_expiry_updates_when_event_date_changes(): void { $eventDate = now()->addDays(3)->startOfDay(); $event = Event::factory()->create(['date' => $eventDate]); $event->ensureLiveShowToken(); $event->refresh(); $this->assertSame($eventDate->copy()->addDay()->endOfDay()->toIso8601String(), $event->live_show_token_expires_at?->toIso8601String()); $newDate = now()->addDays(7)->startOfDay(); $event->update(['date' => $newDate]); $event->refresh(); $this->assertSame($newDate->copy()->addDay()->endOfDay()->toIso8601String(), $event->live_show_token_expires_at?->toIso8601String()); } public function test_ensuring_existing_live_show_token_does_not_mutate_existing_expiry(): void { $this->freezeTime(function (): void { $eventDate = now()->addDays(5)->startOfDay(); $event = Event::factory()->create(['date' => $eventDate]); $event->rotateLiveShowToken(); $customExpiry = now()->addHours(6)->toImmutable(); $event->forceFill(['live_show_token_expires_at' => $customExpiry])->saveQuietly(); $initialExpiry = $event->refresh()->live_show_token_expires_at?->toIso8601String(); $this->travel(6)->hours(); $event->refresh()->ensureLiveShowToken(); $expiryAfterEnsure = $event->refresh()->live_show_token_expires_at?->toIso8601String(); $this->assertNotNull($initialExpiry); $this->assertSame($initialExpiry, $expiryAfterEnsure); }); } public function test_photo_live_status_is_cast_and_defaults_to_none(): void { $photo = Photo::factory()->create(); $photo->refresh(); $this->assertInstanceOf(PhotoLiveStatus::class, $photo->live_status); $this->assertSame(PhotoLiveStatus::NONE, $photo->live_status); $photo->forceFill([ 'live_status' => PhotoLiveStatus::PENDING, 'live_submitted_at' => now(), ])->save(); $photo->refresh(); $this->assertSame(PhotoLiveStatus::PENDING, $photo->live_status); $this->assertNotNull($photo->live_submitted_at); } public function test_photo_live_workflow_sets_expected_timestamps_and_reviewer(): void { $reviewer = User::factory()->create(); $photo = Photo::factory()->create(); $photo->markLivePending(); $photo->refresh(); $this->assertSame(PhotoLiveStatus::PENDING, $photo->live_status); $this->assertNotNull($photo->live_submitted_at); $this->assertNull($photo->live_reviewed_at); $this->assertNull($photo->live_approved_at); $photo->approveForLiveShow($reviewer); $photo->refresh(); $this->assertSame(PhotoLiveStatus::APPROVED, $photo->live_status); $this->assertNotNull($photo->live_reviewed_at); $this->assertNotNull($photo->live_approved_at); $this->assertSame($reviewer->id, $photo->live_reviewed_by); $this->assertNull($photo->live_rejection_reason); $photo->rejectForLiveShow($reviewer, 'policy_violation'); $photo->refresh(); $this->assertSame(PhotoLiveStatus::REJECTED, $photo->live_status); $this->assertNotNull($photo->live_reviewed_at); $this->assertNull($photo->live_approved_at); $this->assertSame($reviewer->id, $photo->live_reviewed_by); $this->assertSame('policy_violation', $photo->live_rejection_reason); } }