Files
Investidor-V2/tests/Feature/InvestorPromoterContinuityTest.php
T
2026-11-15 18:56:39 +02:00

118 lines
9.7 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
final class InvestorPromoterContinuityTest 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('investor_intent_20261115_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 $owner, string $title): string
{
$id = $this->actingAs($owner)->postJson('/investimentos', ['title' => $title, 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
$view = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
$body = ['version' => $view['case']['version'], 'selection' => [], 'declarations' => ['products_services' => 'SYNTHETIC activity '.$title, 'currency' => 'MZN', 'investment_budget_min' => '1000000', 'investment_budget_max' => '5000000']];
$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();
return $id;
}
public function test_existing_company_is_reused_without_changing_ownership_or_repeating_the_comparison(): void
{
$owner = User::factory()->create();
$this->actingAs($owner);
$company = $this->postJson('/api/v1/investment/companies', ['version' => 0, 'operation_key' => (string) Str::uuid(), 'payload' => ['name' => 'SYNTHETIC Hotel Company', 'incorporation_country_code' => 'MZ', 'registration' => 'SYNTHETIC-QA-REG', 'role' => 'SYNTHETIC representative', 'legal_form' => 'quotas']])->assertSuccessful()->json('id');
$id = $this->dossier($owner, 'Criar Hotel');
$options = $this->getJson('/investimentos/'.$id.'/contexto/entidade')->assertOk()->assertJsonPath('companies.0.id', $company)->json();
$memberships = DB::table('memberships')->count();
$command = ['version' => $options['version'], 'operation_key' => (string) Str::uuid(), 'kind' => 'company', 'id' => $company, 'source_version' => 1];
$url = '/investimentos/'.$id.'/contexto/entidade';
$this->postJson($url, $command)->assertOk()->assertJsonPath('continue', '/investimentos/'.$id);
$this->postJson($url, $command)->assertOk()->assertJsonPath('replayed', true);
$case = DB::table('desk_cases')->where('id', $id)->first();
$this->assertSame($owner->id, (int) $case->user_id);
$this->assertNull($case->organization_id);
$this->assertSame($company, $case->company_id);
$this->assertSame($memberships, DB::table('memberships')->count());
$this->assertSame(1, DB::table('investment_company_declarations')->where('user_id', $owner->id)->count());
$this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->assertJsonPath('prefill.simulator.legal_name', 'SYNTHETIC Hotel Company')->assertJsonPath('prefill.simulator.legal_form', 'quotas')->assertJsonPath('prefill.simulator.entity_stage', 'existing')->assertJsonPath('prefill.simulator.investment_budget_max', '5000000');
$preparation = $this->getJson('/investimentos/'.$id.'/preparar-empresa')->assertOk()->assertJsonPath('facts.proposed_name', 'SYNTHETIC Hotel Company')->assertJsonPath('facts.form', 'quotas')->json();
$facts = $preparation['facts'];
$facts['notes'] = 'SYNTHETIC deliberate note';
$this->postJson('/investimentos/'.$id.'/preparar-empresa', ['version' => $preparation['case']['version'], 'operation_key' => (string) Str::uuid(), 'facts' => $facts])->assertOk();
$recovered = $this->getJson('/investimentos/'.$id.'/preparar-empresa')->assertOk()->assertJsonPath('facts.notes', 'SYNTHETIC deliberate note')->json();
$facts['form'] = 'anonymous';
$this->postJson('/investimentos/'.$id.'/preparar-empresa', ['version' => $recovered['case']['version'], 'operation_key' => (string) Str::uuid(), 'facts' => $facts])->assertUnprocessable();
$this->getJson('/investimentos/'.$id)->assertOk()->assertJsonPath('tools.simulator', '/simulador-fiscal?context='.$id);
$this->actingAs(User::factory()->create())->postJson($url, $command)->assertForbidden();
$otherCase = $this->dossier(auth()->user(), 'Another private idea');
$v = $this->getJson('/investimentos/'.$otherCase.'/contexto/entidade')->assertOk()->assertJsonCount(0, 'companies')->json('version');
$this->postJson('/investimentos/'.$otherCase.'/contexto/entidade', [...$command, 'version' => $v, 'operation_key' => (string) Str::uuid()])->assertNotFound();
}
public function test_optional_comparison_can_continue_into_the_same_unconstituted_entity_and_recover_without_creating_a_company(): void
{
$owner = User::factory()->create();
foreach (['Criar Hotel', 'Agronegocio QA'] as $title) {
$id = $this->dossier($owner, $title);
$comparison = $this->getJson('/investimentos/'.$id.'/forma-juridica')->assertOk()->json();
$answers = $comparison['answers'];
$answers['preferred'] = 'quotas';
$body = ['version' => $comparison['case']['version'], 'operation_key' => (string) Str::uuid(), 'answers' => $answers, 'continue_preparation' => true];
$this->postJson('/investimentos/'.$id.'/forma-juridica', $body)->assertOk();
$this->postJson('/investimentos/'.$id.'/forma-juridica', $body)->assertOk()->assertJsonPath('replayed', true);
$this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->assertJsonPath('prefill.simulator.entity_stage', 'being_formed');
$preparation = $this->getJson('/investimentos/'.$id.'/preparar-empresa?form=quotas')->assertOk()->assertJsonPath('facts.form', 'quotas')->assertJsonPath('facts.activities', 'SYNTHETIC activity '.$title)->json();
$facts = $preparation['facts'];
$facts['proposed_name'] = 'SYNTHETIC new company '.$title;
$this->postJson('/investimentos/'.$id.'/preparar-empresa', ['version' => $preparation['case']['version'], 'operation_key' => (string) Str::uuid(), 'facts' => $facts])->assertOk();
$this->getJson('/investimentos/'.$id.'/preparar-empresa?form=anonymous')->assertOk()->assertJsonPath('facts.form', 'quotas')->assertJsonPath('proposedForm', 'anonymous');
$this->getJson('/investimentos/'.$id)->assertOk()->assertJsonPath('investment_context.promoter_entity.kind', 'being_formed');
$this->assertSame(0, DB::table('investment_company_declarations')->where('user_id', $owner->id)->count());
$this->assertNull(DB::table('desk_cases')->where('id', $id)->value('company_id'));
$version = $this->getJson('/investimentos/'.$id.'/contexto/entidade')->json('version');
$this->postJson('/investimentos/'.$id.'/contexto/entidade', ['version' => $version, 'operation_key' => (string) Str::uuid(), 'kind' => 'undetermined'])->assertOk();
$this->getJson('/investimentos/'.$id)->assertOk()->assertJsonPath('investment_context.promoter_entity.kind', 'undetermined')->assertJsonPath('tools.simulator', '/simulador-fiscal?context='.$id);
$this->getJson('/investimentos/'.$id.'/preparar-empresa')->assertOk()->assertJsonPath('facts.proposed_name', $facts['proposed_name']);
}
}
public function test_an_authorised_organization_snapshot_does_not_transfer_the_case_or_grant_master_write_access(): void
{
$owner = User::factory()->create();
$org = (string) Str::uuid();
DB::table('organizations')->insert(['id' => $org, 'name' => 'SYNTHETIC represented organization', 'source_system' => 'unit-qa', 'organization_type' => 'private']);
DB::table('memberships')->insert(['user_id' => $owner->id, 'organization_id' => $org, 'role' => 'viewer']);
$id = $this->dossier($owner, 'Hotel with existing organization');
$url = '/investimentos/'.$id.'/contexto/entidade';
$options = $this->getJson($url)->assertOk()->assertJsonPath('organizations.0.id', $org)->json();
$command = ['kind' => 'organization', 'id' => $org, 'version' => $options['version'], 'operation_key' => (string) Str::uuid(), 'source_version' => 99];
$this->postJson($url, $command)->assertConflict();
$command['source_version'] = 1;
$this->postJson($url, $command)->assertOk();
$case = DB::table('desk_cases')->where('id', $id)->first();
$this->assertNull($case->organization_id); $this->assertNull($case->company_id); $this->assertSame($owner->id, (int) $case->user_id);
$this->assertSame('viewer', DB::table('memberships')->where('user_id', $owner->id)->where('organization_id', $org)->value('role'));
config(['portal.organization_country_enabled' => true]);
$this->putJson('/api/v1/investment/organizations/'.$org.'/country', ['version' => 1, 'country' => 'MZ', 'source_reference' => 'Synthetic QA'])->assertForbidden();
DB::table('memberships')->where('user_id', $owner->id)->where('organization_id', $org)->update(['deleted_at' => now()]);
$current = $this->getJson($url)->assertOk()->assertJsonCount(0, 'organizations')->assertJsonPath('selected.snapshot.name', 'SYNTHETIC represented organization')->json();
$this->postJson($url, [...$command, 'version' => $current['version'], 'operation_key' => (string) Str::uuid()])->assertForbidden();
}
}