92 lines
6.8 KiB
PHP
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'));
|
|
}
|
|
}
|