Files
Investidor-V2/tests/Feature/PortalAuditNextLotTest.php
T
nala 6ddafc95ba
tests / ci (push) Canceled after 0s
oportunidades 4
2026-10-10 07:03:59 +02:00

146 lines
13 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\ExistingBusiness;
use App\Models\InterestRequest;
use App\Models\LandAssistanceCase;
use App\Models\Opportunity;
use App\Models\User;
use App\Services\DeclaredOriginStatistics;
use App\Services\OpportunityManagement;
use App\Services\OpportunityMaturity;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Inertia\Testing\AssertableInertia as Assert;
use Tests\TestCase;
class PortalAuditNextLotTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->withoutVite();
$identity = DB::selectOne('SELECT current_database() AS database,inet_server_port() AS port');
$this->assertStringEndsWith('_test', $identity->database);
$this->assertSame(55441, (int) $identity->port);
config(['portal.notifications_enabled' => false, 'existing-business.enabled' => true, 'investmoz_ai.enabled' => false, 'suitecrm.enabled' => false]);
Http::fake();
}
public function test_eight_maturity_stages_recover_versioned_without_reinterpreting_legacy_or_publishing(): void
{
$manager = User::factory()->create();
config(['portal.managers' => [$manager->email]]);
$this->actingAs($manager);
foreach (array_keys(OpportunityMaturity::STAGES) as $index => $stage) {
$input = ['creation_key' => (string) Str::uuid(), 'reason' => 'Verificação das oito etapas exclusivamente na QA', 'maturity_level' => $index + 1];
if ($index < 6) {
$input['maturity_catalogue_version'] = OpportunityMaturity::VERSION;
}
$result = $this->postJson('/management/opportunities/drafts', $input)->assertCreated()->json();
$record = Opportunity::findOrFail($result['id']);
$stored = json_decode($record->management_draft, true, 512, JSON_THROW_ON_ERROR);
$this->assertSame($stage, $stored['maturity_stage']);
$this->assertSame($index + 1, $stored['maturity_level']);
$this->assertSame(OpportunityMaturity::VERSION, $stored['maturity_catalogue_version']);
$this->assertNull($record->maturity_level);
$this->assertSame('draft', $record->status);
$this->postJson('/management/opportunities/drafts', $input)->assertCreated()->assertJsonPath('id', $record->id);
$this->get('/management/opportunities/'.$record->id.'/edit')->assertInertia(fn (Assert $page) => $page
->has('maturityCatalogue.stages', 8)->where('values.maturity_stage', $stage)->where('values.maturity_catalogue_version', OpportunityMaturity::VERSION));
}
$legacy = $this->postJson('/management/opportunities/drafts', ['creation_key' => (string) Str::uuid(), 'reason' => 'Preservar significado histórico exclusivamente QA', 'maturity_level' => 6])->assertCreated()->json();
$record = Opportunity::findOrFail($legacy['id']);
$label = $record->maturity_label;
$this->assertSame(6, $record->maturity_level);
$this->assertSame(OpportunityMaturity::LEGACY_LABELS[6], $label);
$this->putJson('/management/opportunities/'.$record->id.'/draft', ['etag' => $legacy['etag'], 'reason' => 'Rejeitar conversão implícita do catálogo histórico', 'maturity_catalogue_version' => OpportunityMaturity::VERSION])->assertUnprocessable()->assertJsonValidationErrors('maturity_catalogue_version');
$updated = $this->putJson('/management/opportunities/'.$record->id.'/draft', ['etag' => $legacy['etag'], 'reason' => 'Escolha explícita da nova etapa na versão QA', 'maturity_stage' => 'operational', 'maturity_catalogue_version' => OpportunityMaturity::VERSION])->assertOk()->json();
$this->assertSame(6, $record->fresh()->maturity_level);
$this->assertSame($label, $record->fresh()->maturity_label);
$this->assertSame('operational', app(OpportunityManagement::class)->values($record->fresh())['maturity_stage']);
$this->putJson('/management/opportunities/'.$record->id.'/draft', ['etag' => $updated['etag'], 'reason' => 'Rejeitar etapa e posição incoerentes em QA', 'maturity_level' => 7, 'maturity_stage' => 'operational'])->assertUnprocessable()->assertJsonValidationErrors('maturity_stage');
$this->putJson('/management/opportunities/'.$record->id.'/draft', ['etag' => $legacy['etag'], 'reason' => 'Rejeitar gravação concorrente exclusivamente QA', 'maturity_level' => 8])->assertConflict();
$this->assertSame(9, Opportunity::count());
$this->assertSame(0, Opportunity::published()->count());
$this->actingAs(User::factory()->create())->postJson('/management/opportunities/drafts', ['maturity_level' => 8])->assertForbidden();
}
public function test_business_country_origin_save_recover_filters_submission_statistics_and_actor_isolation(): void
{
$owner = User::factory()->create();
$manager = User::factory()->create();
config(['portal.managers' => [$manager->email]]);
$payload = ['title' => 'Negócio sintético QA', 'country' => 'PT', 'declared_origin_country_code' => 'ZA', 'wizard_version' => '1'];
$input = ['modality' => 'company_sale', 'classification' => 'confidential', 'reason' => 'País e origem independentes exclusivamente QA', 'payload' => $payload];
$this->actingAs($owner)->postJson('/api/v1/existing-business/listings', [...$input, 'payload' => [...$payload, 'country' => 'ZZ']])->assertUnprocessable()->assertJsonValidationErrors('payload.country');
$record = $this->postJson('/api/v1/existing-business/listings', $input)->assertCreated()->assertJsonPath('payload.country', 'PT')->assertJsonPath('payload.declared_origin_country_code', 'ZA')->json();
$url = '/api/v1/existing-business/listings/'.$record['id'];
$this->getJson($url)->assertOk()->assertJsonPath('record.payload.country', 'PT')->assertJsonPath('record.payload.declared_origin_country_code', 'ZA')->assertJsonCount(249, 'countries');
$this->getJson('/api/v1/existing-business/listings?country=PT&origin=ZA')->assertOk()->assertJsonPath('records.total', 1);
$this->getJson('/api/v1/existing-business/listings?origin=PT')->assertOk()->assertJsonPath('records.total', 0);
$this->assertSame([], app(DeclaredOriginStatistics::class)->forManager($manager)['businesses']);
$this->putJson($url, [...$input, 'version_no' => 1, 'payload' => [...$payload, 'country' => 'Portugal']])->assertOk()->assertJsonPath('version_no', 2);
$this->getJson($url)->assertOk()->assertJsonPath('record.payload.country', 'Portugal');
$this->putJson($url, [...$input, 'version_no' => 2, 'payload' => $payload])->assertOk()->assertJsonPath('version_no', 3);
$payload = [...$payload, 'proponent_type' => 'person', 'proponent_name' => 'Promotor QA', 'tax_identifier' => 'SYNTHETIC', 'contact_email' => 'person@example.test', 'acting_capacity' => 'holder', 'declared_holder' => 'Promotor QA', 'location_notes' => 'Localização declarada QA', 'legitimacy_declaration' => 'yes', 'description' => 'Descrição sintética para permitir a submissão deste percurso privado na QA.', 'source_reference' => 'Referência sintética exclusivamente QA', 'valid_until' => today()->addMonth()->toDateString()];
$this->putJson($url, [...$input, 'version_no' => 3, 'payload' => $payload])->assertOk()->assertJsonPath('version_no', 4);
$this->putJson($url, [...$input, 'version_no' => 4, 'payload' => [...$payload, 'declared_origin_country_code' => 'ZZ']])->assertUnprocessable()->assertJsonValidationErrors('payload.declared_origin_country_code');
$this->assertSame(4, ExistingBusiness::findOrFail($record['id'])->version_no);
$this->postJson($url.'/transitions', ['version_no' => 4, 'action' => 'submitted', 'note' => 'Submissão privada exclusivamente na base QA', 'review_consent' => true, 'confirmation_code' => $record['code']])->assertOk()->assertJsonPath('status', 'submitted');
$this->getJson($url)->assertOk()->assertJsonPath('record.payload.declared_origin_country_code', 'ZA')->assertJsonPath('record.payload.country', 'PT');
$stats = app(DeclaredOriginStatistics::class)->forManager($manager, 'ZA');
$this->assertSame(1, $stats['businesses'][0]['count']);
$this->assertSame('ZA', $stats['businesses'][0]['code']);
$this->assertSame([], $stats['interests']);
$this->assertSame([], app(DeclaredOriginStatistics::class)->forManager($manager, 'PT')['businesses']);
$this->actingAs($manager)->get('/management/interests?origin=ZA')->assertInertia(fn (Assert $page) => $page->where('originStatistics.businesses.0.count', 1));
$this->actingAs(User::factory()->create())->getJson($url)->assertForbidden();
$this->getJson('/api/v1/existing-business/listings?country=PT&origin=ZA')->assertOk()->assertJsonPath('records.total', 0);
$this->assertDatabaseCount('existing_business_revisions', 5);
$this->assertStringNotContainsString('person@example.test', (string) DB::table('existing_businesses')->where('id', $record['id'])->value('payload'));
}
public function test_land_country_origin_and_four_territory_levels_recover_in_support_filters_statistics(): void
{
$owner = User::factory()->create();
$manager = User::factory()->create();
config(['portal.managers' => [$manager->email]]);
$path = [];
$parent = (string) Str::uuid();
DB::table('geo_locations')->insert(['id' => $parent, 'code' => $parent, 'source_system' => 'cartografia-workbook', 'external_id' => '964a187ad32ea553b9e112cd159caa90b900050c42b7329594a75bcd798a96ff:'.$parent, 'data_classification' => 'public', 'name' => 'QA country', 'location_type' => 'country']);
foreach (['province', 'district', 'admin_post', 'locality'] as $type) {
$id = (string) Str::uuid();
DB::table('geo_locations')->insert(['id' => $id, 'code' => $id, 'source_system' => 'cartografia-workbook', 'external_id' => '964a187ad32ea553b9e112cd159caa90b900050c42b7329594a75bcd798a96ff:'.$id, 'data_classification' => 'public', 'name' => 'QA '.$type, 'location_type' => $type, 'parent_id' => $parent]);
$path[$type === 'admin_post' ? 'admin_post_id' : $type.'_id'] = $id;
$parent = $id;
}
$payload = ['country' => 'PT', 'declared_origin_country_code' => 'ZA', 'support_kind' => 'investment', 'request_description' => 'Pedido sintético de apoio fundiário exclusivamente QA', 'applicant_category' => 'individual', 'full_name' => 'Pessoa QA', 'email' => 'land@example.test', 'preferred_contact' => 'email', 'area_status' => 'identified', 'purpose' => 'Preparação sintética QA', 'parcels' => [$path], 'contact_consent' => true, 'truth_declaration' => true];
$this->actingAs($owner)->get('/investidor/terra-duat/novo')->assertInertia(fn (Assert $page) => $page->has('countries', 249));
$record = $this->postJson('/investidor/terra-duat', ['payload' => $payload])->assertCreated()->json();
$url = '/investidor/terra-duat/'.$record['id'];
$this->getJson($url)->assertOk()->assertJsonPath('case.payload.country', 'PT')->assertJsonPath('case.payload.declared_origin_country_code', 'ZA')->assertJsonPath('case.payload.parcels.0.locality_id', $parent)->assertJsonCount(4, 'territoryPaths.0')->assertJsonPath('territoryPaths.0.3.id', $parent)->assertJsonPath('declaredOrigin.code', 'ZA');
$this->putJson($url, ['version' => 1, 'payload' => [...$payload, 'country' => 'Moçambique']])->assertOk()->assertJsonPath('version', 2);
$this->getJson($url)->assertOk()->assertJsonPath('case.payload.country', 'Moçambique');
$this->putJson($url, ['version' => 2, 'payload' => [...$payload, 'declared_origin_country_code' => 'ZZ']])->assertUnprocessable()->assertJsonValidationErrors('payload.declared_origin_country_code');
$this->assertSame(2, LandAssistanceCase::findOrFail($record['id'])->version);
$this->postJson($url.'/solicitar-apoio', ['version' => 2, 'payload' => $payload])->assertAccepted();
$interest = InterestRequest::firstOrFail();
$this->assertSame('PT', $interest->country);
$this->assertSame('ZA', $interest->metadata['declared_origin_country_code']);
$this->getJson($url)->assertOk()->assertJsonPath('case.payload.parcels.0.locality_id', $parent)->assertJsonPath('case.payload.declared_origin_country_code', 'ZA');
$this->actingAs($manager)->get('/management/interests?country=PT&origin=ZA')->assertInertia(fn (Assert $page) => $page->where('requests.total', 1)->where('requests.data.0.country', 'PT')->where('requests.data.0.declaredOrigin.code', 'ZA')->where('originStatistics.interests.0.count', 1));
$this->get('/management/interests?origin=PT')->assertInertia(fn (Assert $page) => $page->where('requests.total', 0)->has('originStatistics.interests', 0));
$this->get('/management/interests?origin=unknown')->assertInertia(fn (Assert $page) => $page->where('requests.total', 0));
$this->actingAs(User::factory()->create())->getJson($url)->assertNotFound();
$this->get('/management/interests?origin=ZA')->assertForbidden();
$this->assertDatabaseCount('interest_requests', 1);
Http::assertNothingSent();
}
}