179 lines
13 KiB
PHP
179 lines
13 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\InterestRequest;
|
|
use App\Models\SimulationFormDraft;
|
|
use App\Models\User;
|
|
use App\Services\CountryCatalogue;
|
|
use App\Services\VirtualDesk;
|
|
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 PortalAuditContinuationTest 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, 'investmoz_ai.enabled' => false, 'suitecrm.enabled' => false]);
|
|
Http::fake();
|
|
}
|
|
|
|
private function publicPayload(): array
|
|
{
|
|
return ['fullName' => 'Pessoa sintética QA', 'email' => 'portal-integration-test@gmail.com', 'investorType' => 'individual',
|
|
'country' => 'PT', 'declared_origin_country_code' => 'ZA', 'sectorInterest' => 'Energia',
|
|
'message' => 'Pedido exclusivamente sintético na base QA.', 'privacyConsent' => true];
|
|
}
|
|
|
|
public function test_public_country_and_origin_survive_api_storage_management_recovery_and_statistics(): void
|
|
{
|
|
foreach (['/', '/manifestar-interesse', '/apoio', '/formularios/investidor', '/formularios/empresa'] as $url) {
|
|
$this->get($url)->assertInertia(fn (Assert $page) => $page->has('countries', 249));
|
|
}
|
|
$data = $this->publicPayload();
|
|
$this->postJson('/api/v1/interest-requests', [...$data, 'country' => 'ZZ'], ['Idempotency-Key' => (string) Str::uuid()])->assertUnprocessable()->assertJsonValidationErrors('country');
|
|
$this->assertDatabaseCount('interest_requests', 0);
|
|
$key = ['Idempotency-Key' => (string) Str::uuid()];
|
|
$receipt = $this->postJson('/api/v1/interest-requests', $data, $key)->assertCreated()->json();
|
|
$this->postJson('/api/v1/interest-requests', $data, $key)->assertExactJson($receipt);
|
|
$interest = InterestRequest::firstOrFail();
|
|
$this->assertSame('PT', $interest->country);
|
|
$this->assertSame('ZA', $interest->metadata['declared_origin_country_code']);
|
|
$manager = User::factory()->create();
|
|
config(['portal.managers' => [$manager->email]]);
|
|
$this->actingAs($manager)->get('/management/interests/'.$interest->id)->assertInertia(fn (Assert $page) => $page
|
|
->component('management/interest')->where('interest.country', 'PT')->where('declaredOrigin.code', 'ZA')->where('declaredOrigin.name', app(CountryCatalogue::class)->describe('ZA')['name']));
|
|
$this->get('/management/crm/leads?origin=ZA')->assertInertia(fn (Assert $page) => $page
|
|
->where('originStatistics.interests.0.code', 'ZA')->where('originStatistics.interests.0.count', 1));
|
|
$this->get('/management/crm/leads?origin=PT')->assertInertia(fn (Assert $page) => $page->has('originStatistics.interests', 0));
|
|
$this->actingAs(User::factory()->create())->get('/management/interests/'.$interest->id)->assertForbidden();
|
|
$this->assertDatabaseCount('interest_requests', 1);
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_support_kinds_keep_iso_country_separate_and_legacy_text_unchanged(): void
|
|
{
|
|
$data = $this->publicPayload();
|
|
$manager = User::factory()->create();
|
|
config(['portal.managers' => [$manager->email]]);
|
|
foreach (['support', 'investor', 'company', 'partnership'] as $index => $kind) {
|
|
$payload = [...$data, 'kind' => $kind, 'company' => 'Empresa sintética QA', 'submission_key' => (string) Str::uuid()];
|
|
$this->post('/apoio', $payload)->assertSessionHasNoErrors()->assertRedirect();
|
|
$this->assertSame('PT', InterestRequest::latest('id')->firstOrFail()->country);
|
|
$this->assertSame('ZA', InterestRequest::latest('id')->firstOrFail()->metadata['declared_origin_country_code']);
|
|
$interest = InterestRequest::latest('id')->firstOrFail();
|
|
$this->actingAs($manager)->get('/management/interests/'.$interest->id)->assertInertia(fn (Assert $p) => $p
|
|
->where('interest.country', 'PT')->where('declaredOrigin.code', 'ZA'));
|
|
$this->get('/management/interests?country=PT&origin=ZA')->assertInertia(fn (Assert $p) => $p
|
|
->where('requests.total', $index + 1)->where('originStatistics.interests.0.code', 'ZA')
|
|
->where('originStatistics.interests.0.count', $index + 1));
|
|
$this->get('/management/interests?origin=PT')->assertInertia(fn (Assert $p) => $p->where('requests.total', 0)->has('originStatistics.interests', 0));
|
|
}
|
|
$this->postJson('/apoio', [...$data, 'kind' => 'support', 'country' => 'ZZ', 'submission_key' => (string) Str::uuid()])->assertUnprocessable()->assertJsonValidationErrors('country');
|
|
$this->post('/apoio', [...$data, 'kind' => 'support', 'country' => 'Moçambique', 'submission_key' => (string) Str::uuid()])->assertSessionHasNoErrors();
|
|
$this->assertSame('Moçambique', InterestRequest::latest('id')->firstOrFail()->country);
|
|
$this->assertDatabaseCount('interest_requests', 5);
|
|
}
|
|
|
|
private function territory(string $type, ?string $parent = null, bool $public = true): string
|
|
{
|
|
$id = (string) Str::uuid();
|
|
DB::table('geo_locations')->insert(['id' => $id, 'code' => $id, 'name' => 'Território sintético QA '.$type, 'location_type' => $type,
|
|
'parent_id' => $parent, 'source_system' => 'cartografia-workbook', 'external_id' => '964a187ad32ea553b9e112cd159caa90b900050c42b7329594a75bcd798a96ff:'.$id,
|
|
'data_classification' => $public ? 'public' : 'internal']);
|
|
|
|
return $id;
|
|
}
|
|
|
|
public function test_iso_catalogue_does_not_depend_on_geo_publication_and_is_searchable_and_pageable(): void
|
|
{
|
|
$this->assertDatabaseCount('geo_locations', 0);
|
|
$this->assertDatabaseCount('reference_catalog_versions', 0);
|
|
$codes = [];
|
|
foreach ([1, 2, 3] as $page) {
|
|
$rows = $this->getJson('/api/v1/catalogs/countries?per_page=100&page='.$page)->assertOk()->assertJsonPath('total', 249)->json('data');
|
|
$codes = [...$codes, ...array_column($rows, 'code')];
|
|
}
|
|
$this->assertCount(249, array_unique($codes));
|
|
$this->assertContains('PT', $codes);
|
|
$this->assertContains('ZA', $codes);
|
|
$this->getJson('/api/v1/catalogs/countries?q=Mocambique')->assertOk()->assertJsonPath('data.0.value', 'MZ');
|
|
$this->getJson('/api/v1/catalogs/countries?q=Portugal')->assertOk()->assertJsonPath('data.0.value', 'PT');
|
|
$this->getJson('/api/v1/catalogs/countries?parent_id='.Str::uuid())->assertUnprocessable();
|
|
$this->getJson('/api/v1/catalogs/countries?per_page=101')->assertUnprocessable();
|
|
$this->assertDatabaseCount('geo_locations', 0);
|
|
$this->assertDatabaseCount('reference_catalog_versions', 0);
|
|
}
|
|
|
|
public function test_simulator_recovers_iso_countries_in_private_draft_without_reinterpreting_legacy_values(): void
|
|
{
|
|
$owner = User::factory()->create();
|
|
$this->actingAs($owner);
|
|
$data = ['personal_snapshot' => ['fiscal_residency_country' => 'PT'],
|
|
'economic_snapshot' => ['purpose' => 'simulate_only', 'journey_type' => 'investor', 'tax_calculation' => 'no', 'funds_origin_countries' => ['PT', 'ZA']]];
|
|
$headers = ['Idempotency-Key' => (string) Str::uuid()];
|
|
$created = $this->postJson('/api/v1/simulation-drafts', $data, $headers)->assertCreated();
|
|
$id = $created->json('data.id');
|
|
$row = DB::table('simulation_form_drafts')->where('id', $id)->first();
|
|
$this->assertSame('PT', SimulationFormDraft::decryptSnapshot($row->personal_snapshot)['fiscal_residency_country']);
|
|
$this->assertSame(['PT', 'ZA'], SimulationFormDraft::decryptSnapshot($row->economic_snapshot)['funds_origin_countries']);
|
|
$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->assertJsonPath('data.personal_snapshot.fiscal_residency_country', 'PT')->assertJsonPath('data.economic_snapshot.funds_origin_countries', ['PT', 'ZA']);
|
|
$this->postJson('/api/v1/simulation-drafts', [...$data, 'personal_snapshot' => ['fiscal_residency_country' => 'ZZ']], ['Idempotency-Key' => (string) Str::uuid()])
|
|
->assertUnprocessable()->assertJsonValidationErrors('personal_snapshot.fiscal_residency_country');
|
|
$legacy = [...$data, 'personal_snapshot' => ['fiscal_residency_country' => 'Portugal']];
|
|
$this->putJson('/api/v1/simulation-drafts/'.$id, $legacy, ['If-Match' => $created->headers->get('ETag')])->assertOk()->assertJsonPath('data.version_no', 2);
|
|
$this->actingAs(User::factory()->create())->getJson('/api/v1/simulation-drafts/'.$id)->assertNotFound();
|
|
$this->actingAs($owner)->getJson('/api/v1/simulation-drafts/'.$id)->assertJsonPath('data.personal_snapshot.fiscal_residency_country', 'Portugal');
|
|
$this->assertDatabaseCount('simulations', 0);
|
|
}
|
|
|
|
public function test_desk_territory_cascade_is_saved_recovered_and_presented_with_origin_without_cross_account_access(): void
|
|
{
|
|
$owner = User::factory()->create();
|
|
$manager = User::factory()->create();
|
|
config(['portal.managers' => [$manager->email]]);
|
|
$country = $this->territory('country');
|
|
$province = $this->territory('province', $country);
|
|
$district = $this->territory('district', $province);
|
|
$post = $this->territory('admin_post', $district);
|
|
$locality = $this->territory('locality', $post);
|
|
$private = $this->territory('district', $province, false);
|
|
foreach ([['province', null, $province], ['district', $province, $district], ['admin_post', $district, $post], ['locality', $post, $locality]] as [$type, $parent, $selected]) {
|
|
$this->getJson('/api/v1/investment/territory-options?'.http_build_query(['type' => $type, 'parent_id' => $parent, 'q' => 'sintetico']))
|
|
->assertOk()->assertJsonPath('data.0.id', $selected);
|
|
}
|
|
$service = $this->actingAs($manager)->postJson('/management/balcao/servicos', ['code' => 'project-preparation', 'confirm' => true])->assertCreated()->json('id');
|
|
$case = $this->actingAs($owner)->postJson('/balcao-virtual/dossiers', ['title' => 'Dossier sintético QA', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
|
|
$id = $this->postJson('/balcao-virtual/dossiers/'.$case.'/pedidos', ['service_version_id' => $service, 'client_key' => (string) Str::uuid()])->assertCreated()->json('id');
|
|
$url = '/balcao-virtual/pedidos/'.$id;
|
|
$payload = ['title' => 'Projeto sintético QA', 'territory_id' => $locality, 'location_source' => 'Fonte sintética exclusivamente QA', 'declared_origin_country_code' => 'PT'];
|
|
$this->putJson($url, ['version' => 1, 'payload' => $payload])->assertOk()->assertJsonPath('version', 2);
|
|
$stored = DB::table('desk_requests')->where('id', $id)->value('payload');
|
|
$this->assertSame($locality, app(VirtualDesk::class)->decode($stored)['territory_id']);
|
|
$this->assertStringNotContainsString($locality, $stored);
|
|
$this->get($url)->assertInertia(fn (Assert $page) => $page->component('desk/request')->has('countries', 249)
|
|
->where('request.payload.territory_id', $locality)->where('request.payload.declared_origin_country_code', 'PT')
|
|
->has('territoryPaths.territory_id', 4)->where('territoryPaths.territory_id.0.id', $province)->where('territoryPaths.territory_id.3.id', $locality));
|
|
$this->putJson($url, ['version' => 2, 'payload' => [...$payload, 'territory_id' => $private]])->assertUnprocessable()->assertJsonValidationErrors('payload.territory_id');
|
|
$this->getJson($url)->assertJsonPath('request.version', 2)->assertJsonPath('request.payload.territory_id', $locality);
|
|
$this->actingAs(User::factory()->create())->getJson($url)->assertForbidden();
|
|
$this->actingAs($owner)->getJson($url)->assertJsonPath('territoryPaths.territory_id.3.id', $locality);
|
|
$submitted = [...$payload, 'description' => 'Descrição exclusivamente sintética na QA.', 'investment_amount' => '10.00', 'currency' => 'MZN'];
|
|
$this->putJson($url, ['version' => 2, 'payload' => $submitted, 'consent' => true, 'submit' => true])->assertOk()->assertJsonPath('status', 'submitted');
|
|
$this->actingAs($manager)->get('/management/crm/leads?origin=PT')->assertInertia(fn (Assert $page) => $page
|
|
->has('originStatistics.interests', 0)->where('originStatistics.desk.0.code', 'PT')->where('originStatistics.desk.0.count', 1));
|
|
Http::assertNothingSent();
|
|
}
|
|
}
|