419 lines
15 KiB
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);
|
|
});
|
|
});
|