withoutMiddleware(EnsurePlatformSession::class); $this->user = User::create([ 'public_id' => 'emp-user-001', 'name' => 'Employee Test User', 'email' => 'employee-test@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->user->public_id, 'name' => 'Employee Org', 'slug' => 'employee-org', 'settings' => ['onboarded' => true, 'employee_kiosk_enabled' => true], ]); Member::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $this->user->public_id, 'role' => 'org_admin', ]); $this->deviceToken = 'kiosk-employee-'.str_repeat('y', 32); Device::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'name' => 'Lobby Kiosk', 'type' => 'kiosk', 'status' => 'offline', 'device_token' => $this->deviceToken, ]); } protected function createEmployee(string $code = 'EMP001', string $pin = '1234'): Employee { $service = app(EmployeePresenceService::class); $employee = Employee::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'employee_code' => $code, 'full_name' => 'Jane Staff', 'department' => 'Operations', 'pin_hash' => $service->hashPin($pin), 'active' => true, ]); $service->presenceFor($employee); return $employee; } public function test_admin_can_create_employee(): void { $this->actingAs($this->user) ->post(route('frontdesk.employees.store'), [ 'employee_code' => 'EMP100', 'full_name' => 'Kwame Ops', 'pin' => '4321', 'department' => 'Ops', ]) ->assertRedirect(route('frontdesk.employees.index')); $this->assertDatabaseHas('frontdesk_employees', [ 'employee_code' => 'EMP100', 'full_name' => 'Kwame Ops', ]); } public function test_kiosk_staff_sign_in_and_step_out_flow(): void { $this->createEmployee(); $this->postJson(route('frontdesk.kiosk.device.staff.action', $this->deviceToken), [ 'employee_code' => 'EMP001', 'pin' => '1234', 'action' => 'sign_in', ])->assertOk() ->assertJsonPath('employee.status', 'on_site'); $this->postJson(route('frontdesk.kiosk.device.staff.action', $this->deviceToken), [ 'employee_code' => 'EMP001', 'pin' => '1234', 'action' => 'step_out', 'step_out_reason' => 'client_visit', 'destination' => 'Client office', ])->assertOk() ->assertJsonPath('employee.status', 'stepped_out') ->assertJsonPath('employee.destination', 'Client office'); $this->postJson(route('frontdesk.kiosk.device.staff.action', $this->deviceToken), [ 'employee_code' => 'EMP001', 'pin' => '1234', 'action' => 'step_in', ])->assertOk() ->assertJsonPath('employee.status', 'on_site'); $this->postJson(route('frontdesk.kiosk.device.staff.action', $this->deviceToken), [ 'employee_code' => 'EMP001', 'pin' => '1234', 'action' => 'sign_out', ])->assertOk() ->assertJsonPath('employee.status', 'off_site'); $this->assertDatabaseCount('frontdesk_employee_presence_events', 4); } public function test_kiosk_rejects_invalid_pin(): void { $this->createEmployee(); $this->postJson(route('frontdesk.kiosk.device.staff.action', $this->deviceToken), [ 'employee_code' => 'EMP001', 'pin' => '9999', 'action' => 'sign_in', ])->assertUnprocessable(); } public function test_security_dashboard_lists_staff_on_site(): void { $employee = $this->createEmployee(); app(EmployeePresenceService::class)->signIn($employee)['presence']; $this->actingAs($this->user) ->get(route('frontdesk.security.index')) ->assertOk() ->assertSee('Jane Staff', false) ->assertSee('Staff on premises', false); } public function test_dashboard_staff_on_site_updates_immediately_after_sign_out(): void { Cache::flush(); $employee = $this->createEmployee(); $service = app(EmployeePresenceService::class); $service->signIn($employee); $this->actingAs($this->user) ->get(route('frontdesk.dashboard')) ->assertOk() ->assertViewHas('stats', fn (array $stats) => $stats['staff_on_site'] === 1); $service->signOut($employee); $this->actingAs($this->user) ->get(route('frontdesk.dashboard')) ->assertOk() ->assertViewHas('stats', fn (array $stats) => $stats['staff_on_site'] === 0); } }