service = app(FileStorageService::class); } protected function storedMedium(string $disk = 'private', string $content = 'hello'): Medium { $medium = Medium::factory()->create(['disk' => $disk, 'path' => 'files/sample.txt', 'filename' => 'sample.txt', 'mime_type' => 'text/plain']); Storage::disk($disk)->put($medium->path, $content); return $medium; } public function test_store_file_saves_public_file_and_medium_with_url(): void { $file = UploadedFile::fake()->create('My Scan.pdf', 12, 'application/pdf'); $medium = $this->service->storeFile($file); $this->assertSame(MediaDiskEnum::Public, $medium->disk); $this->assertMatchesRegularExpression('/^\d+_[A-Za-z0-9]{6}_my-scan\.pdf$/', $medium->filename); $this->assertSame('files/' . $medium->filename, $medium->path); $this->assertSame(Storage::disk('public')->url($medium->path), $medium->url); $this->assertSame(12 * 1024, $medium->size); $this->assertSame('application/pdf', $medium->mime_type); Storage::disk('public')->assertExists($medium->path); } public function test_store_file_saves_private_file_without_url_in_custom_directory(): void { $medium = $this->service->storeFile(UploadedFile::fake()->image('x.png'), 'private', 'reports'); $this->assertSame(MediaDiskEnum::Private, $medium->disk); $this->assertNull($medium->url); $this->assertStringStartsWith('reports/', $medium->path); Storage::disk('private')->assertExists($medium->path); Storage::disk('public')->assertMissing($medium->path); } public function test_store_file_generates_distinct_filenames_for_same_upload_name(): void { $first = $this->service->storeFile(UploadedFile::fake()->image('same.png')); $second = $this->service->storeFile(UploadedFile::fake()->image('same.png')); $this->assertNotSame($first->filename, $second->filename); } public function test_move_to_disk_moves_file_from_private_to_public_and_sets_url(): void { $medium = $this->storedMedium('private'); $this->assertTrue($this->service->moveToDisk($medium, 'public')); $medium->refresh(); $this->assertSame(MediaDiskEnum::Public, $medium->disk); $this->assertSame(Storage::disk('public')->url('files/sample.txt'), $medium->url); Storage::disk('public')->assertExists('files/sample.txt'); Storage::disk('private')->assertMissing('files/sample.txt'); $this->assertSame('hello', Storage::disk('public')->get('files/sample.txt')); } public function test_move_to_disk_moves_file_to_private_and_clears_url(): void { $medium = $this->storedMedium('public'); $medium->update(['url' => 'http://example.test/files/sample.txt']); $this->assertTrue($this->service->moveToDisk($medium, 'private')); $medium->refresh(); $this->assertSame(MediaDiskEnum::Private, $medium->disk); $this->assertNull($medium->url); Storage::disk('private')->assertExists('files/sample.txt'); Storage::disk('public')->assertMissing('files/sample.txt'); } public function test_move_to_disk_returns_true_without_changes_for_same_disk(): void { $medium = $this->storedMedium('private'); $this->assertTrue($this->service->moveToDisk($medium, 'private')); Storage::disk('private')->assertExists('files/sample.txt'); } public function test_move_to_disk_returns_false_when_source_file_is_missing(): void { $medium = Medium::factory()->private()->create(['path' => 'files/missing.txt']); $this->assertFalse($this->service->moveToDisk($medium, 'public')); $this->assertSame(MediaDiskEnum::Private, $medium->fresh()->disk); } public function test_delete_medium_removes_file_and_record(): void { $medium = $this->storedMedium('private'); $this->service->deleteMedium($medium); Storage::disk('private')->assertMissing('files/sample.txt'); $this->assertDatabaseMissing('media', ['id' => $medium->id]); } public function test_delete_medium_removes_record_even_when_file_is_missing(): void { $medium = Medium::factory()->private()->create(['path' => 'files/missing.txt']); $this->service->deleteMedium($medium); $this->assertDatabaseMissing('media', ['id' => $medium->id]); } public function test_download_file_returns_attachment_for_private_existing_file(): void { $medium = $this->storedMedium('private'); $response = $this->service->downloadFile($medium); $this->assertStringContainsString('attachment', $response->headers->get('Content-Disposition')); $this->assertStringContainsString('sample.txt', $response->headers->get('Content-Disposition')); } public function test_download_file_aborts_for_public_medium(): void { $medium = $this->storedMedium('public'); $this->expectException(NotFoundHttpException::class); $this->service->downloadFile($medium); } public function test_download_file_aborts_when_file_is_missing(): void { $medium = Medium::factory()->private()->create(['path' => 'files/missing.txt']); $this->expectException(NotFoundHttpException::class); $this->service->downloadFile($medium); } public function test_serve_file_returns_inline_content_with_mime_type(): void { $medium = $this->storedMedium('private', 'inline body'); $response = $this->service->serveFile($medium); $this->assertSame(200, $response->getStatusCode()); $this->assertSame('inline body', $response->getContent()); $this->assertSame('text/plain', $response->headers->get('Content-Type')); $this->assertSame('inline; filename="sample.txt"', $response->headers->get('Content-Disposition')); } public function test_serve_file_aborts_for_public_medium(): void { $medium = $this->storedMedium('public'); $this->expectException(NotFoundHttpException::class); $this->service->serveFile($medium); } }