batchName = 'test-'.\Illuminate\Support\Str::uuid(); $this->staging = storage_path('app/private/document-import-staging/'.$this->batchName); File::ensureDirectoryExists($this->staging); Storage::fake('local'); Bus::fake(); } protected function tearDown(): void { File::deleteDirectory($this->staging); parent::tearDown(); } public function test_preview_is_read_only_and_batch_import_is_private_idempotent_and_attributed(): void { $pdf = "%PDF-1.4\nfixture document\n%%EOF"; file_put_contents($this->staging.'/source.pdf', $pdf); $record = [ 'external_id' => 'test-document-v1', 'relative_path' => $this->batchName.'/source.pdf', 'title' => 'Documento de teste', 'issuer' => 'Entidade de teste', 'file_name' => 'documento.pdf', 'source_url' => 'https://example.test/documento.pdf', 'source_reference' => 'URL de teste; sem validação legal.', 'document_number' => 'TEST-1', 'document_date' => '2026-01-01', 'language' => 'pt', 'version_label' => 'edição de teste', 'category' => 'general', 'topic' => 'test', 'document_type' => 'guide', ]; file_put_contents($this->staging.'/manifest.json', json_encode(['documents' => [$record, $record]], JSON_THROW_ON_ERROR)); $actor = \App\Models\User::factory()->create(); $this->artisan(new ImportDocumentBatch(), ['manifest' => $this->batchName.'/manifest.json'])->assertSuccessful(); $this->assertDatabaseCount('documents', 0); $this->assertDatabaseCount('portal_resources', 0); $this->assertSame([], Storage::disk('local')->allFiles('portal-documents')); $this->artisan(new ImportDocumentBatch(), ['manifest' => $this->batchName.'/manifest.json', '--apply' => true, '--actor' => (string) $actor->id])->assertSuccessful(); $this->assertDatabaseCount('documents', 1); $this->assertDatabaseCount('portal_resources', 1); $document = DB::table('documents')->firstOrFail(); $this->assertSame($actor->id, $document->uploaded_by); $this->assertSame('quarantined', $document->status); $this->assertSame('internal', $document->data_classification); $this->assertSame('pending', $document->malware_scan_status); $this->assertDatabaseHas('portal_resources', ['published' => false, 'rights_status' => 'unconfirmed', 'created_by' => $actor->id]); $this->assertDatabaseHas('audit_events', ['actor_user_id' => $actor->id, 'action' => 'private_document_batch_imported']); Bus::assertDispatchedTimes(ScanDocumentForMalware::class, 1); $this->artisan(new ImportDocumentBatch(), ['manifest' => $this->batchName.'/manifest.json', '--apply' => true, '--actor' => (string) $actor->id])->assertSuccessful(); $this->assertDatabaseCount('documents', 1); $this->assertDatabaseCount('portal_resources', 1); $this->assertDatabaseCount('audit_events', 1); Bus::assertDispatchedTimes(ScanDocumentForMalware::class, 1); } public function test_apply_refuses_missing_actor_and_source_path_traversal(): void { file_put_contents($this->staging.'/source.pdf', "%PDF-1.4\nfixture\n%%EOF"); $record = ['external_id' => 'test-document', 'relative_path' => '..\\..\\..\\..\\.env', 'title' => 'Test', 'issuer' => 'Test']; file_put_contents($this->staging.'/manifest.json', json_encode(['documents' => [$record]], JSON_THROW_ON_ERROR)); $this->artisan(new ImportDocumentBatch(), ['manifest' => $this->batchName.'/manifest.json', '--apply' => true])->assertFailed(); $this->assertDatabaseCount('documents', 0); $this->artisan(new ImportDocumentBatch(), ['manifest' => $this->batchName.'/manifest.json'])->assertFailed(); $this->assertDatabaseCount('documents', 0); } }