Files
compras/tests/Feature/PilotAccessProposalTest.php
2026-11-02 20:27:11 +02:00

77 lines
5.7 KiB
PHP

<?php
use App\Enums\PurchasingRole;
use App\Models\Department;
use App\Models\Tenant;
use App\Models\User;
use App\Services\OrganizationAccess;
use App\Services\OrganizationActivationPreview;
use App\Services\WorkContext;
use Illuminate\Support\Facades\DB;
use Inertia\Testing\AssertableInertia as Assert;
test('synthetic pilot access proposal preserves existing authority while activating only its explicit task additions in a disposable database', function () {
$connection = DB::connection();
$isolated = ($connection->getDriverName() === 'sqlite' && $connection->getDatabaseName() === ':memory:')
|| ($connection->getDriverName() === 'pgsql' && preg_match('/^compras_receipts_test_[a-f0-9]{12}$/D', $connection->getDatabaseName()));
if (! app()->environment('testing') || ! $isolated) {
throw new LogicException('Proposta proibida fora de ensaio descartável.');
}
$proposal = json_decode(file_get_contents(base_path('docs/proofs/pilot-access-proposal.json')), true, 64, JSON_THROW_ON_ERROR);
expect($proposal['status'])->toBe('pending_owner_authorization')->and($proposal['environment'])->toBe('http://127.0.0.1:8005');
$company = Tenant::query()->create(['name' => 'Simulação descartável dos acessos do piloto',
'work_context_policy' => ['revision' => 1, 'enabled' => $proposal['enabled_contexts']]]);
$department = Department::query()->create(['tenant_id' => $company->id, 'name' => 'Departamento sintético']);
config(['work_context.entitlements.'.$company->id => $proposal['enabled_contexts']]);
$users = [];
$before = [];
foreach ($proposal['users'] as $person) {
$user = User::factory()->create(['tenant_id' => $company->id, 'department_id' => $department->id,
'name' => 'Simulação — '.$person['name'], 'purchasing_role' => PurchasingRole::from($person['current_permissions'][0])]);
$users[$person['id']] = $user;
$before[$person['id']] = ['permissions' => app(OrganizationAccess::class)->permissions($user),
'contexts' => app(WorkContext::class)->available($user), 'requester' => app(OrganizationAccess::class)->canRequest($user),
'department_scoped' => app(OrganizationAccess::class)->departmentScoped($user)];
expect($before[$person['id']]['contexts'])->toBe($person['current_contexts']);
}
$actor = $users[8];
$this->actingAs($actor);
$base = '/administracao/parametrizacao';
DB::table('organization_access')->insert(['tenant_id' => $company->id, 'active' => false, 'version' => 1, 'created_at' => now(), 'updated_at' => now()]);
$version = 1;
$profiles = [];
foreach ($proposal['profiles'] as $profile) {
$this->postJson($base.'/profile', ['version' => $version++, 'name' => $profile['name'], 'permissions' => $profile['permissions'], 'work_contexts' => $profile['work_contexts']])->assertSessionHasNoErrors()->assertRedirect();
$profiles[$profile['key']] = DB::table('organization_profiles')->where('name', $profile['name'])->sole()->id;
}
foreach ($proposal['users'] as $person) {
$ids = array_map(fn ($key) => $profiles[$key], $person['profile_keys']);
$this->postJson($base.'/assign', ['version' => $version++, 'user_id' => $users[$person['id']]->id, 'profile_ids' => $ids,
'maintenance' => false, 'expires_at' => null, 'reason' => 'Ensaiar proposta sintética apenas na base descartável.'])->assertSessionHasNoErrors()->assertRedirect();
}
$rules = $proposal['proposed_rules'];
$this->postJson($base.'/rules', ['version' => $version++, 'first_limit' => $rules['first_limit'], 'second_limit' => $rules['second_limit'],
'includes_tax' => $rules['includes_tax'], 'reason' => 'Conferência sintética das mesmas alçadas, sem alterar os valores.'])->assertSessionHasNoErrors()->assertRedirect();
$this->postJson($base.'/work_contexts', ['version' => $version++, 'enabled' => $proposal['enabled_contexts'], 'confirm' => true,
'reason' => 'Preservar as opções existentes apenas na simulação descartável.'])->assertSessionHasNoErrors()->assertRedirect();
$preview = app(OrganizationActivationPreview::class)->build($actor);
expect($preview['can_activate'])->toBeTrue()->and($preview['losing_access'])->toBe(0)->and($preview['permanent_administrators'])->toBe(2);
foreach ($users as $sourceId => $user) {
expect(app(OrganizationAccess::class)->permissions($user))->toBe($before[$sourceId]['permissions']);
}
$this->postJson($base.'/activate', ['version' => $version, 'confirm' => true, 'reason' => 'Activar a proposta exclusivamente no ensaio descartável.'])->assertSessionHasNoErrors()->assertRedirect();
foreach ($proposal['users'] as $person) {
$user = $users[$person['id']];
$permissions = app(OrganizationAccess::class)->permissions($user);
expect($permissions)->toEqualCanonicalizing($person['planned_permissions'])
->and(array_values(array_diff($person['current_permissions'], $permissions)))->toBe([])
->and(app(WorkContext::class)->available($user))->toBe($before[$person['id']]['contexts'])
->and(app(OrganizationAccess::class)->canRequest($user))->toBe($before[$person['id']]['requester'])
->and(app(OrganizationAccess::class)->departmentScoped($user))->toBe($before[$person['id']]['department_scoped']);
}
$this->get($base)->assertInertia(fn (Assert $page) => $page->where('activationPreview.active', true)->where('activationPreview.losing_access', 0));
expect(DB::table('users')->count())->toBe(12)->and(DB::table('requisitions')->count())->toBe(0)
->and(DB::table('purchase_orders')->count())->toBe(0)->and(DB::table('invoices')->count())->toBe(0)
->and(DB::table('pa_access')->count())->toBe(0);
});