Refresh list status over Reverb with polling fallback, clarify queued vs processing, streamline upload empty states, and seed an admin login.
123 lines
4.0 KiB
PHP
123 lines
4.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Jobs\TranscribeRecording;
|
|
use App\Livewire\UploadRecordings;
|
|
use App\Models\Recording;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Bus;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Livewire\Livewire;
|
|
use Tests\TestCase;
|
|
|
|
class RecordingDuplicateUploadTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected User $user;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->user = User::factory()->create();
|
|
$this->actingAs($this->user);
|
|
}
|
|
|
|
public function test_duplicate_content_hash_is_skipped_on_upload(): void
|
|
{
|
|
Storage::fake('local');
|
|
Bus::fake();
|
|
|
|
$first = UploadedFile::fake()->createWithContent('meeting.mp3', 'identical-audio-bytes');
|
|
$duplicate = UploadedFile::fake()->createWithContent('meeting-copy.mp3', 'identical-audio-bytes');
|
|
|
|
Livewire::test(UploadRecordings::class)
|
|
->set('audio', [$first])
|
|
->assertRedirect();
|
|
|
|
$this->assertSame(1, Recording::query()->count());
|
|
Bus::assertDispatched(TranscribeRecording::class, 1);
|
|
|
|
Livewire::test(UploadRecordings::class)
|
|
->set('audio', [$duplicate])
|
|
->assertRedirect(route('recordings.create'))
|
|
->assertSessionHas('error');
|
|
|
|
$this->assertSame(1, Recording::query()->count());
|
|
Bus::assertDispatched(TranscribeRecording::class, 1);
|
|
}
|
|
|
|
public function test_batch_upload_skips_duplicate_content_within_request(): void
|
|
{
|
|
Storage::fake('local');
|
|
Bus::fake();
|
|
|
|
$one = UploadedFile::fake()->createWithContent('one.mp3', 'same-bytes');
|
|
$two = UploadedFile::fake()->createWithContent('two.mp3', 'same-bytes');
|
|
$three = UploadedFile::fake()->createWithContent('three.mp3', 'different-bytes');
|
|
|
|
Livewire::test(UploadRecordings::class)
|
|
->set('audio', [$one, $two, $three])
|
|
->assertRedirect(route('recordings.index'))
|
|
->assertSessionHas('success');
|
|
|
|
$this->assertStringContainsString('Skipped 1 duplicate', session('success'));
|
|
$this->assertSame(2, Recording::query()->count());
|
|
Bus::assertDispatched(TranscribeRecording::class, 2);
|
|
}
|
|
|
|
public function test_upload_page_shows_dropzone(): void
|
|
{
|
|
$this->get(route('recordings.create'))
|
|
->assertOk()
|
|
->assertSeeLivewire('upload-recordings')
|
|
->assertSee('Drop audio files here or click to browse')
|
|
->assertDontSee('Title (optional)')
|
|
->assertDontSee('Uploads start as soon as you drop');
|
|
}
|
|
|
|
public function test_duplicate_filename_and_size_is_skipped_without_content_hash(): void
|
|
{
|
|
Storage::fake('local');
|
|
Bus::fake();
|
|
|
|
Recording::query()->create([
|
|
'user_id' => $this->user->id,
|
|
'title' => 'Legacy',
|
|
'original_filename' => 'legacy.mp3',
|
|
'file_path' => 'recordings/legacy.mp3',
|
|
'file_size_bytes' => strlen('legacy-audio'),
|
|
'content_hash' => null,
|
|
'transcription_status' => 'done',
|
|
]);
|
|
|
|
Livewire::test(UploadRecordings::class)
|
|
->set('audio', [UploadedFile::fake()->createWithContent('legacy.mp3', 'legacy-audio')])
|
|
->assertRedirect(route('recordings.create'))
|
|
->assertSessionHas('error');
|
|
|
|
$this->assertSame(1, Recording::query()->count());
|
|
Bus::assertNothingDispatched();
|
|
}
|
|
|
|
public function test_stored_recording_persists_content_hash(): void
|
|
{
|
|
Storage::fake('local');
|
|
Bus::fake();
|
|
|
|
$file = UploadedFile::fake()->createWithContent('hash-me.mp3', 'payload-for-hash');
|
|
|
|
Livewire::test(UploadRecordings::class)
|
|
->set('audio', [$file])
|
|
->assertRedirect();
|
|
|
|
$recording = Recording::query()->first();
|
|
$this->assertNotNull($recording);
|
|
$this->assertSame(hash('sha256', 'payload-for-hash'), $recording->content_hash);
|
|
}
|
|
}
|