create([ 'codigo' => 'acme', 'nombre' => 'Acme', 'dominio' => 'acme.com', ]); $file = UploadedFile::fake()->image('logo.png'); $attachment = app(AttachmentService::class)->store( $tenant, $file, 'attachments/acme/logo.png', AttachmentType::Image, ); $this->assertSame(AttachmentType::Image, $attachment->type); Storage::disk('s3')->assertExists('attachments/acme/logo.png'); $this->assertDatabaseHas('attachments', [ 'id' => $attachment->id, 'attachable_type' => $tenant->getMorphClass(), 'attachable_id' => $tenant->getKey(), 'path' => 'attachments/acme/logo.png', 'filename' => 'logo.png', 'type' => AttachmentType::Image->value, ]); $freshAttachment = Attachment::query()->findOrFail($attachment->id); $this->assertSame(AttachmentType::Image, $freshAttachment->type); $this->assertTrue($freshAttachment->attachable->is($tenant)); } public function test_it_does_not_persist_the_attachment_when_the_s3_upload_fails(): void { $tenant = Tenant::query()->create([ 'codigo' => 'globex', 'nombre' => 'Globex', 'dominio' => 'globex.com', ]); $disk = Mockery::mock(); Storage::shouldReceive('disk') ->once() ->with('s3') ->andReturn($disk); $disk->shouldReceive('putFileAs') ->once() ->with('attachments/globex', Mockery::type(UploadedFile::class), 'manual.pdf') ->andReturn(false); try { app(AttachmentService::class)->store( $tenant, UploadedFile::fake()->create('manual.pdf', 10, 'application/pdf'), 'attachments/globex/manual.pdf', AttachmentType::Pdf, ); $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'); $tenant = Tenant::query()->create([ 'codigo' => 'initech', 'nombre' => 'Initech', 'dominio' => 'initech.com', ]); Storage::disk('s3')->put('attachments/initech/spec.pdf', 'spec'); $attachment = $tenant->attachments()->create([ 'path' => 'attachments/initech/spec.pdf', '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 { $tenant = Tenant::query()->create([ 'codigo' => 'umbrella', 'nombre' => 'Umbrella', 'dominio' => 'umbrella.com', ]); $attachment = $tenant->attachments()->create([ 'path' => 'attachments/umbrella/audio.mp3', '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, ]); } } }