'test-owner-uuid', 'name' => 'Test User', 'email' => 'test@example.com', 'password' => bcrypt('password'), ]); $resolver = app(OrganizationResolver::class); $org = $resolver->completeOnboarding($user, [ 'organization_name' => 'Test Org', 'industry' => 'retail', 'appointment_mode' => 'hybrid', 'branch_name' => 'Main', 'timezone' => 'UTC', ]); $branch = Branch::first(); $queue = ServiceQueue::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Reception', 'prefix' => 'A', 'strategy' => 'fifo', 'is_active' => true, ]); return [$user, $org, $branch, $queue]; } public function test_issue_ticket_increments_sequence(): void { [$user, , , $queue] = $this->setUpOrg(); $engine = app(QueueEngine::class); $ticket = $engine->issueTicket($queue, $user->public_id, ['source' => 'api']); $this->assertSame('A001', $ticket->ticket_number); $this->assertSame('waiting', $ticket->status); $ticket2 = $engine->issueTicket($queue->fresh(), $user->public_id); $this->assertSame('A002', $ticket2->ticket_number); } public function test_health_endpoint(): void { $this->getJson('/api/health')->assertOk()->assertJson(['app' => 'queue']); } public function test_appointment_check_in_issues_ticket(): void { [$user, $org, $branch, $queue] = $this->setUpOrg(); $appointment = \App\Models\QueueAppointment::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'service_queue_id' => $queue->id, 'customer_name' => 'Jane Doe', 'status' => 'scheduled', 'scheduled_at' => now()->addHour(), 'reference' => 'APT-TEST', ]); $ticket = app(\App\Services\Qms\AppointmentService::class)->checkIn($appointment, $user->public_id); $this->assertSame('appointment', $ticket->priority); $this->assertSame('checked_in', $appointment->fresh()->status); $this->assertSame($ticket->id, $appointment->fresh()->ticket_id); } public function test_workflow_metadata_attached_on_issue(): void { [$user, $org, $branch, $queue] = $this->setUpOrg(); $workflow = \App\Models\Workflow::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'name' => 'Test flow', 'is_active' => true, ]); \App\Models\WorkflowStep::create([ 'workflow_id' => $workflow->id, 'service_queue_id' => $queue->id, 'name' => 'Step 1', 'sort_order' => 1, ]); $ticket = app(QueueEngine::class)->issueTicket($queue, $user->public_id); $this->assertSame($workflow->id, data_get($ticket->metadata, 'workflow_id')); } public function test_routing_rule_redirects_ticket(): void { [$user, $org, $branch, $queue] = $this->setUpOrg(); $target = ServiceQueue::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'VIP', 'prefix' => 'V', 'strategy' => 'fifo', 'is_active' => true, ]); \App\Models\QueueRule::create([ 'owner_ref' => $user->public_id, 'service_queue_id' => $queue->id, 'rule_type' => 'routing', 'conditions' => ['source' => 'api'], 'actions' => ['target_queue_id' => $target->id], 'sort_order' => 1, 'is_active' => true, ]); $ticket = app(QueueEngine::class)->issueTicket($queue, $user->public_id, ['source' => 'api']); $this->assertSame($target->id, $ticket->service_queue_id); } public function test_service_session_tracked(): void { [$user, , , $queue] = $this->setUpOrg(); $engine = app(QueueEngine::class); $ticket = $engine->issueTicket($queue, $user->public_id); $counter = \App\Models\Counter::create([ 'owner_ref' => $user->public_id, 'organization_id' => $queue->organization_id, 'branch_id' => $queue->branch_id, 'name' => 'C1', 'status' => 'available', 'is_active' => true, ]); $engine->callTicket($ticket, $counter, $user->public_id); $engine->startServing($ticket->fresh(), $user->public_id); $engine->completeTicket($ticket->fresh(), $user->public_id); $this->assertDatabaseHas('queue_service_sessions', [ 'ticket_id' => $ticket->id, ]); $session = \App\Models\ServiceSession::where('ticket_id', $ticket->id)->first(); $this->assertNotNull($session->ended_at); } public function test_handoff_keeps_ticket_number_and_frees_counter(): void { [$user, $org, $branch, $queue] = $this->setUpOrg(); $lab = ServiceQueue::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Lab', 'prefix' => 'L', 'strategy' => 'fifo', 'is_active' => true, ]); $engine = app(QueueEngine::class); $ticket = $engine->issueTicket($queue, $user->public_id, [ 'source' => 'reception', 'customer_name' => 'Patient One', ]); $this->assertSame('A001', $ticket->ticket_number); $counter = \App\Models\Counter::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Doctor 1', 'status' => 'available', 'is_active' => true, ]); $engine->callTicket($ticket, $counter, $user->public_id); $engine->startServing($ticket->fresh(), $user->public_id); $handedOff = $engine->transfer( $ticket->fresh(), $lab, $counter, 'Lab tests needed', $user->public_id, ); $this->assertSame('A001', $handedOff->ticket_number); $this->assertSame($lab->id, $handedOff->service_queue_id); $this->assertSame('waiting', $handedOff->status); $this->assertNull($handedOff->counter_id); $this->assertNull($handedOff->completed_at); $this->assertSame('available', $counter->fresh()->status); $this->assertDatabaseHas('queue_ticket_transfers', [ 'ticket_id' => $ticket->id, 'from_service_queue_id' => $queue->id, 'to_service_queue_id' => $lab->id, 'reason' => 'Lab tests needed', ]); $session = \App\Models\ServiceSession::where('ticket_id', $ticket->id)->first(); $this->assertNotNull($session); $this->assertNotNull($session->ended_at); } public function test_reports_sqlite_compatible(): void { [$user, $org, , $queue] = $this->setUpOrg(); app(QueueEngine::class)->issueTicket($queue, $user->public_id, ['source' => 'api']); $reports = app(\App\Services\Qms\ReportService::class); $from = now()->subDay(); $to = now()->addDay(); $data = $reports->ticketsReport($user->public_id, $org->id, $from, $to); $this->assertArrayHasKey('total_issued', $data); $this->assertGreaterThanOrEqual(1, $data['total_issued']); } public function test_device_heartbeat(): void { [$user, $org, $branch] = $this->setUpOrg(); $device = \App\Models\Device::create([ 'owner_ref' => $user->public_id, 'organization_id' => $org->id, 'branch_id' => $branch->id, 'name' => 'Kiosk 1', 'type' => 'kiosk', 'device_token' => 'test-device-token-123', ]); $this->postJson('/api/devices/heartbeat', [], [ 'Authorization' => 'Bearer test-device-token-123', ])->assertOk()->assertJsonPath('status', 'ok'); $this->assertSame('online', $device->fresh()->status); } }