| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- <?php
- namespace Tests\Unit\Services;
- use App\Models\Permission;
- use App\Models\Role;
- use App\Models\User;
- use App\Services\Access\AccessService;
- use Database\Seeders\RbacSeeder;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Tests\TestCase;
- class AccessServiceTest extends TestCase
- {
- use RefreshDatabase;
- protected $seed = true;
- public function test_rbac_seeder_backfills_user_role_id(): void
- {
- $user = User::factory()->create(['role' => Role::MANAGER]);
- $this->seed(RbacSeeder::class);
- $user->refresh();
- $this->assertNotNull($user->role_id);
- $this->assertSame(Role::MANAGER, $user->roleModel->slug);
- }
- public function test_admin_role_has_seeded_permissions(): void
- {
- $admin = User::factory()->create(['role' => Role::ADMIN]);
- $this->seed(RbacSeeder::class);
- $admin->refresh();
- $this->assertTrue(app(AccessService::class)->can($admin, 'catalog.delete'));
- $this->assertTrue(app(AccessService::class)->can($admin, 'catalog.fields.product_price.update'));
- }
- public function test_manager_has_seeded_permissions_but_not_admin_only_permissions(): void
- {
- $manager = User::factory()->create(['role' => Role::MANAGER]);
- $this->seed(RbacSeeder::class);
- $manager->refresh();
- $this->assertTrue(app(AccessService::class)->can($manager, 'catalog.view'));
- $this->assertTrue(app(AccessService::class)->can($manager, 'catalog.fields.product_price.view'));
- $this->assertFalse(app(AccessService::class)->can($manager, 'catalog.import'));
- $this->assertFalse(app(AccessService::class)->can($manager, 'catalog.update'));
- }
- public function test_seeded_roles_have_visibility_scopes(): void
- {
- $manager = User::factory()->create(['role' => Role::MANAGER]);
- $brigadier = User::factory()->create(['role' => Role::BRIGADIER]);
- $warehouseHead = User::factory()->create(['role' => Role::WAREHOUSE_HEAD]);
- $this->seed(RbacSeeder::class);
- $this->assertSame('manager', $manager->refresh()->visibilityScope('orders'));
- $this->assertSame('brigadier', $brigadier->refresh()->visibilityScope('orders'));
- $this->assertSame('warehouse_head', $warehouseHead->refresh()->visibilityScope('orders'));
- $this->assertSame('manager', $warehouseHead->visibilityScope('schedule'));
- }
- public function test_visibility_scope_uses_highest_priority_permission(): void
- {
- $this->seed(RbacSeeder::class);
- $role = Role::query()->create([
- 'slug' => 'hybrid_scope',
- 'name' => 'Hybrid scope',
- 'is_system' => false,
- 'is_active' => true,
- ]);
- $permissions = Permission::query()
- ->whereIn('slug', ['orders.scope.brigadier', 'orders.scope.manager'])
- ->pluck('id')
- ->mapWithKeys(fn (int $id): array => [$id => ['effect' => 'allow']]);
- $role->permissions()->sync($permissions);
- $user = User::factory()->create(['role' => $role->slug, 'role_id' => $role->id]);
- $this->assertSame('manager', app(AccessService::class)->visibilityScope($user, 'orders'));
- }
- public function test_role_deny_overrides_user_allow(): void
- {
- $this->seed(RbacSeeder::class);
- $permission = Permission::query()->where('slug', 'catalog.view')->firstOrFail();
- $role = Role::query()->create([
- 'slug' => 'deny_catalog',
- 'name' => 'Deny catalog',
- 'is_system' => false,
- 'is_active' => true,
- ]);
- $role->permissions()->sync([
- $permission->id => ['effect' => 'deny'],
- ]);
- $user = User::factory()->create(['role' => $role->slug, 'role_id' => $role->id]);
- $user->permissions()->sync([
- $permission->id => ['effect' => 'allow'],
- ]);
- app(AccessService::class)->bumpCacheVersion();
- $this->assertFalse(app(AccessService::class)->can($user, 'catalog.view'));
- }
- public function test_exact_route_permission_lookup_supports_dotted_route_names(): void
- {
- $this->assertSame(
- 'catalog.search',
- app(AccessService::class)->routePermission('product.search')
- );
- }
- public function test_user_deny_overrides_role_allow(): void
- {
- $manager = User::factory()->create(['role' => Role::MANAGER]);
- $this->seed(RbacSeeder::class);
- $manager->refresh();
- $permission = Permission::query()->where('slug', 'catalog.view')->firstOrFail();
- $manager->permissions()->syncWithoutDetaching([
- $permission->id => ['effect' => 'deny'],
- ]);
- app(AccessService::class)->bumpCacheVersion();
- $this->assertFalse(app(AccessService::class)->can($manager, 'catalog.view'));
- }
- public function test_assistant_head_has_materialized_admin_permissions_without_runtime_inheritance(): void
- {
- $assistantHead = User::factory()->create(['role' => Role::ASSISTANT_HEAD]);
- $this->seed(RbacSeeder::class);
- $assistantHead->refresh();
- $this->assertTrue(app(AccessService::class)->can($assistantHead, 'maf_orders.delete'));
- }
- public function test_custom_role_with_admin_permissions_has_same_action_access(): void
- {
- $this->seed(RbacSeeder::class);
- $role = Role::query()->create([
- 'slug' => 'root',
- 'name' => 'Root',
- 'is_system' => false,
- 'is_active' => true,
- ]);
- $permissions = Permission::query()
- ->pluck('id')
- ->mapWithKeys(fn (int $id): array => [$id => ['effect' => 'allow']]);
- $role->permissions()->sync($permissions);
- $user = User::factory()->create(['role' => $role->slug, 'role_id' => $role->id]);
- $this->assertTrue(app(AccessService::class)->can($user, 'admin.roles'));
- $this->assertTrue(app(AccessService::class)->can($user, 'catalog.delete'));
- $this->assertTrue(app(AccessService::class)->can($user, 'orders.documents.generate'));
- $this->assertSame('admin', app(AccessService::class)->visibilityScope($user, 'orders'));
- }
- public function test_user_permission_query_scopes_include_custom_roles(): void
- {
- $this->seed(RbacSeeder::class);
- $role = Role::query()->create([
- 'slug' => 'root_scope',
- 'name' => 'Root scope',
- 'is_system' => false,
- 'is_active' => true,
- ]);
- $permission = Permission::query()->where('slug', 'orders.scope.admin')->firstOrFail();
- $role->permissions()->sync([$permission->id => ['effect' => 'allow']]);
- $user = User::factory()->create(['role' => $role->slug, 'role_id' => $role->id]);
- $this->assertTrue(User::query()->withPermission('orders.scope.admin')->whereKey($user->id)->exists());
- }
- }
|