withoutMiddleware(EnsurePlatformSession::class); $this->user = User::create([ 'public_id' => 'test-user-001', 'name' => 'Test User', 'email' => 'test@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->user->public_id, 'name' => 'Test Clinic', 'slug' => 'test-clinic', 'timezone' => 'UTC', 'settings' => ['onboarded' => true], ]); Member::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $this->user->public_id, 'role' => 'receptionist', ]); $this->branch = Branch::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main Branch', 'is_active' => true, ]); Department::create([ 'owner_ref' => $this->user->public_id, 'branch_id' => $this->branch->id, 'name' => 'General Outpatient', 'type' => 'outpatient', 'is_active' => true, ]); $this->patient = Patient::create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-2026-00001', 'first_name' => 'Ama', 'last_name' => 'Mensah', ]); $this->practitioner = Practitioner::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'name' => 'Dr. Kwame Asante', 'specialty' => 'General Practice', 'is_active' => true, ]); } public function test_appointments_index_loads(): void { $this->actingAs($this->user) ->get(route('care.appointments.index')) ->assertOk() ->assertSee('Appointments'); } public function test_can_book_appointment(): void { $this->actingAs($this->user) ->post(route('care.appointments.store'), [ 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $this->practitioner->id, 'scheduled_at' => now()->addDay()->format('Y-m-d\TH:i'), 'reason' => 'Follow-up', ]) ->assertRedirect(); $appointment = Appointment::first(); $this->assertNotNull($appointment); $this->assertSame(Appointment::STATUS_SCHEDULED, $appointment->status); $this->assertDatabaseHas('care_audit_logs', ['action' => 'appointment.created']); } public function test_full_appointment_workflow(): void { $appointment = Appointment::create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $this->practitioner->id, 'type' => Appointment::TYPE_SCHEDULED, 'status' => Appointment::STATUS_SCHEDULED, 'scheduled_at' => now()->addHour(), 'reason' => 'Check-up', ]); $this->actingAs($this->user) ->post(route('care.appointments.check-in', $appointment)) ->assertRedirect(route('care.queue.index')); $appointment->refresh(); $this->assertSame(Appointment::STATUS_WAITING, $appointment->status); $this->assertNotNull($appointment->visit_id); $this->assertDatabaseHas('care_visits', ['patient_id' => $this->patient->id]); Member::where('user_ref', $this->user->public_id)->update(['role' => 'doctor']); $this->actingAs($this->user) ->post(route('care.queue.start', $appointment)) ->assertRedirect(); $appointment->refresh(); $this->assertSame(Appointment::STATUS_IN_CONSULTATION, $appointment->status); $consultation = Consultation::first(); $this->assertNotNull($consultation); $this->actingAs($this->user) ->put(route('care.consultations.update', $consultation), [ 'symptoms' => 'Headache and fever', 'clinical_notes' => 'Patient appears stable.', 'vitals' => [ 'bp_systolic' => 120, 'bp_diastolic' => 80, 'pulse' => 72, 'temperature' => 37.2, ], 'diagnoses' => [ ['code' => 'R51', 'description' => 'Headache', 'is_primary' => true], ], ]) ->assertRedirect(); $this->assertDatabaseHas('care_vital_signs', ['bp_systolic' => 120]); $this->assertDatabaseHas('care_diagnoses', ['description' => 'Headache']); $this->actingAs($this->user) ->post(route('care.consultations.complete', $consultation)) ->assertRedirect(route('care.queue.index')); $appointment->refresh(); $consultation->refresh(); $this->assertSame(Appointment::STATUS_COMPLETED, $appointment->status); $this->assertSame(Consultation::STATUS_COMPLETED, $consultation->status); $this->assertSame(Visit::STATUS_COMPLETED, $appointment->visit->status); } public function test_walk_in_registers_to_queue(): void { $this->actingAs($this->user) ->post(route('care.appointments.walk-in.store'), [ 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $this->practitioner->id, 'reason' => 'Urgent visit', ]) ->assertRedirect(route('care.queue.index')); $appointment = Appointment::first(); $this->assertSame(Appointment::TYPE_WALK_IN, $appointment->type); $this->assertSame(Appointment::STATUS_WAITING, $appointment->status); $this->assertNotNull($appointment->queue_position); } public function test_queue_page_loads(): void { $this->actingAs($this->user) ->get(route('care.queue.index', ['branch_id' => $this->branch->id])) ->assertOk() ->assertSee('Patient queue'); } public function test_queue_repairs_duplicate_positions(): void { $second = Patient::create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-2026-00002', 'first_name' => 'Kofi', 'last_name' => 'Mensah', ]); foreach ([$this->patient, $second] as $index => $patient) { Appointment::create([ 'uuid' => (string) \Illuminate\Support\Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'practitioner_id' => $this->practitioner->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now()->subMinutes(10 - $index), 'waiting_at' => now()->subMinutes(10 - $index), 'queue_position' => 1, 'reason' => $index === 0 ? 'Toothache' : 'Blurry vision', 'created_by' => $this->user->public_id, ]); } $this->actingAs($this->user) ->get(route('care.queue.index', ['branch_id' => $this->branch->id])) ->assertOk() ->assertSee('Toothache') ->assertSee('Blurry vision'); $positions = Appointment::query() ->where('branch_id', $this->branch->id) ->where('status', Appointment::STATUS_WAITING) ->orderBy('waiting_at') ->pluck('queue_position') ->all(); $this->assertSame([1, 2], array_map('intval', $positions)); } public function test_api_can_book_and_check_in(): void { Sanctum::actingAs($this->user); $response = $this->postJson('/api/v1/appointments', [ 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'scheduled_at' => now()->addDays(2)->toIso8601String(), 'reason' => 'API booking', ]); $response->assertCreated(); $uuid = $response->json('uuid'); $this->postJson("/api/v1/appointments/{$uuid}/check-in") ->assertOk() ->assertJsonPath('status', Appointment::STATUS_WAITING); } }