262 lines
23 KiB
PHP
262 lines
23 KiB
PHP
<?php
|
|
|
|
use App\Enums\PurchasingRole;
|
|
use App\Models\AuditLog;
|
|
use App\Models\User;
|
|
use App\Services\OrganizationAccess;
|
|
use App\Services\RequisitionWorkflow;
|
|
use Database\Seeders\DatabaseSeeder;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
|
|
require_once __DIR__.'/../Support/ReceiptFixture.php';
|
|
require_once __DIR__.'/../Support/AiPlanFixture.php';
|
|
|
|
function organizationProfile(int $tenant, string $name, array $permissions): int
|
|
{
|
|
return DB::table('organization_profiles')->insertGetId(['tenant_id' => $tenant, 'name' => $name, 'permissions' => json_encode($permissions, JSON_THROW_ON_ERROR), 'version' => 1, 'created_at' => now(), 'updated_at' => now()]);
|
|
}
|
|
|
|
function organizationMember(int $tenant, int $user, int $profile): void
|
|
{
|
|
DB::table('organization_memberships')->insert(['tenant_id' => $tenant, 'user_id' => $user, 'profile_id' => $profile, 'maintenance' => false, 'created_at' => now(), 'updated_at' => now()]);
|
|
}
|
|
|
|
beforeEach(function () {
|
|
$this->f = receiptFixture();
|
|
$this->admin = $this->f['issuer'];
|
|
$this->second = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'department_id' => $this->f['r']->department_id, 'purchasing_role' => PurchasingRole::Requester]);
|
|
$this->adminProfile = organizationProfile($this->f['tenant']->id, 'Direcção de sistemas', ['admin']);
|
|
organizationMember($this->f['tenant']->id, $this->admin->id, $this->adminProfile);
|
|
organizationMember($this->f['tenant']->id, $this->second->id, $this->adminProfile);
|
|
DB::table('organization_access')->insert(['tenant_id' => $this->f['tenant']->id, 'active' => true, 'version' => 1, 'created_at' => now(), 'updated_at' => now()]);
|
|
$this->base = '/administracao/parametrizacao';
|
|
$this->actingAs($this->admin);
|
|
});
|
|
|
|
test('intelligence settings are independently configurable audited and scoped without rewriting processes', function () {
|
|
$before = $this->f['r']->fresh()->toArray();
|
|
$body = ['version' => 1, 'procurement_brief' => false, 'people_brief' => true, 'reason' => 'Apresentar sínteses apenas no percurso de Pessoas.'];
|
|
$this->postJson($this->base.'/intelligence', $body)->assertRedirect();
|
|
expect($this->f['tenant']->fresh()->intelligence_policy)->toBe(['revision' => 1, 'configured' => true, 'procurement_brief' => false, 'people_brief' => true])
|
|
->and($this->f['r']->fresh()->toArray())->toBe($before);
|
|
$this->get($this->base)->assertInertia(fn (Assert $page) => $page->where('intelligenceReady', true)->where('intelligencePolicy.procurement_brief', false));
|
|
$this->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertInertia(fn (Assert $page) => $page->where('brief', null)->where('report.requisition.id', $this->f['r']->id));
|
|
$this->postJson($this->base.'/intelligence', $body)->assertUnprocessable();
|
|
$this->postJson($this->base.'/intelligence', [...$body, 'version' => 2, 'procurement_brief' => true, 'people_brief' => false])->assertRedirect();
|
|
$this->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertInertia(fn (Assert $page) => $page->where('brief.method', 'procurement_evidence_brief_v1'));
|
|
expect($this->f['tenant']->fresh()->intelligence_policy['revision'])->toBe(2)
|
|
->and(AuditLog::query()->where('action', 'organization.intelligence')->count())->toBe(2);
|
|
});
|
|
|
|
test('intelligence configuration rejects unauthorised actors secret fields external activation and cross company targets', function () {
|
|
$body = ['version' => 1, 'procurement_brief' => false, 'people_brief' => false, 'reason' => 'Configuração de sínteses da organização.'];
|
|
foreach (['api_key' => 'DO-NOT-STORE', 'external_processing_enabled' => true, 'tenant_id' => $this->f['tenant']->id + 1, 'procurement_brief' => 'invalid'] as $key => $value) {
|
|
$this->postJson($this->base.'/intelligence', [...$body, $key => $value])->assertUnprocessable();
|
|
}
|
|
$this->actingAs($this->f['receiver'])->postJson($this->base.'/intelligence', $body)->assertForbidden();
|
|
expect($this->f['tenant']->fresh()->intelligence_policy)->toBeNull()
|
|
->and(AuditLog::query()->where('action', 'organization.intelligence')->count())->toBe(0);
|
|
});
|
|
|
|
test('intelligence policy and global version roll back when audit cannot be recorded', function () {
|
|
$this->mock(RequisitionWorkflow::class)->shouldReceive('audit')->once()->andThrow(new RuntimeException('Injected audit failure'));
|
|
$this->withoutExceptionHandling();
|
|
expect(fn () => $this->postJson($this->base.'/intelligence', ['version' => 1, 'procurement_brief' => false, 'people_brief' => false, 'reason' => 'Não persistir sem registo de auditoria.']))->toThrow(RuntimeException::class);
|
|
expect($this->f['tenant']->fresh()->intelligence_policy)->toBeNull()
|
|
->and(DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->value('version'))->toBe(1);
|
|
});
|
|
|
|
test('AI plan is versioned audited and preserved when local summary choices change without activating a provider', function () {
|
|
Http::fake();
|
|
$before = $this->f['r']->fresh()->toArray();
|
|
$this->postJson($this->base.'/intelligence', ['version' => 1, 'procurement_brief' => false, 'people_brief' => true, 'reason' => 'Ocultar a síntese local de Compras.'])->assertRedirect();
|
|
$this->postJson($this->base.'/ai_plan', [...aiPlanInput(2), 'monthly_budget_usd' => '10'])->assertRedirect();
|
|
$policy = $this->f['tenant']->fresh()->intelligence_policy;
|
|
expect($policy['procurement_brief'])->toBeFalse()->and($policy['revision'])->toBe(2)
|
|
->and($policy['ai_plan']['state'])->toBe('draft')->and($policy['ai_plan']['monthly_budget_usd'])->toBe('10.00');
|
|
$this->get($this->base)->assertInertia(fn (Assert $page) => $page->where('aiPlan.provider', 'openai')->where('aiPlan.state', 'draft'));
|
|
$this->postJson($this->base.'/intelligence', ['version' => 3, 'procurement_brief' => true, 'people_brief' => false, 'reason' => 'Mudar apresentação sem apagar o plano.'])->assertRedirect();
|
|
expect($this->f['tenant']->fresh()->intelligence_policy['ai_plan'])->toBe($policy['ai_plan'])
|
|
->and($this->f['r']->fresh()->toArray())->toBe($before)
|
|
->and(AuditLog::query()->where('action', 'organization.ai_plan')->count())->toBe(1);
|
|
$this->postJson($this->base.'/ai_plan', aiPlanInput(3))->assertUnprocessable();
|
|
Http::assertNotSent(fn ($request) => ! in_array(parse_url($request->url(), PHP_URL_HOST), ['127.0.0.1', 'localhost'], true));
|
|
});
|
|
|
|
test('AI plan rejects secret endpoints activation invalid scopes and unbounded or incomplete settings', function () {
|
|
foreach ([['api_key' => 'NOT-A-SECRET'], ['endpoint' => 'https://invalid.example'], ['state' => 'active'], ['external_processing_enabled' => true],
|
|
['provider' => 'unknown'], ['model' => 'https://invalid.example/model'], ['model' => 'sk-fictitious-not-a-real-key'], ['domains' => ['unknown']], ['data_scopes' => ['raw_candidate_cv']],
|
|
['domains' => []], ['monthly_budget_usd' => '0'], ['monthly_requests' => 0], ['max_input_bytes' => 64001], ['max_output_tokens' => 4001]] as $bad) {
|
|
$this->postJson($this->base.'/ai_plan', [...aiPlanInput(), ...$bad])->assertUnprocessable();
|
|
}
|
|
expect($this->f['tenant']->fresh()->intelligence_policy)->toBeNull()
|
|
->and(AuditLog::query()->where('action', 'organization.ai_plan')->count())->toBe(0);
|
|
$this->actingAs($this->f['receiver'])->postJson($this->base.'/ai_plan', aiPlanInput())->assertForbidden();
|
|
});
|
|
|
|
test('AI plan rolls back with the shared configuration version when audit fails', function () {
|
|
$this->mock(RequisitionWorkflow::class)->shouldReceive('audit')->once()->andThrow(new RuntimeException('Injected audit failure'));
|
|
$this->withoutExceptionHandling();
|
|
expect(fn () => $this->postJson($this->base.'/ai_plan', aiPlanInput()))->toThrow(RuntimeException::class);
|
|
expect($this->f['tenant']->fresh()->intelligence_policy)->toBeNull()
|
|
->and(DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->value('version'))->toBe(1);
|
|
});
|
|
|
|
test('tax configuration is versioned tenant scoped and never rewrites existing process lines', function () {
|
|
$before = $this->f['r']->items()->get()->toArray();
|
|
$this->postJson($this->base.'/tax', ['version' => 1, 'default_tax_rate' => '5.00', 'reason' => 'Enquadramento fiscal específico da empresa.'])->assertRedirect();
|
|
expect($this->f['tenant']->fresh()->default_tax_rate)->toBe('5.00')
|
|
->and($this->f['r']->items()->get()->toArray())->toBe($before);
|
|
$this->get('/requisicoes/criar')->assertForbidden();
|
|
$this->postJson($this->base.'/tax', ['version' => 1, 'default_tax_rate' => '16', 'reason' => 'Pedido com versão antiga não pode gravar.'])->assertUnprocessable();
|
|
$this->postJson($this->base.'/tax', ['version' => 2, 'default_tax_rate' => '101', 'reason' => 'Taxa inválida não pode ser gravada.'])->assertUnprocessable();
|
|
expect(AuditLog::query()->where('action', 'organization.tax')->count())->toBe(1);
|
|
$this->actingAs($this->f['receiver'])->postJson($this->base.'/tax', ['version' => 2, 'default_tax_rate' => '16', 'reason' => 'Sem competência de administração.'])->assertForbidden();
|
|
});
|
|
|
|
test('comparison policy requires valid weights authority and current version without rewriting existing processes', function () {
|
|
$body = ['version' => 1, 'minimum_proposals' => 2, 'minimum_evaluators' => 1, 'weights' => ['price' => 50, 'quality' => 20, 'delivery' => 10, 'history' => 10, 'payment' => 10], 'reason' => 'Regra comercial aprovada para novos processos.'];
|
|
$this->postJson($this->base.'/comparison', [...$body, 'weights' => [...$body['weights'], 'price' => 49]])->assertUnprocessable();
|
|
expect($this->f['tenant']->fresh()->comparison_policy)->toBeNull();
|
|
$this->postJson($this->base.'/comparison', [...$body, 'minimum_evaluators' => 0])->assertUnprocessable();
|
|
$this->postJson($this->base.'/comparison', [...$body, 'weights' => [...$body['weights'], 'other' => 0]])->assertUnprocessable();
|
|
$this->postJson($this->base.'/comparison', $body)->assertRedirect();
|
|
expect($this->f['tenant']->fresh()->comparison_policy['revision'])->toBe(1)->and($this->f['r']->fresh()->comparison_policy)->toBeNull();
|
|
$this->postJson($this->base.'/comparison', $body)->assertUnprocessable();
|
|
$this->get($this->base)->assertInertia(fn (Assert $page) => $page->where('comparisonPolicy.minimum_proposals', 2));
|
|
expect(AuditLog::query()->where('action', 'organization.comparison')->count())->toBe(1);
|
|
$this->actingAs($this->f['receiver'])->postJson($this->base.'/comparison', [...$body, 'version' => 2])->assertForbidden();
|
|
});
|
|
|
|
test('setup requires a second named administrator and atomically gives both configured administration', function () {
|
|
$new = User::factory()->create();
|
|
$this->actingAs($new)->postJson('/empresa/iniciar', ['name' => 'Empresa de entrega QA', 'department' => 'Sede'])->assertUnprocessable()->assertJsonValidationErrors(['second_name', 'second_email', 'second_password']);
|
|
$this->post('/empresa/iniciar', ['name' => 'Empresa de entrega QA', 'department' => 'Sede', 'first_limit' => '1000.00', 'second_limit' => '2000.00', 'includes_tax' => true, 'second_name' => 'Segundo administrador QA', 'second_email' => 'segundo-entrega@example.test', 'second_password' => 'QA-Password-123456', 'second_password_confirmation' => 'QA-Password-123456'])->assertRedirect();
|
|
$new->refresh();
|
|
$second = User::query()->where('email', 'segundo-entrega@example.test')->sole();
|
|
expect($second->tenant_id)->toBe($new->tenant_id)->and(Hash::check('QA-Password-123456', $second->password))->toBeTrue();
|
|
expect(app(OrganizationAccess::class)->permissions($new))->toBe(['admin'])->and(app(OrganizationAccess::class)->permissions($second))->toBe(['admin']);
|
|
$this->actingAs($second)->get($this->base)->assertOk();
|
|
});
|
|
|
|
test('both administrators can access parameters independently of the stored development role', function () {
|
|
$this->get($this->base)->assertInertia(fn (Assert $p) => $p->where('active', true)->where('version', 1)->where('profiles.0.name', 'Direcção de sistemas')->has('users')->missing('users.0.password'));
|
|
$this->actingAs($this->second)->get($this->base)->assertOk();
|
|
$this->get('/empresa')->assertRedirect($this->base);
|
|
expect($this->second->purchasing_role)->toBe(PurchasingRole::Requester);
|
|
});
|
|
|
|
test('custom profile grants a real operational permission and the legacy profile grants nothing after activation', function () {
|
|
$this->post($this->base.'/profile', ['version' => 1, 'name' => 'Conferência da empresa', 'permissions' => ['finance']])->assertRedirect();
|
|
$profile = (int) DB::table('organization_profiles')->where('name', 'Conferência da empresa')->value('id');
|
|
$data = ['version' => 2, 'name' => 'Utilizador configurado', 'email' => 'configurado@example.test', 'password' => 'QA-Password-123456', 'password_confirmation' => 'QA-Password-123456', 'department_id' => $this->f['r']->department_id, 'profile_ids' => [$profile], 'reason' => 'Atribuição aprovada pela empresa no ensaio.'];
|
|
$this->post($this->base.'/user', $data)->assertRedirect();
|
|
$user = User::query()->where('email', $data['email'])->sole();
|
|
expect($user->purchasing_role)->toBe(PurchasingRole::Configured)->and($user->hasFunctions(['finance']))->toBeTrue()->and($user->hasFunctions(['admin']))->toBeFalse();
|
|
$this->actingAs($user)->get('/facturas')->assertOk();
|
|
$this->get('/requisicoes/criar')->assertForbidden();
|
|
$this->get($this->base)->assertForbidden();
|
|
$legacy = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'purchasing_role' => PurchasingRole::Admin]);
|
|
$this->actingAs($legacy)->get('/facturas')->assertForbidden();
|
|
$this->get($this->base)->assertForbidden();
|
|
});
|
|
|
|
test('two permanent administrators cannot be removed by assignment profile editing or expiry', function () {
|
|
$this->postJson($this->base.'/assign', ['version' => 1, 'user_id' => $this->second->id, 'profile_ids' => [], 'maintenance' => false, 'reason' => 'Tentativa de retirar o segundo administrador.'])->assertUnprocessable()->assertJsonValidationErrors('administrators');
|
|
$this->postJson($this->base.'/profile', ['version' => 1, 'id' => $this->adminProfile, 'name' => 'Sem poderes', 'permissions' => []])->assertUnprocessable()->assertJsonValidationErrors('administrators');
|
|
$this->postJson($this->base.'/assign', ['version' => 1, 'user_id' => $this->second->id, 'profile_ids' => [$this->adminProfile], 'maintenance' => false, 'expires_at' => now()->addDay()->toIso8601String(), 'reason' => 'Tentativa de tornar temporário o segundo administrador.'])->assertUnprocessable();
|
|
expect(DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->value('version'))->toBe(1)->and(DB::table('organization_memberships')->count())->toBe(2);
|
|
});
|
|
|
|
test('maintenance is named temporary read-only audited and expires without bypassing authentication', function () {
|
|
$profile = organizationProfile($this->f['tenant']->id, 'Consulta TEMPO', ['finance']);
|
|
$support = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'department_id' => $this->f['r']->department_id, 'purchasing_role' => PurchasingRole::Admin]);
|
|
$grant = ['version' => 1, 'user_id' => $support->id, 'profile_ids' => [$profile], 'maintenance' => true, 'expires_at' => now()->addHour()->toIso8601String(), 'reason' => 'Diagnóstico TEMPO identificado e autorizado no ensaio.'];
|
|
$this->post($this->base.'/assign', $grant)->assertRedirect();
|
|
$this->actingAs($support)->get('/facturas')->assertOk();
|
|
expect(AuditLog::query()->where('action', 'maintenance.consulted')->count())->toBe(1);
|
|
$this->post('/requisicoes', [])->assertForbidden();
|
|
$this->get($this->base)->assertForbidden();
|
|
$this->travel(2)->hours();
|
|
$this->get('/facturas')->assertForbidden();
|
|
auth()->logout();
|
|
$this->get('/facturas')->assertRedirect('/login');
|
|
});
|
|
|
|
test('maintenance cannot receive administration and access can be revoked immediately', function () {
|
|
$this->postJson($this->base.'/assign', ['version' => 1, 'user_id' => $this->second->id, 'profile_ids' => [$this->adminProfile], 'maintenance' => true, 'expires_at' => now()->addHour()->toIso8601String(), 'reason' => 'Tentativa de conceder administração à manutenção.'])->assertUnprocessable();
|
|
$profile = organizationProfile($this->f['tenant']->id, 'Compras da empresa', ['procurement']);
|
|
organizationMember($this->f['tenant']->id, $this->f['receiver']->id, $profile);
|
|
$this->actingAs($this->f['receiver'])->get('/fornecedores')->assertOk();
|
|
$this->actingAs($this->admin)->post($this->base.'/assign', ['version' => 1, 'user_id' => $this->f['receiver']->id, 'profile_ids' => [], 'maintenance' => false, 'reason' => 'Revogação imediata da atribuição de teste.'])->assertRedirect();
|
|
$this->actingAs($this->f['receiver'])->get('/fornecedores')->assertForbidden();
|
|
});
|
|
|
|
test('foreign profiles users forged permissions and stale configuration cannot be used', function () {
|
|
$other = receiptFixture();
|
|
$foreign = organizationProfile($other['tenant']->id, 'Perfil alheio', ['admin']);
|
|
$this->actingAs($this->admin)->postJson($this->base.'/assign', ['version' => 1, 'user_id' => $this->second->id, 'profile_ids' => [$foreign], 'maintenance' => false, 'reason' => 'Tentativa sobre um perfil de outra empresa.'])->assertUnprocessable();
|
|
$this->post($this->base.'/assign', ['version' => 1, 'user_id' => $other['issuer']->id, 'profile_ids' => [$this->adminProfile], 'maintenance' => false, 'reason' => 'Tentativa sobre utilizador de outra empresa.'])->assertNotFound();
|
|
$this->postJson($this->base.'/profile', ['version' => 1, 'name' => 'Forjado', 'permissions' => ['superuser']])->assertUnprocessable();
|
|
$this->postJson($this->base.'/profile', ['version' => 1, 'name' => 'Forjado', 'permissions' => ['finance'], 'tenant_id' => $other['tenant']->id])->assertUnprocessable();
|
|
$this->post($this->base.'/profile', ['version' => 1, 'name' => 'Perfil novo', 'permissions' => ['finance']])->assertRedirect();
|
|
$this->postJson($this->base.'/profile', ['version' => 1, 'name' => 'Sobreposição', 'permissions' => ['finance']])->assertUnprocessable()->assertJsonValidationErrors('version');
|
|
});
|
|
|
|
test('activation requires two assigned administrators and replaces the previous role authority', function () {
|
|
DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->update(['active' => false]);
|
|
DB::table('tenants')->where('id', $this->f['tenant']->id)->update(['rules_provisional' => false]);
|
|
DB::table('users')->where('id', $this->admin->id)->update(['purchasing_role' => 'admin']);
|
|
DB::table('organization_memberships')->where('user_id', $this->second->id)->delete();
|
|
$this->actingAs($this->admin->refresh())->postJson($this->base.'/activate', ['version' => 1, 'confirm' => true, 'reason' => 'Activação da configuração escolhida pela empresa.'])->assertUnprocessable();
|
|
organizationMember($this->f['tenant']->id, $this->second->id, $this->adminProfile);
|
|
$this->post($this->base.'/activate', ['version' => 1, 'confirm' => true, 'reason' => 'Activação da configuração escolhida pela empresa.'])->assertRedirect();
|
|
expect(app(OrganizationAccess::class)->active($this->f['tenant']->id))->toBeTrue();
|
|
$this->actingAs($this->f['receiver'])->get('/recepcoes')->assertForbidden();
|
|
});
|
|
|
|
test('configuration audit failure rolls a profile and its version back', function () {
|
|
$this->mock(RequisitionWorkflow::class)->shouldReceive('audit')->once()->andThrow(new RuntimeException('Injected audit failure'));
|
|
$this->withoutExceptionHandling();
|
|
expect(fn () => $this->post($this->base.'/profile', ['version' => 1, 'name' => 'Não persistir', 'permissions' => ['finance']]))->toThrow(RuntimeException::class);
|
|
expect(DB::table('organization_profiles')->where('name', 'Não persistir')->exists())->toBeFalse()->and(DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->value('version'))->toBe(1);
|
|
});
|
|
|
|
test('company rules replace provisional defaults exactly and preserve existing submitted snapshots', function () {
|
|
$before = $this->f['r']->refresh()->approval_snapshot;
|
|
$this->post($this->base.'/rules', ['version' => 1, 'first_limit' => '1234.56', 'second_limit' => '9876.54', 'includes_tax' => false, 'reason' => 'Limites expressamente definidos pela empresa no ensaio.'])->assertRedirect();
|
|
$company = $this->f['tenant']->refresh();
|
|
expect($company->first_limit_minor)->toBe(123456)->and($company->second_limit_minor)->toBe(987654)->and($company->rules_provisional)->toBeFalse()->and($company->approval_includes_tax)->toBeFalse();
|
|
expect($this->f['r']->refresh()->approval_snapshot)->toBe($before);
|
|
$this->postJson($this->base.'/rules', ['version' => 2, 'first_limit' => '2000.00', 'second_limit' => '1000.00', 'includes_tax' => true, 'reason' => 'Intervalo incoerente deve ser recusado.'])->assertUnprocessable()->assertJsonValidationErrors('second_limit');
|
|
});
|
|
|
|
test('maintenance profile cannot acquire administration by editing its permissions', function () {
|
|
$profile = organizationProfile($this->f['tenant']->id, 'Diagnóstico temporário', ['finance']);
|
|
$support = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'department_id' => $this->f['r']->department_id]);
|
|
$this->post($this->base.'/assign', ['version' => 1, 'user_id' => $support->id, 'profile_ids' => [$profile], 'maintenance' => true, 'expires_at' => now()->addHour()->toIso8601String(), 'reason' => 'Acesso temporário nominativo para diagnóstico.'])->assertRedirect();
|
|
$this->postJson($this->base.'/profile', ['version' => 2, 'id' => $profile, 'name' => 'Diagnóstico temporário', 'permissions' => ['finance', 'admin']])->assertUnprocessable()->assertJsonValidationErrors('permissions');
|
|
expect($support->hasFunctions(['admin']))->toBeFalse();
|
|
});
|
|
|
|
test('the product default seeder does not create demonstration accounts or change existing users', function () {
|
|
$before = User::query()->orderBy('id')->get()->map(fn (User $u) => $u->getAttributes())->all();
|
|
$this->seed(DatabaseSeeder::class);
|
|
expect(User::query()->orderBy('id')->get()->map(fn (User $u) => $u->getAttributes())->all())->toBe($before);
|
|
expect(User::query()->where('email', 'test@example.com')->exists())->toBeFalse();
|
|
});
|
|
|
|
test('a configured account has no fallback development permissions while activation is pending', function () {
|
|
$staged = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'department_id' => $this->f['r']->department_id, 'purchasing_role' => PurchasingRole::Configured]);
|
|
organizationMember($this->f['tenant']->id, $staged->id, $this->adminProfile);
|
|
DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->update(['active' => false]);
|
|
$this->actingAs($staged)->get($this->base)->assertForbidden();
|
|
$this->get('/requisicoes/criar')->assertForbidden();
|
|
expect(app(OrganizationAccess::class)->canRequest($staged))->toBeFalse();
|
|
DB::table('organization_access')->where('tenant_id', $this->f['tenant']->id)->update(['active' => true]);
|
|
$this->get($this->base)->assertOk();
|
|
});
|