refactor: update toggleStatus method in EmployeeController to accept validated request data for improved status management

This commit is contained in:
Yoga Pangestu 2026-06-22 18:20:42 +07:00
parent 86956b1c9f
commit e3b138d410
2 changed files with 541 additions and 1 deletions

View File

@ -99,7 +99,7 @@ public function update(EmployeeRequest $request, User $user): RedirectResponse
public function toggleStatus(ToggleStatusRequest $request, User $user): RedirectResponse
{
$this->employeeService->toggleStatus($user);
$this->employeeService->toggleStatus($user, $request->validated());
$this->flashSuccess('Status pegawai berhasil diubah.');

View File

@ -0,0 +1,540 @@
<?php
use App\Enums\Permission as PermissionEnum;
use App\Models\Employee;
use App\Models\User;
use App\Models\UserProfile;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
});
// ─── Helper ───────────────────────────────────────────────
function createEmployeeUserWithPermission(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;
}
function createEmployeeWithProfile(): User
{
$user = User::factory()->create();
UserProfile::factory()->create([
'user_id' => $user->id,
'full_name' => fake()->name(),
'phone_number' => fake()->numerify('08##########'),
'gender' => fake()->randomElement(['male', 'female']),
]);
Employee::factory()->create(['user_id' => $user->id]);
$user->assignRole('marketing');
return $user;
}
function validEmployeePayload(): array
{
return [
'email' => 'pegawai@example.com',
'username' => 'pegawai_baru',
'full_name' => 'Pegawai Baru',
'phone_number' => '081234567890',
'gender' => 'male',
'birth_date' => '1995-01-15',
'address' => 'Jl. Merdeka No. 1',
'role' => 'marketing',
'join_date' => '2024-01-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
];
}
// ─── Index ────────────────────────────────────────────────
describe('Employee Index', function () {
test('authenticated user with permission can view employee index', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$this->actingAs($user)
->get(route('admin.hr.employees.index'))
->assertOk();
});
test('guest is redirected to login', function () {
$this->get(route('admin.hr.employees.index'))
->assertRedirect(route('login'));
});
test('user without permission is forbidden', function () {
$user = User::factory()->create();
$this->actingAs($user)
->get(route('admin.hr.employees.index'))
->assertForbidden();
});
test('index displays employees', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
createEmployeeWithProfile();
createEmployeeWithProfile();
$this->actingAs($user)
->get(route('admin.hr.employees.index'))
->assertOk();
});
test('index can search by name', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$employee = createEmployeeWithProfile();
$employee->profile->update(['full_name' => 'Budi Santoso']);
$this->actingAs($user)
->get(route('admin.hr.employees.index', ['search' => 'Budi']))
->assertOk();
});
test('index can filter by role', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
createEmployeeWithProfile();
$this->actingAs($user)
->get(route('admin.hr.employees.index', ['role' => 'marketing']))
->assertOk();
});
test('index can filter by is_active status', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
createEmployeeWithProfile();
$this->actingAs($user)
->get(route('admin.hr.employees.index', ['is_active' => '1']))
->assertOk();
});
});
// ─── Create ───────────────────────────────────────────────
describe('Employee Create', function () {
test('authenticated user with permission can view create form', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$this->actingAs($user)
->get(route('admin.hr.employees.create'))
->assertOk();
});
test('guest is redirected to login', function () {
$this->get(route('admin.hr.employees.create'))
->assertRedirect(route('login'));
});
test('user without create permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$this->actingAs($user)
->get(route('admin.hr.employees.create'))
->assertForbidden();
});
});
// ─── Store ────────────────────────────────────────────────
describe('Employee Store', function () {
test('authenticated user with permission can create an employee', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$this->actingAs($user)
->post(route('admin.hr.employees.store'), validEmployeePayload())
->assertRedirect(route('admin.hr.employees.index'));
$this->assertDatabaseHas('users', [
'email' => 'pegawai@example.com',
'username' => 'pegawai_baru',
]);
});
test('guest cannot create an employee', function () {
$this->post(route('admin.hr.employees.store'), validEmployeePayload())
->assertRedirect(route('login'));
});
test('user without create permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$this->actingAs($user)
->post(route('admin.hr.employees.store'), validEmployeePayload())
->assertForbidden();
});
test('email is required', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['email'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('email');
});
test('email must be unique', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$existing = User::factory()->create();
$payload = validEmployeePayload();
$payload['email'] = $existing->email;
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('email');
});
test('username is required', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['username'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('username');
});
test('username must be unique', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$existing = User::factory()->create();
$payload = validEmployeePayload();
$payload['username'] = $existing->username;
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('username');
});
test('full_name is required', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['full_name'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('full_name');
});
test('role is required', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['role'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('role');
});
test('role must be assignable', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['role'] = 'developer';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('role');
});
test('join_date is required for non-owner roles', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['join_date'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('join_date');
});
test('employment_status is required for non-owner roles', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['employment_status'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('employment_status');
});
test('base_salary is required for non-owner roles', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['base_salary'] = '';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('base_salary');
});
test('phone_number must be valid format', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$payload = validEmployeePayload();
$payload['phone_number'] = '123invalid';
$this->actingAs($user)
->post(route('admin.hr.employees.store'), $payload)
->assertSessionHasErrors('phone_number');
});
test('creating employee also creates profile and employee record', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);
$this->actingAs($user)
->post(route('admin.hr.employees.store'), validEmployeePayload());
$newUser = User::where('email', 'pegawai@example.com')->first();
expect($newUser)->not->toBeNull();
expect($newUser->profile)->not->toBeNull();
expect($newUser->employee)->not->toBeNull();
expect($newUser->profile->full_name)->toBe('Pegawai Baru');
expect($newUser->employee->base_salary)->toBe(5000000);
});
});
// ─── Edit ─────────────────────────────────────────────────
describe('Employee Edit', function () {
test('authenticated user with permission can view edit form', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_UPDATE);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->get(route('admin.hr.employees.edit', $employee))
->assertOk();
});
test('guest is redirected to login', function () {
$employee = createEmployeeWithProfile();
$this->get(route('admin.hr.employees.edit', $employee))
->assertRedirect(route('login'));
});
test('user without update permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->get(route('admin.hr.employees.edit', $employee))
->assertForbidden();
});
});
// ─── Update ───────────────────────────────────────────────
describe('Employee Update', function () {
test('authenticated user with permission can update an employee', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_UPDATE);
$employee = createEmployeeWithProfile();
$payload = validEmployeePayload();
$payload['email'] = $employee->email;
$payload['username'] = $employee->username;
$payload['full_name'] = 'Nama Diubah';
$this->actingAs($user)
->put(route('admin.hr.employees.update', $employee), $payload)
->assertRedirect(route('admin.hr.employees.index'));
expect($employee->fresh()->profile->full_name)->toBe('Nama Diubah');
});
test('guest cannot update an employee', function () {
$employee = createEmployeeWithProfile();
$this->put(route('admin.hr.employees.update', $employee), validEmployeePayload())
->assertRedirect(route('login'));
});
test('user without update permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->put(route('admin.hr.employees.update', $employee), validEmployeePayload())
->assertForbidden();
});
test('email must be unique on update (ignoring self)', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_UPDATE);
$employee = createEmployeeWithProfile();
$payload = validEmployeePayload();
$payload['email'] = $employee->email;
$payload['username'] = $employee->username;
$this->actingAs($user)
->put(route('admin.hr.employees.update', $employee), $payload)
->assertRedirect(route('admin.hr.employees.index'));
});
});
// ─── Toggle Status ────────────────────────────────────────
describe('Employee Toggle Status', function () {
test('authenticated user with permission can toggle status', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_TOGGLE_STATUS);
$employee = createEmployeeWithProfile();
$originalStatus = $employee->is_active;
$this->actingAs($user)
->patch(route('admin.hr.employees.toggle_status', $employee), [
'is_active' => ! $originalStatus,
])
->assertRedirect(route('admin.hr.employees.index'));
expect($employee->fresh()->is_active)->toBe(! $originalStatus);
});
test('guest cannot toggle status', function () {
$employee = createEmployeeWithProfile();
$this->patch(route('admin.hr.employees.toggle_status', $employee), [
'is_active' => false,
])->assertRedirect(route('login'));
});
test('user without toggle permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->patch(route('admin.hr.employees.toggle_status', $employee), [
'is_active' => false,
])
->assertForbidden();
});
test('is_active field is required', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_TOGGLE_STATUS);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->patch(route('admin.hr.employees.toggle_status', $employee), [
'is_active' => null,
])
->assertSessionHasErrors('is_active');
});
});
// ─── Reset Password ───────────────────────────────────────
describe('Employee Reset Password', function () {
test('authenticated user with permission can reset password', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_RESET_PASSWORD);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->post(route('admin.hr.employees.reset_password', $employee))
->assertRedirect(route('admin.hr.employees.index'));
});
test('guest cannot reset password', function () {
$employee = createEmployeeWithProfile();
$this->post(route('admin.hr.employees.reset_password', $employee))
->assertRedirect(route('login'));
});
test('user without reset password permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->post(route('admin.hr.employees.reset_password', $employee))
->assertForbidden();
});
});
// ─── Destroy ──────────────────────────────────────────────
describe('Employee Destroy', function () {
test('authenticated user with permission can delete an employee', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_DELETE);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->delete(route('admin.hr.employees.destroy', $employee))
->assertRedirect(route('admin.hr.employees.index'));
$this->assertSoftDeleted('users', ['id' => $employee->id]);
});
test('guest cannot delete an employee', function () {
$employee = createEmployeeWithProfile();
$this->delete(route('admin.hr.employees.destroy', $employee))
->assertRedirect(route('login'));
$this->assertNotSoftDeleted('users', ['id' => $employee->id]);
});
test('user without delete permission is forbidden', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);
$employee = createEmployeeWithProfile();
$this->actingAs($user)
->delete(route('admin.hr.employees.destroy', $employee))
->assertForbidden();
$this->assertNotSoftDeleted('users', ['id' => $employee->id]);
});
test('deleting employee also soft deletes profile and employee record', function () {
$user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_DELETE);
$employee = createEmployeeWithProfile();
$profileId = $employee->profile->id;
$employeeId = $employee->employee->id;
$this->actingAs($user)
->delete(route('admin.hr.employees.destroy', $employee));
$this->assertSoftDeleted('users', ['id' => $employee->id]);
$this->assertSoftDeleted('user_profiles', ['id' => $profileId]);
$this->assertSoftDeleted('employees', ['id' => $employeeId]);
});
});