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(); } }