Files
Investidor-V2/tests/Feature/OrganizationCountryTest.php
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

92 lines
6.8 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\InvestmentSubmission;
use App\Models\User;
use App\Services\PortalProfileAdministration;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
class OrganizationCountryTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
config(['portal.organization_country_enabled' => true]);
}
public function test_auditor_with_officer_membership_cannot_change_the_master_country(): void
{
config(['portal.profile_access_enabled' => true]);
$auditor = User::factory()->create();
$approver = User::factory()->create();
$org = (string) Str::uuid();
DB::table('organizations')->insert(['id' => $org, 'name' => 'Synthetic QA auditor scope', 'source_system' => 'unit-qa', 'organization_type' => 'private']);
DB::table('memberships')->insert(['user_id' => $auditor->id, 'organization_id' => $org, 'role' => 'officer']);
app(PortalProfileAdministration::class)->insertGrant((string) Str::uuid(), $approver, ['user_id' => $auditor->id, 'role' => 'auditor', 'opportunity_types' => [], 'responsibilities' => []]);
$auditor->refresh();
$this->actingAs($auditor)->withSession(['portal.access_revision' => (int) $auditor->portal_access_revision]);
$this->getJson('/api/v1/investment/profile')->assertOk()->assertJsonPath('organizations.0.canEdit', false);
$this->putJson('/api/v1/investment/organizations/'.$org.'/country', ['version' => 1, 'country' => 'FR', 'source_reference' => 'Synthetic declaration blocked by auditor policy'])->assertForbidden();
$this->assertNull(DB::table('organizations')->where('id', $org)->value('incorporation_country_code'));
$this->assertSame(1, DB::table('organizations')->where('id', $org)->value('version_no'));
}
public function test_master_country_is_explicit_authorized_versioned_and_never_rewrites_previous_submissions(): void
{
$this->withoutVite();
$owner = User::factory()->create();
$viewer = User::factory()->create();
$other = User::factory()->create();
$org = (string) Str::uuid();
DB::table('organizations')->insert(['id' => $org, 'name' => 'Synthetic QA master', 'source_system' => 'unit-qa', 'external_id' => 'stable-master', 'organization_type' => 'private']);
foreach ([[$owner, 'officer'], [$viewer, 'viewer']] as [$actor,$role]) {
DB::table('memberships')->insert(['user_id' => $actor->id, 'organization_id' => $org, 'role' => $role]);
}
$this->actingAs($owner)->putJson('/api/v1/investment/profile', ['version' => 0, 'payload' => ['country' => 'Historical value', 'residence_country_code' => 'PT']])->assertOk();
$company = $this->postJson('/api/v1/investment/companies', ['version' => 0, 'payload' => ['name' => 'Private declared entity', 'role' => 'QA representative', 'incorporation_country_code' => 'MZ']])->assertOk()->json('id');
$old = $this->postJson('/api/v1/investment/submissions', ['company_id' => $company])->assertCreated()->json('id');
$before = InvestmentSubmission::findOrFail($old)->getRawOriginal('payload');
$url = '/api/v1/investment/organizations/'.$org.'/country';
$this->putJson($url, ['version' => 1, 'country' => 'ZZ', 'source_reference' => 'Synthetic source'])->assertUnprocessable();
$this->putJson($url, ['version' => 1, 'country' => 'FR', 'source_reference' => 'Synthetic declaration; no real registration'])->assertOk()->assertJsonPath('version', 2);
$this->putJson($url, ['version' => 1, 'country' => 'ZA', 'source_reference' => 'Stale synthetic declaration'])->assertConflict();
$reuse = '/api/v1/investment/companies/'.$company.'/organization-country';
$this->postJson($reuse, ['version' => 1, 'organization_id' => $org, 'organization_version' => 2])->assertOk()->assertJsonPath('country', 'FR');
$new = $this->postJson('/api/v1/investment/submissions', ['company_id' => $company])->assertCreated()->json('id');
$source = InvestmentSubmission::findOrFail($new)->payload['organization_country_snapshot'];
$this->assertSame($org, $source['organization_id']);
$this->assertSame(2, $source['organization_version']);
$this->putJson('/api/v1/investment/submissions/'.$new, ['version' => 1, 'payload' => ['incorporation_country_code' => 'FR', 'organization_country_snapshot' => array_reverse($source, true)]])->assertOk();
$forged = $source;
$forged['country'] = 'ZA';
$this->putJson('/api/v1/investment/submissions/'.$new, ['version' => 2, 'payload' => ['incorporation_country_code' => 'FR', 'organization_country_snapshot' => $forged]])->assertUnprocessable();
$this->putJson($url, ['version' => 2, 'country' => 'ZA', 'source_reference' => 'Synthetic second declaration'])->assertOk();
$this->assertSame($source, InvestmentSubmission::findOrFail($new)->payload['organization_country_snapshot']);
$this->assertSame($before, InvestmentSubmission::findOrFail($old)->getRawOriginal('payload'));
$this->assertSame('MZ', InvestmentSubmission::findOrFail($old)->payload['incorporation_country_code']);
$this->assertSame('PT', InvestmentSubmission::findOrFail($old)->payload['residence_country_code']);
$this->assertSame(1, DB::table('organizations')->count());
$row = DB::table('organizations')->where('id', $org)->first();
$this->assertSame('unit-qa', $row->source_system);
$this->assertSame('stable-master', $row->external_id);
$audit = DB::table('audit_events')->where('action', 'incorporation_country_saved')->orderByRaw("(after_data->>'version')::integer")->get();
$this->assertCount(2, $audit);
$snapshot = json_decode(Crypt::decryptString(json_decode($audit[1]->before_data, true)['snapshot']), true);
$this->assertSame('FR', $snapshot['incorporation_country_code']);
$this->actingAs($viewer)->getJson('/api/v1/investment/profile')->assertOk()->assertJsonPath('organizations.0.canEdit', false);
$this->putJson($url, ['version' => 3, 'country' => 'MZ', 'source_reference' => 'Not authorized'])->assertForbidden();
$this->actingAs($other)->getJson('/api/v1/investment/profile')->assertOk()->assertJsonCount(0, 'organizations');
$this->putJson($url, ['version' => 3, 'country' => 'MZ', 'source_reference' => 'Wrong organization'])->assertForbidden();
DB::table('memberships')->where('user_id', $owner->id)->update(['deleted_at' => now()]);
$this->actingAs($owner)->postJson($reuse, ['version' => 2, 'organization_id' => $org, 'organization_version' => 3])->assertForbidden();
$this->assertSame(3, DB::table('organizations')->where('id', $org)->value('version_no'));
}
}