seed(RolePermissionSeeder::class); }); // ─── Helper ─────────────────────────────────────────────── function createRoleUserWithPermission(PermissionEnum ...$permissions): User { $user = User::factory()->create(); $user->givePermissionTo( array_merge( [PermissionEnum::DASHBOARD_VIEW->value], array_map(fn (PermissionEnum $p) => $p->value, $permissions) ) ); $user->forgetCachedPermissions(); return $user; } // ─── Index ──────────────────────────────────────────────── describe('Role Index', function () { test('authenticated user with permission can view role index', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $this->actingAs($user) ->get(route('admin.system.roles.index')) ->assertOk(); }); test('guest is redirected to login', function () { $this->get(route('admin.system.roles.index')) ->assertRedirect(route('login')); }); test('user without permission is forbidden', function () { $user = User::factory()->create(); $this->actingAs($user) ->get(route('admin.system.roles.index')) ->assertForbidden(); }); test('index displays all seeded roles', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $this->actingAs($user) ->get(route('admin.system.roles.index')) ->assertOk(); }); test('index can search roles by name', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $this->actingAs($user) ->get(route('admin.system.roles.index', ['search' => 'developer'])) ->assertOk(); }); }); // ─── Create ─────────────────────────────────────────────── describe('Role Create', function () { test('authenticated user with permission can view create form', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->get(route('admin.system.roles.create')) ->assertOk(); }); test('guest is redirected to login', function () { $this->get(route('admin.system.roles.create')) ->assertRedirect(route('login')); }); test('user without create permission is forbidden', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $this->actingAs($user) ->get(route('admin.system.roles.create')) ->assertForbidden(); }); }); // ─── Store ──────────────────────────────────────────────── describe('Role Store', function () { test('authenticated user with permission can create a role', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => 'Custom Role', 'permissions' => [PermissionEnum::DASHBOARD_VIEW->value, PermissionEnum::CATEGORIES_VIEW->value], ]) ->assertRedirect(route('admin.system.roles.index')); $this->assertDatabaseHas('roles', [ 'name' => 'custom-role', ]); $role = Role::findByName('custom-role'); expect($role->permissions->pluck('name')->toArray()) ->toContain(PermissionEnum::DASHBOARD_VIEW->value, PermissionEnum::CATEGORIES_VIEW->value); }); test('guest cannot create a role', function () { $this->post(route('admin.system.roles.store'), [ 'name' => 'New Role', ])->assertRedirect(route('login')); }); test('user without create permission is forbidden', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => 'New Role', ]) ->assertForbidden(); }); test('name is required', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => '', ]) ->assertSessionHasErrors('name'); }); test('name must not exceed 50 characters', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => str_repeat('a', 51), ]) ->assertSessionHasErrors('name'); }); test('name must be unique', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => RoleEnum::DEVELOPER->value, ]) ->assertSessionHasErrors('name'); }); test('name is slugified before saving', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => 'My Custom Role', ]) ->assertRedirect(route('admin.system.roles.index')); $this->assertDatabaseHas('roles', [ 'name' => 'my-custom-role', ]); }); test('permissions must exist in database', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => 'new-role', 'permissions' => ['nonexistent.permission'], ]) ->assertSessionHasErrors('permissions.0'); }); test('role can be created without permissions', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_CREATE); $this->actingAs($user) ->post(route('admin.system.roles.store'), [ 'name' => 'empty-role', 'permissions' => [], ]) ->assertRedirect(route('admin.system.roles.index')); $this->assertDatabaseHas('roles', [ 'name' => 'empty-role', ]); $role = Role::findByName('empty-role'); expect($role->permissions)->toHaveCount(0); }); }); // ─── Edit ───────────────────────────────────────────────── describe('Role Edit', function () { test('authenticated user with permission can view edit form', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::findByName(RoleEnum::ADMIN_TOKO->value); $this->actingAs($user) ->get(route('admin.system.roles.edit', $role)) ->assertOk(); }); test('guest is redirected to login', function () { $role = Role::findByName(RoleEnum::ADMIN_TOKO->value); $this->get(route('admin.system.roles.edit', $role)) ->assertRedirect(route('login')); }); test('user without update permission is forbidden', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $role = Role::findByName(RoleEnum::ADMIN_TOKO->value); $this->actingAs($user) ->get(route('admin.system.roles.edit', $role)) ->assertForbidden(); }); test('edit page shows role data with permissions', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::findByName(RoleEnum::ADMIN_TOKO->value); $this->actingAs($user) ->get(route('admin.system.roles.edit', $role)) ->assertOk(); }); test('protected roles are flagged as protected', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::findByName(RoleEnum::DEVELOPER->value); $this->actingAs($user) ->get(route('admin.system.roles.edit', $role)) ->assertOk(); }); }); // ─── Update ─────────────────────────────────────────────── describe('Role Update', function () { test('authenticated user with permission can update a role', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::create(['name' => 'updatable-role', 'guard_name' => 'web']); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'updated-role-name', 'permissions' => [PermissionEnum::DASHBOARD_VIEW->value], ]) ->assertRedirect(route('admin.system.roles.index')); $this->assertDatabaseHas('roles', [ 'id' => $role->id, 'name' => 'updated-role-name', ]); $role->refresh(); expect($role->permissions->pluck('name')->toArray()) ->toContain(PermissionEnum::DASHBOARD_VIEW->value); }); test('guest cannot update a role', function () { $role = Role::create(['name' => 'test-role', 'guard_name' => 'web']); $this->put(route('admin.system.roles.update', $role), [ 'name' => 'new-name', ])->assertRedirect(route('login')); }); test('user without update permission is forbidden', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW); $role = Role::create(['name' => 'test-role', 'guard_name' => 'web']); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'new-name', ]) ->assertForbidden(); }); test('name is required on update', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::create(['name' => 'test-role', 'guard_name' => 'web']); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => '', ]) ->assertSessionHasErrors('name'); }); test('name must be unique on update', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::create(['name' => 'test-role', 'guard_name' => 'web']); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => RoleEnum::OWNER->value, ]) ->assertSessionHasErrors('name'); }); test('name can stay the same on update', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::create(['name' => 'test-role', 'guard_name' => 'web']); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'test-role', 'permissions' => [], ]) ->assertRedirect(route('admin.system.roles.index')); }); test('protected role name cannot be changed', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::findByName(RoleEnum::DEVELOPER->value); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'renamed-developer', 'permissions' => $role->permissions->pluck('name')->toArray(), ]) ->assertRedirect(route('admin.system.roles.index')); $this->assertDatabaseHas('roles', [ 'id' => $role->id, 'name' => RoleEnum::DEVELOPER->value, ]); }); test('owner role name cannot be changed', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::findByName(RoleEnum::OWNER->value); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'renamed-owner', 'permissions' => $role->permissions->pluck('name')->toArray(), ]) ->assertRedirect(route('admin.system.roles.index')); $this->assertDatabaseHas('roles', [ 'id' => $role->id, 'name' => RoleEnum::OWNER->value, ]); }); test('role permissions can be updated', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::create(['name' => 'perm-test-role', 'guard_name' => 'web']); $role->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'perm-test-role', 'permissions' => [PermissionEnum::CATEGORIES_VIEW->value, PermissionEnum::ORDERS_VIEW->value], ]) ->assertRedirect(route('admin.system.roles.index')); $role->refresh(); $permissionNames = $role->permissions->pluck('name')->toArray(); expect($permissionNames)->toContain(PermissionEnum::CATEGORIES_VIEW->value); expect($permissionNames)->toContain(PermissionEnum::ORDERS_VIEW->value); expect($permissionNames)->not->toContain(PermissionEnum::DASHBOARD_VIEW->value); }); test('all permissions can be removed from role', function () { $user = createRoleUserWithPermission(PermissionEnum::ROLES_VIEW, PermissionEnum::ROLES_UPDATE); $role = Role::create(['name' => 'clear-perm-role', 'guard_name' => 'web']); $role->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $this->actingAs($user) ->put(route('admin.system.roles.update', $role), [ 'name' => 'clear-perm-role', 'permissions' => [], ]) ->assertRedirect(route('admin.system.roles.index')); $role->refresh(); expect($role->permissions)->toHaveCount(0); }); });