withoutVite(); Http::fake(); Mail::fake(); Queue::fake(); Storage::fake('local'); $this->manager = User::factory()->create(); config(['portal.managers' => [$this->manager->email], 'portal.document_managers' => [], 'suitecrm.enabled' => false, 'investmoz_ai.enabled' => false]); $this->actingAs($this->manager); $this->id = (string) Str::uuid(); $bytes = "%PDF-1.7\nSYNTHETIC retry document\n%%EOF"; $this->cipher = Crypt::encryptString($bytes); Storage::disk('local')->put('opportunity-resources/'.$this->id, $this->cipher); DB::table('documents')->insert(['id' => $this->id, 'source_system' => 'portal-services', 'file_name' => 'PRIVATE-synthetic.pdf', 'storage_key' => 'opportunity-resources/'.$this->id, 'sha256' => hash('sha256', $bytes), 'size_bytes' => strlen($bytes), 'media_type' => 'application/pdf', 'status' => 'quarantined', 'data_classification' => 'restricted', 'storage_encryption' => 'laravel', 'uploaded_by' => $this->manager->id, 'malware_scan_status' => 'inconclusive', 'malware_failure_reason' => 'scanner_unavailable']); } private function retry(): TestResponse { return $this->from('/management/portal')->post('/management/documents/'.$this->id.'/malware/retry'); } private function document(): \stdClass { return DB::table('documents')->where('id', $this->id)->first(); } private function assertOriginal(): void { $this->assertSame($this->cipher, Storage::disk('local')->get('opportunity-resources/'.$this->id)); $this->assertSame(1, DB::table('documents')->count()); } public function test_http_retry_confirms_dispatch_acceptance_and_audits_the_existing_manager(): void { $this->retry()->assertRedirect('/management/portal')->assertSessionHas('success', fn ($value) => str_contains($value, 'aceite pelo serviço de envio')); Queue::assertPushed(ScanDocumentForMalware::class, 1); $this->assertSame('pending', $this->document()->malware_scan_status); $this->assertSame('quarantined', $this->document()->status); $event = DB::table('audit_events')->where('action', 'scan_retry_requested')->sole(); $this->assertSame($this->manager->id, $event->actor_user_id); $data = json_decode($event->after_data, true); $this->assertSame($this->id, $data['document_id']); $this->assertTrue(Str::isUuid($data['reference'])); $this->assertStringNotContainsString('PRIVATE', $event->after_data); $this->assertOriginal(); } public function test_an_existing_unique_lease_neither_resets_failure_nor_claims_another_push(): void { $job = new ScanDocumentForMalware($this->id); $this->assertTrue((new UniqueLock(app(Repository::class)))->acquire($job)); $before = $this->document(); $this->retry()->assertRedirect()->assertSessionMissing('success')->assertSessionHas('warning', fn ($value) => str_contains($value, 'reserva de análise')); Queue::assertNothingPushed(); $this->assertEquals($before, $this->document()); $this->assertSame(0, DB::table('audit_events')->where('action', 'scan_retry_requested')->count()); $this->assertOriginal(); } public function test_transport_failure_is_reported_as_uncertain_and_does_not_erase_the_original(): void { Bus::shouldReceive('dispatch')->once()->andThrow(new \RuntimeException('PRIVATE queue password and SQL')); $this->retry()->assertRedirect()->assertSessionMissing('success')->assertSessionHas('warning', fn ($value) => str_contains($value, 'Não foi possível confirmar')); $this->assertStringNotContainsString('PRIVATE', session('warning')); $this->assertSame('failed', $this->document()->malware_scan_status); $this->assertSame('quarantined', $this->document()->status); $this->assertSame(0, DB::table('document_malware_scans')->count()); $this->assertNull(Context::getHidden('laravel_unique_job_key')); $this->assertFalse((new UniqueLock(app(Repository::class)))->acquire(new ScanDocumentForMalware($this->id))); $this->assertOriginal(); } public function test_lost_acknowledgement_after_a_sync_clean_result_does_not_requarantine_it(): void { Bus::shouldReceive('dispatch')->once()->andReturnUsing(function ($job) { $job->handle(new CleanScannerFixture); throw new \RuntimeException('PRIVATE acknowledgement lost'); }); $this->retry()->assertRedirect()->assertSessionHas('warning')->assertSessionMissing('success'); $this->assertSame('clean', $this->document()->malware_scan_status); $this->assertSame('active', $this->document()->status); $this->assertSame(1, DB::table('document_malware_scans')->count()); $this->assertOriginal(); } public function test_outer_rollback_discards_the_request_and_a_later_clean_result_blocks_a_stale_callback(): void { DB::beginTransaction(); $result = app(DocumentScanRetry::class)->request($this->manager, $this->id); $this->assertSame('deferred', $result['outcome']); DB::rollBack(); Queue::assertNothingPushed(); $this->assertSame('inconclusive', $this->document()->malware_scan_status); DB::beginTransaction(); app(DocumentScanRetry::class)->request($this->manager, $this->id); DB::table('documents')->where('id', $this->id)->update(['status' => 'active', 'malware_scan_status' => 'clean']); DB::commit(); Queue::assertNothingPushed(); $this->assertSame('clean', $this->document()->malware_scan_status); $this->assertTrue((new UniqueLock(app(Repository::class)))->acquire(new ScanDocumentForMalware($this->id))); $this->assertSame(0, DB::table('audit_events')->where('action', 'scan_retry_requested')->count()); } public function test_current_permission_is_rechecked_after_commit_before_any_scan_state_change(): void { DB::beginTransaction(); app(DocumentScanRetry::class)->request($this->manager, $this->id); config(['portal.managers' => []]); DB::commit(); Queue::assertNothingPushed(); $this->assertSame('inconclusive', $this->document()->malware_scan_status); $this->assertSame(0, DB::table('audit_events')->where('action', 'scan_retry_requested')->count()); $this->assertTrue((new UniqueLock(app(Repository::class)))->acquire(new ScanDocumentForMalware($this->id))); } public function test_support_without_document_management_authority_cannot_request_a_scan(): void { $support = User::factory()->create(); app(PortalProfileAdministration::class)->insertGrant((string) Str::uuid(), $this->manager, ['user_id' => $support->id, 'role' => 'technical_support', 'opportunity_types' => ['investment_project'], 'source' => 'synthetic-retry-support-fixture']); $this->actingAs($support); $this->retry()->assertForbidden(); Queue::assertNothingPushed(); $this->assertSame('inconclusive', $this->document()->malware_scan_status); } public function test_clean_infected_scanning_and_deleted_documents_are_not_reset_by_retry(): void { foreach (['clean', 'infected', 'scanning'] as $state) { DB::table('documents')->where('id', $this->id)->update(['malware_scan_status' => $state]); $this->retry()->assertStatus(422); $this->assertSame($state, $this->document()->malware_scan_status); } DB::table('documents')->where('id', $this->id)->update(['status' => 'deleted', 'deleted_at' => now()]); $this->retry()->assertNotFound(); Queue::assertNothingPushed(); } public function test_different_documents_have_independent_delivery_leases_and_stale_preparation_can_be_retried(): void { $first = new ScanDocumentForMalware($this->id); $delivery = app(DocumentScanDelivery::class); $this->assertSame('stale', $delivery->dispatch($first, fn () => false)); $this->assertSame('accepted', $delivery->dispatch(new ScanDocumentForMalware($this->id))); $this->assertSame('duplicate', $delivery->dispatch(new ScanDocumentForMalware($this->id))); $this->assertSame('accepted', $delivery->dispatch(new ScanDocumentForMalware((string) Str::uuid()))); Queue::assertPushed(ScanDocumentForMalware::class, 2); } public function test_json_retry_has_no_redirect_and_distinguishes_duplicate_from_transport_failure(): void { $url = '/management/documents/'.$this->id.'/malware/retry'; $this->postJson($url)->assertOk()->assertJsonPath('outcome', 'accepted')->assertHeader('Cache-Control', 'no-store, private'); $this->postJson($url)->assertOk()->assertJsonPath('outcome', 'duplicate'); Queue::assertPushed(ScanDocumentForMalware::class, 1); } public function test_json_transport_failure_returns_only_a_safe_reference_with_the_uncertain_status(): void { Bus::shouldReceive('dispatch')->once()->andThrow(new \RuntimeException('PRIVATE transport payload')); $response = $this->postJson('/management/documents/'.$this->id.'/malware/retry')->assertStatus(503)->assertJsonPath('outcome', 'unconfirmed'); $this->assertTrue(Str::isUuid($response->json('reference'))); $this->assertStringNotContainsString('PRIVATE', $response->getContent()); $this->assertSame('failed', $this->document()->malware_scan_status); } }