150 lines
8.8 KiB
PHP
150 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Jobs\ScanDocumentForMalware;
|
|
use App\Models\LandAssistanceCase;
|
|
use App\Models\User;
|
|
use App\Services\Malware\MalwareScanner;
|
|
use App\Services\PartnershipExchange;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use Tests\Support\SyntheticDocument;
|
|
use Tests\TestCase;
|
|
|
|
class IndependentDocumentOperationsTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
Storage::fake('local');
|
|
Queue::fake();
|
|
config(['services.malware.scanner' => 'clamd', 'services.malware.host' => '127.0.0.1', 'services.malware.port' => 3310]);
|
|
if (! app(MalwareScanner::class)->health()['available']) {
|
|
$this->markTestSkipped('Real ClamD required; no fabricated scan proof.');
|
|
}
|
|
}
|
|
|
|
private function files(): array
|
|
{
|
|
$image = imagecreatetruecolor(16, 16);
|
|
ob_start();
|
|
imagepng($image);
|
|
$png = ob_get_clean();
|
|
ob_start();
|
|
imagejpeg($image);
|
|
$jpeg = ob_get_clean();
|
|
imagedestroy($image);
|
|
|
|
return ['qa.pdf' => SyntheticDocument::pdf(), 'qa.png' => $png, 'qa.jpg' => $jpeg];
|
|
}
|
|
|
|
private function scan(string $id): object
|
|
{
|
|
(new ScanDocumentForMalware($id))->handle(app(MalwareScanner::class));
|
|
$document = DB::table('documents')->where('id', $id)->first();
|
|
$this->assertSame('active', $document->status);
|
|
$this->assertSame('clean', $document->malware_scan_status);
|
|
$this->assertSame('ClamAV', $document->malware_engine);
|
|
$this->assertSame(trim($document->sha256), $document->malware_scanned_sha256);
|
|
$this->assertNotNull($document->malware_signatures_at);
|
|
|
|
return $document;
|
|
}
|
|
|
|
public function test_land_pdf_png_jpeg_scan_recovery_print_and_profile_permissions_preserve_originals(): void
|
|
{
|
|
$owner = User::factory()->create();
|
|
$manager = User::factory()->create();
|
|
config(['portal.managers' => [$manager->email]]);
|
|
$this->actingAs($owner)->postJson('/investidor/terra-duat', ['payload' => ['full_name' => 'Synthetic QA investor']])->assertCreated();
|
|
$case = LandAssistanceCase::firstOrFail();
|
|
$base = '/investidor/terra-duat/'.$case->id;
|
|
foreach ($this->files() as $name => $bytes) {
|
|
$this->post($base.'/documentos', ['version' => $case->fresh()->version, 'category' => str_ends_with($name, '.pdf') ? 'prior_right' : 'photo',
|
|
'file' => UploadedFile::fake()->createWithContent($name, $bytes)], ['Accept' => 'application/json'])->assertCreated();
|
|
$documentId = DB::table('documents')->where('file_name', $name)->value('id');
|
|
$link = DB::table('land_assistance_documents')->where('case_id', $case->id)->where('document_id', $documentId)->first();
|
|
$url = $base.'/documentos/'.$link->id;
|
|
$this->get($url)->assertNotFound();
|
|
$document = $this->scan($link->document_id);
|
|
$response = $this->get($url)->assertOk()->assertDownload($name);
|
|
$this->assertSame($bytes, file_get_contents($response->baseResponse->getFile()->getPathname()));
|
|
// Existing canonical encrypted originals must remain readable without making plaintext copies.
|
|
Storage::disk('local')->put($document->storage_key, Crypt::encryptString($bytes));
|
|
DB::table('documents')->where('id', $document->id)->update(['storage_encryption' => 'laravel']);
|
|
$this->get($url)->assertOk()->assertContent($bytes);
|
|
$this->assertSame($owner->id, $document->uploaded_by);
|
|
$this->assertSame(strlen($bytes), $document->size_bytes);
|
|
$this->actingAs($manager)->get($url)->assertNotFound();
|
|
$this->actingAs(User::factory()->create())->get($url)->assertNotFound();
|
|
$this->actingAs($owner)->getJson($base)->assertOk();
|
|
$this->assertSame($bytes, Crypt::decryptString(Storage::disk('local')->get($document->storage_key)));
|
|
Storage::disk('local')->put($document->storage_key, Crypt::encryptString('Tampered QA only'));
|
|
$this->get($url)->assertNotFound();
|
|
}
|
|
$this->get($base.'/imprimir')->assertOk(); // Printable response; browser PDF rendering remains pending.
|
|
$this->assertSame(3, DB::table('documents')->count());
|
|
$this->assertSame(3, DB::table('document_malware_scans')->where('result', 'clean')->count());
|
|
}
|
|
|
|
public function test_partnership_real_scans_sharing_revocation_new_session_and_encrypted_originals(): void
|
|
{
|
|
$owner = User::factory()->create();
|
|
$candidate = User::factory()->create();
|
|
$manager = User::factory()->create();
|
|
$outsider = User::factory()->create();
|
|
config(['portal.managers' => [$manager->email]]);
|
|
$exchange = app(PartnershipExchange::class);
|
|
$record = $exchange->save($owner, ['client_key' => (string) Str::uuid(), 'title' => 'Synthetic document QA partnership', 'kind' => 'seeking', 'partnership_type' => 'joint_venture',
|
|
'offer' => 'Synthetic QA capacity', 'seeking' => 'Synthetic QA contribution', 'payload' => ['summary' => 'Synthetic QA only', 'consent' => true], 'origins' => [], 'locations' => []]);
|
|
$record = $exchange->transition($owner, $record->id, $record->version, 'submit', 'QA private document test');
|
|
$record = $exchange->transition($manager, $record->id, $record->version, 'publish', 'QA fixture only; no active disclosure');
|
|
$interest = $exchange->interest($candidate, $record->id, ['proposal' => 'Synthetic QA proposal', 'payload' => ['consent' => true]]);
|
|
$interest = $exchange->decideInterest($owner, $interest->id, $interest->version, 'shortlist', 'QA room preparation');
|
|
$exchange->decideInterest($owner, $interest->id, $interest->version, 'connect', 'QA private room access');
|
|
$room = DB::table('partnership_rooms')->where('interest_id', $interest->id)->first();
|
|
$base = '/parcerias/salas/'.$room->id;
|
|
foreach ($this->files() as $name => $bytes) {
|
|
$this->actingAs($owner)->post($base.'/documentos', ['shared' => false, 'file' => UploadedFile::fake()->createWithContent($name, $bytes)], ['Accept' => 'application/json'])->assertOk();
|
|
$link = DB::table('partnership_documents')->where('room_id', $room->id)->orderByDesc('id')->first();
|
|
$url = $base.'/documentos/'.$link->id;
|
|
$this->get($url)->assertNotFound();
|
|
$document = $this->scan($link->document_id);
|
|
$response = $this->get($url)->assertOk()->assertDownload($name);
|
|
$this->assertSame($bytes, file_get_contents($response->baseResponse->getFile()->getPathname()));
|
|
$this->actingAs($candidate)->get($url)->assertNotFound();
|
|
$this->postJson($url.'/partilha', ['shared' => true])->assertForbidden();
|
|
$this->actingAs($owner)->postJson($url.'/partilha', ['shared' => true])->assertOk();
|
|
Storage::disk('local')->put($document->storage_key, Crypt::encryptString($bytes));
|
|
DB::table('documents')->where('id', $document->id)->update(['storage_encryption' => 'laravel']);
|
|
$this->actingAs($candidate)->get($url)->assertOk()->assertContent($bytes);
|
|
$this->assertSame($owner->id, $document->uploaded_by);
|
|
$this->assertSame(strlen($bytes), $document->size_bytes);
|
|
// A new authentication context recovers shared documents from the repository.
|
|
auth()->logout();
|
|
$this->actingAs($candidate->fresh())->getJson($base)->assertOk()->assertJsonCount(1, 'documents');
|
|
$this->get($url)->assertOk()->assertContent($bytes);
|
|
$this->actingAs($manager)->get($url)->assertForbidden();
|
|
$this->actingAs($outsider)->get($url)->assertForbidden();
|
|
$this->actingAs($owner)->postJson($url.'/partilha', ['shared' => false])->assertOk();
|
|
$this->actingAs($candidate)->get($url)->assertNotFound();
|
|
$this->getJson($base)->assertOk()->assertJsonCount(0, 'documents');
|
|
$this->actingAs($owner)->get($url)->assertOk()->assertContent($bytes);
|
|
$this->assertSame($bytes, Crypt::decryptString(Storage::disk('local')->get($document->storage_key)));
|
|
Storage::disk('local')->put($document->storage_key, Crypt::encryptString('Tampered QA only'));
|
|
$this->get($url)->assertNotFound();
|
|
}
|
|
$this->assertSame(3, DB::table('documents')->count());
|
|
$this->assertSame(6, DB::table('partnership_events')->where('event_type', 'partnership.document_sharing_changed.v1')->count());
|
|
$this->assertSame(3, DB::table('document_malware_scans')->where('result', 'clean')->count());
|
|
}
|
|
}
|