refactor: update toggleStatus method in EmployeeController to accept validated request data for improved status management
This commit is contained in:
parent
86956b1c9f
commit
e3b138d410
@ -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.');
|
||||
|
||||
|
||||
540
tests/Feature/Admin/Hr/EmployeeTest.php
Normal file
540
tests/Feature/Admin/Hr/EmployeeTest.php
Normal 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]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user