user = User::factory()->create(); $this->actingAs($this->user); } public function test_report_progress_broadcasts_transcription_updated(): void { Event::fake([RecordingTranscriptionUpdated::class]); $recording = Recording::query()->create([ 'user_id' => $this->user->id, 'title' => 'Broadcast progress', 'original_filename' => 'progress.mp3', 'file_path' => 'recordings/progress.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'processing', 'transcription_driver' => 'local', 'transcription_started_at' => now(), ]); $recording->reportProgress('Transcribing locally…', 50); Event::assertDispatched(RecordingTranscriptionUpdated::class, function (RecordingTranscriptionUpdated $event) use ($recording): bool { return $event->recording->is($recording) && $event->broadcastWith()['percent'] === 50 && $event->broadcastWith()['progress'] === 'Transcribing locally…'; }); } public function test_successful_transcription_broadcasts_done_status(): void { Event::fake([RecordingTranscriptionUpdated::class]); Storage::fake('local'); Storage::disk('local')->put('recordings/sample.mp3', 'fake-audio-bytes'); Transcription::fake(['Hello from the recorder.']); $recording = Recording::query()->create([ 'user_id' => $this->user->id, 'title' => 'Sample', 'original_filename' => 'sample.mp3', 'file_path' => 'recordings/sample.mp3', 'file_size_bytes' => 12, 'transcription_status' => 'pending', 'transcription_driver' => 'local', 'transcription_started_at' => now(), ]); (new TranscribeRecording($recording))->handle(app(TranscriptionService::class)); Event::assertDispatched(RecordingTranscriptionUpdated::class, function (RecordingTranscriptionUpdated $event) use ($recording): bool { $payload = $event->broadcastWith(); return $event->recording->is($recording) && $payload['status'] === 'done' && $payload['percent'] === 100 && $payload['has_transcript'] === true && ($payload['word_count'] ?? 0) > 0; }); } public function test_failed_transcription_broadcasts_failure(): void { Event::fake([RecordingTranscriptionUpdated::class]); Storage::fake('local'); Storage::disk('local')->put('recordings/bad.mp3', 'fake-audio-bytes'); Transcription::fake(function () { throw new \RuntimeException('Provider unavailable'); }); $recording = Recording::query()->create([ 'user_id' => $this->user->id, 'title' => 'Bad', 'original_filename' => 'bad.mp3', 'file_path' => 'recordings/bad.mp3', 'file_size_bytes' => 12, 'transcription_status' => 'pending', 'transcription_driver' => 'local', 'transcription_started_at' => now(), ]); try { (new TranscribeRecording($recording))->handle(app(TranscriptionService::class)); $this->fail('Expected transcription to throw'); } catch (\RuntimeException $e) { $this->assertSame('Provider unavailable', $e->getMessage()); } Event::assertDispatched(RecordingTranscriptionUpdated::class, function (RecordingTranscriptionUpdated $event) use ($recording): bool { $payload = $event->broadcastWith(); return $event->recording->is($recording) && $payload['status'] === 'failed' && $payload['error'] === 'Provider unavailable'; }); } public function test_cancel_transcription_broadcasts_cancelled_status(): void { Event::fake([RecordingTranscriptionUpdated::class]); $recording = Recording::query()->create([ 'user_id' => $this->user->id, 'title' => 'Cancel me', 'original_filename' => 'cancel.mp3', 'file_path' => 'recordings/cancel.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'processing', 'transcription_driver' => 'local', 'transcription_started_at' => now(), ]); $recording->cancelTranscription(); Event::assertDispatched(RecordingTranscriptionUpdated::class, function (RecordingTranscriptionUpdated $event) use ($recording): bool { return $event->recording->is($recording) && $event->broadcastWith()['status'] === 'cancelled'; }); } public function test_broadcast_event_uses_private_recording_channel(): void { $recording = Recording::query()->create([ 'user_id' => $this->user->id, 'title' => 'Channel', 'original_filename' => 'channel.mp3', 'file_path' => 'recordings/channel.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'pending', ]); $event = new RecordingTranscriptionUpdated($recording); $this->assertSame('RecordingTranscriptionUpdated', $event->broadcastAs()); $this->assertSame('private-recording.'.$recording->id, $event->broadcastOn()[0]->name); $this->assertSame('private-user.'.$this->user->id.'.recordings', $event->broadcastOn()[1]->name); } }