store/tests/Feature/Admin/System/RoleTest.php

419 lines
15 KiB
PHP

<?php
use App\Enums\Permission as PermissionEnum;
use App\Enums\Role as RoleEnum;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->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);
});
});