Files
Investidor-V2/tests/Feature/InvestmentContextContinuityTest.php

104 lines
6.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Services\InvestmentZones;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
class InvestmentContextContinuityTest extends TestCase
{
use DatabaseTransactions;
protected function setUp(): void
{
parent::setUp();
$this->withoutVite();
$db = DB::selectOne('select current_database() as name, inet_server_port() as port');
$this->assertSame('simulator_fiscal_delta_20261021_test', $db->name);
$this->assertSame(55441, (int) $db->port);
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false, 'investmoz_ai.enabled' => false]);
}
private function dossier(User $user, string $title): string
{
return $this->actingAs($user)->postJson('/investimentos', ['title' => $title, 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
}
public function test_same_dossier_context_reuses_linked_draft_and_preserves_account_and_project_boundaries(): void
{
$owner = User::factory()->create();
$a = $this->dossier($owner, 'A');
$b = $this->dossier($owner, 'B');
foreach ([[$a, 'MZN', 'national'], [$b, 'USD', 'foreign']] as [$id,$currency,$origin]) {
$this->actingAs($owner)->postJson('/api/v1/simulation-drafts', ['context' => $id, 'personal_snapshot' => ['investor_origin' => $origin, 'tax_residency_country' => 'ZA', 'person_type' => 'collective'], 'economic_snapshot' => ['purpose' => 'simulate_only', 'project_title' => $id, 'currency' => $currency, 'expected_start_date' => '2026-01-01']], ['Idempotency-Key' => (string) Str::uuid()])->assertCreated();
$v = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk();
$v->assertJsonPath('prefill.simulator.currency', $currency)->assertJsonPath('prefill.simulator.investor_origin', $origin)->assertJsonPath('prefill.simulator.tax_residency_country', 'ZA');
$this->assertSame('linked_simulation', $v->json('context.provenance.currency.kind'));
}
$this->actingAs(User::factory()->create())->getJson('/investimentos/'.$a.'/contexto')->assertForbidden();
$this->actingAs($owner)->getJson('/investimentos/'.$a.'/contexto')->assertOk()->assertJsonPath('prefill.simulator.currency', 'MZN');
}
public function test_context_change_is_previewed_versioned_and_idempotent_without_changing_the_source_draft(): void
{
$owner = User::factory()->create();
$id = $this->dossier($owner, 'Impacto');
$view = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
$body = ['version' => $view['case']['version'], 'selection' => [], 'declarations' => ['currency' => 'USD', 'investor_origin' => 'foreign', 'tax_residency_country' => 'MZ'], 'replace_fields' => []];
$plan = $this->postJson('/investimentos/'.$id.'/contexto/previa', $body)->assertOk()->json();
$this->assertNotEmpty($plan['updated']);
$this->assertTrue($plan['dependency_changed']);
$control = [...$body, 'preview_hash' => $plan['preview_hash'], 'operation_key' => (string) Str::uuid(), 'confirm' => true];
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', $control)->assertOk();
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', $control)->assertOk()->assertJsonPath('replayed', true);
$this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->assertJsonPath('prefill.simulator.currency', 'USD')->assertJsonPath('prefill.simulator.tax_residency_country', 'MZ');
}
public function test_zone_selection_reuses_existing_reference_and_impact_workflow_without_granting_a_benefit(): void
{
$owner = User::factory()->create();
$id = $this->dossier($owner, 'Zonas QA');
$template = app(InvestmentZones::class)->visible()->where('category', 'industrial_park')->firstOrFail();
foreach (['zee', 'zfi', 'industrial_park'] as $category) {
// Synthetic catalogue metadata, rolled back. Never a legal source or published rule in homologation.
$zone = $template->replicate();
$zone->id = (string) Str::uuid();
$zone->code = 'SINT-CONTEXT-'.Str::random(12);
$zone->name = 'SINTÉTICO QA '.$category;
$zone->category = $category;
$zone->legal_regime = $category === 'industrial_park' ? 'pending' : $category;
$zone->source_evidence = [['source_type' => 'government', 'url' => 'https://example.test/synthetic-zone', 'supports' => 'Synthetic test only; no legal or eligibility conclusion']];
$zone->save();
$view = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
$body = ['version' => $view['case']['version'], 'selection' => ['park' => 'park:'.$zone->id]];
$plan = $this->postJson('/investimentos/'.$id.'/contexto/previa', $body)->assertOk()->json();
$this->assertSame($category, $plan['fields']['investment_regime']);
$this->assertContains('S17', $plan['review_steps']);
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', [...$body, 'preview_hash' => $plan['preview_hash'], 'operation_key' => (string) Str::uuid(), 'confirm' => true])->assertOk();
$after = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk();
$after->assertJsonPath('prefill.simulator.investment_regime', $category);
$snapshot = json_decode($after->json('prefill.simulator.context_applicability_snapshot'), true);
$this->assertSame($zone->id, $snapshot['zone']['id']);
$this->assertArrayNotHasKey('benefit_rate', $snapshot);
}
$view = $this->getJson('/investimentos/'.$id.'/contexto')->json();
$body = ['version' => $view['case']['version'], 'selection' => ['park' => null]];
$plan = $this->postJson('/investimentos/'.$id.'/contexto/previa', $body)->assertOk()->json();
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', [...$body, 'preview_hash' => $plan['preview_hash'], 'operation_key' => (string) Str::uuid(), 'confirm' => true])->assertOk();
$this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->assertJsonPath('prefill.simulator.investment_regime', 'general')->assertJsonPath('prefill.simulator.special_zone_id', '');
}
public function test_a_title_edit_does_not_invalidate_applicability_or_ask_for_irrelevant_reviews(): void
{
$owner = User::factory()->create();
$id = $this->dossier($owner, 'Título original');
$view = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
$this->postJson('/investimentos/'.$id.'/contexto/previa', ['version' => $view['case']['version'], 'selection' => [], 'declarations' => ['project_title' => 'Título revisto']])
->assertOk()->assertJsonPath('dependency_changed', false)->assertJsonPath('review_steps', []);
}
}