batchName = 'test-'.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 = User::factory()->create(); config(['portal.managers' => [$actor->email]]); $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); } public function test_qa_batch_real_scan_preserves_original_replay_identity_conflict_and_profile_guards(): void { config(['services.malware.scanner' => 'clamd', 'services.malware.host' => '127.0.0.1', 'services.malware.port' => 3310]); $scanner = app(MalwareScanner::class); if (! $scanner->health()['available']) { $this->markTestSkipped('Real ClamD required for batch evidence.'); } $bytes = SyntheticDocument::pdf('Synthetic QA batch - no official or legal value'); file_put_contents($this->staging.'/source.pdf', $bytes); $record = ['external_id' => 'qa-batch-v1', 'relative_path' => $this->batchName.'/source.pdf', 'title' => 'Synthetic QA batch', 'issuer' => 'Synthetic QA issuer', 'source_url' => 'https://example.invalid/qa', 'version_label' => 'QA-1']; file_put_contents($this->staging.'/manifest.json', json_encode(['documents' => [$record, $record]], JSON_THROW_ON_ERROR)); $actor = User::factory()->create(); $options = ['manifest' => $this->batchName.'/manifest.json', '--apply' => true, '--actor' => (string) $actor->id]; $this->artisan(new ImportDocumentBatch, $options)->assertFailed(); $this->assertDatabaseCount('documents', 0); $this->assertSame([], Storage::disk('local')->allFiles('portal-documents')); config(['portal.managers' => [$actor->email]]); $this->app->detectEnvironment(fn () => 'production'); $this->artisan(new ImportDocumentBatch, $options)->assertFailed(); $this->app->detectEnvironment(fn () => 'testing'); $this->assertDatabaseCount('documents', 0); $this->artisan(new ImportDocumentBatch, $options)->assertSuccessful(); $document = DB::table('documents')->firstOrFail(); $this->assertSame($bytes, Storage::disk('local')->get($document->storage_key)); $this->assertDatabaseCount('documents', 1); $this->assertDatabaseCount('portal_resources', 1); (new ScanDocumentForMalware($document->id))->handle($scanner); $scanned = DB::table('documents')->where('id', $document->id)->first(); $this->assertSame('clean', $scanned->malware_scan_status); $this->assertSame('ClamAV', $scanned->malware_engine); $this->assertSame(hash('sha256', $bytes), $scanned->malware_scanned_sha256); $response = $this->actingAs($actor)->get('/management/documents/'.$document->id.'/download')->assertOk(); $this->assertSame($bytes, file_get_contents($response->baseResponse->getFile()->getPathname())); $resource = DB::table('portal_resources')->first(); $this->get('/documentos/'.$resource->id.'/download')->assertNotFound(); $this->artisan(new ImportDocumentBatch, $options)->assertSuccessful(); file_put_contents($this->staging.'/source.pdf', SyntheticDocument::pdf('Different QA bytes - same identity')); $this->artisan(new ImportDocumentBatch, $options)->assertSuccessful(); $this->assertDatabaseCount('documents', 1); $this->assertDatabaseCount('portal_resources', 1); $this->assertSame($bytes, Storage::disk('local')->get($document->storage_key)); $this->assertDatabaseHas('portal_resources', ['id' => $resource->id, 'published' => false]); $this->assertSame(1, DB::table('audit_events')->where('action', 'private_document_batch_imported')->count()); $this->actingAs(User::factory()->create())->get('/management/documents/'.$document->id.'/download')->assertForbidden(); } }