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_direct_admin_has_all_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.update')); } 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($assistantHead->hasRole(Role::ADMIN)); $this->assertTrue(app(AccessService::class)->can($assistantHead, 'maf_orders.delete')); } }