102 lines
5.8 KiB
PHP
102 lines
5.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use App\Services\OrganizationCountryInstallation;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Tests\TestCase;
|
|
|
|
class OrganizationCountryInstallationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private function actor(): User
|
|
{
|
|
$user = User::factory()->create(['two_factor_secret' => Crypt::encryptString('JBSWY3DPEHPK3PXP'), 'two_factor_confirmed_at' => now()]);
|
|
config(['portal.managers' => [$user->email]]);
|
|
|
|
return $user;
|
|
}
|
|
|
|
private function login(User $user, bool $mfa = true): static
|
|
{
|
|
return $this->actingAs($user)->withSession(['portal.mfa_actor' => $mfa ? $user->id : null, 'portal.mfa_at' => $mfa ? time() : 0]);
|
|
}
|
|
|
|
private function readiness(bool $ready): void
|
|
{
|
|
// Unit fixture only. Real activation requires the existing installer's verified backup evidence.
|
|
$this->partialMock(OrganizationCountryInstallation::class, function ($mock) use ($ready) {
|
|
$mock->shouldReceive('evidence')->andReturn(['ready' => $ready, 'backupDate' => null]);
|
|
});
|
|
}
|
|
|
|
private function input(): array
|
|
{
|
|
$this->app['session']->forget('portal.country_installation_operation');
|
|
$plan = $this->getJson('/management/opportunity-resources-install/organization-country')->assertOk()->json();
|
|
|
|
return ['hash' => $plan['hash'], 'confirm' => true, 'idempotency_key' => $plan['operationKey'],
|
|
'reason' => 'Synthetic QA installation without accounts or inferred values'];
|
|
}
|
|
|
|
public function test_cold_install_requires_readiness_is_atomic_and_repetition_has_no_duplicates(): void
|
|
{
|
|
$actor = $this->actor();
|
|
$this->login($actor);
|
|
// TestCase has verified the isolated unit database before RefreshDatabase or these writes.
|
|
Schema::table('organizations', fn ($table) => $table->dropColumn(['incorporation_country_code', 'incorporation_country_provenance']));
|
|
DB::table('migrations')->where('migration', OrganizationCountryInstallation::MIGRATION)->delete();
|
|
$this->readiness(false);
|
|
$this->getJson('/management/opportunity-resources-install/organization-country')->assertOk()->assertJsonPath('installed', false)->assertJsonPath('consistent', true)->assertJsonPath('ready', false);
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $this->input())->assertUnprocessable();
|
|
$this->readiness(true);
|
|
$organizations = DB::table('organizations')->count();
|
|
$users = DB::table('users')->count();
|
|
$input = $this->input();
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $input)->assertOk()->assertJsonPath('changed', true)->assertJsonPath('new_columns', 2);
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $input)->assertOk()->assertJsonPath('changed', true);
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', [...$input, 'reason' => 'Outro conteúdo na mesma chave de repetição'])->assertConflict();
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $this->input())->assertOk()->assertJsonPath('changed', false)->assertJsonPath('new_columns', 0);
|
|
$this->assertSame($organizations, DB::table('organizations')->count());
|
|
$this->assertSame($users, DB::table('users')->count());
|
|
$this->assertSame(1, DB::table('migrations')->where('migration', OrganizationCountryInstallation::MIGRATION)->count());
|
|
$this->assertSame(2, DB::table('audit_events')->where('source_system', 'organization-country-installation')->count());
|
|
$this->assertFalse(config('organization_country.authenticated_installation'));
|
|
}
|
|
|
|
public function test_missing_mfa_other_user_and_partial_schema_are_denied(): void
|
|
{
|
|
$actor = $this->actor();
|
|
$this->readiness(true);
|
|
$this->login($actor, false);
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $this->input())->assertForbidden();
|
|
$this->login(User::factory()->create());
|
|
$this->getJson('/management/opportunity-resources-install/organization-country')->assertForbidden();
|
|
$this->login($actor);
|
|
Schema::table('organizations', fn ($table) => $table->dropColumn('incorporation_country_provenance'));
|
|
$this->getJson('/management/opportunity-resources-install/organization-country')->assertOk()->assertJsonPath('consistent', false);
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $this->input())->assertConflict();
|
|
$this->assertSame(0, DB::table('audit_events')->where('source_system', 'organization-country-installation')->count());
|
|
}
|
|
|
|
public function test_designated_holder_uses_existing_authority_without_legacy_manager_allowlist(): void
|
|
{
|
|
$holder = $this->actor();
|
|
$second = User::factory()->create();
|
|
$approver = User::factory()->create();
|
|
config(['portal.managers' => []]);
|
|
DB::table('portal_superuser_pair')->insert(['id' => 1, 'first_user_id' => $holder->id, 'second_user_id' => $second->id, 'approved_by' => $approver->id, 'version' => 1]);
|
|
$this->readiness(false);
|
|
$this->login($holder);
|
|
$this->getJson('/management/opportunity-resources-install/organization-country')->assertOk()->assertJsonPath('installed', true);
|
|
$this->postJson('/management/opportunity-resources-install/organization-country', $this->input())->assertOk()->assertJsonPath('changed', false);
|
|
$this->login($approver);
|
|
$this->getJson('/management/opportunity-resources-install/organization-country')->assertForbidden();
|
|
}
|
|
}
|