image('logo.png'); $attachment = app(AttachmentService::class)->store( $file, 'attachments/acme', ); $this->assertSame(AttachmentType::Image, $attachment->type); $this->assertTrue(Str::isUuid($attachment->key)); $this->assertSame('logo.png', $attachment->filename); $this->assertSame('attachments/acme/'.$attachment->key.'.png', $attachment->path); Storage::disk('s3')->assertExists('attachments/acme/'.$attachment->key.'.png'); $this->assertDatabaseHas('attachments', [ 'id' => $attachment->id, 'key' => $attachment->key, 'path' => 'attachments/acme/'.$attachment->key.'.png', 'filename' => 'logo.png', 'type' => AttachmentType::Image->value, ]); $freshAttachment = Attachment::query()->findOrFail($attachment->id); $this->assertSame(AttachmentType::Image, $freshAttachment->type); } public function test_it_does_not_persist_the_attachment_when_the_s3_upload_fails(): void { $disk = Mockery::mock(); Storage::shouldReceive('disk') ->once() ->with('s3') ->andReturn($disk); $disk->shouldReceive('putFileAs') ->once() ->with('attachments/globex', Mockery::type(UploadedFile::class), Mockery::on(static fn (string $value): bool => Str::isUuid(pathinfo($value, PATHINFO_FILENAME)))) ->andReturn(false); try { app(AttachmentService::class)->store( UploadedFile::fake()->create('manual.pdf', 10, 'application/pdf'), 'attachments/globex', ); $this->fail('Expected an AttachmentStorageException to be thrown.'); } catch (AttachmentStorageException) { $this->assertDatabaseCount('attachments', 0); } } public function test_it_deletes_from_s3_before_removing_the_database_record(): void { Storage::fake('s3'); Storage::disk('s3')->put('attachments/initech/spec.pdf', 'spec'); $attachment = Attachment::query()->create([ 'path' => 'attachments/initech/spec.pdf', 'key' => (string) Str::uuid(), 'filename' => 'spec.pdf', 'type' => AttachmentType::Pdf, 'mime_type' => 'application/pdf', 'extension' => 'pdf', 'size' => 512, ]); app(AttachmentService::class)->delete($attachment); Storage::disk('s3')->assertMissing('attachments/initech/spec.pdf'); $this->assertDatabaseMissing('attachments', [ 'id' => $attachment->id, ]); } public function test_it_keeps_the_database_record_when_the_s3_delete_fails(): void { $attachment = Attachment::query()->create([ 'path' => 'attachments/umbrella/audio.mp3', 'key' => (string) Str::uuid(), 'filename' => 'audio.mp3', 'type' => AttachmentType::Audio, 'mime_type' => 'audio/mpeg', 'extension' => 'mp3', 'size' => 1024, ]); $disk = Mockery::mock(); Storage::shouldReceive('disk') ->once() ->with('s3') ->andReturn($disk); $disk->shouldReceive('delete') ->once() ->with('attachments/umbrella/audio.mp3') ->andReturn(false); try { app(AttachmentService::class)->delete($attachment); $this->fail('Expected an AttachmentStorageException to be thrown.'); } catch (AttachmentStorageException) { $this->assertDatabaseHas('attachments', [ 'id' => $attachment->id, ]); } } }