Files
ladill-care/tests/Feature/CareQueueBridgeTest.php
T
isaaccladandCursor dd4bc493b4
Deploy Ladill Care / deploy (push) Failing after 1m13s
Hard-delete Care remote Queue adapter (Phase 5).
Care Queue Engine is the only path; remove QueueClient, driver config, and remote HTTP tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-18 11:16:41 +00:00

369 lines
13 KiB
PHP

<?php
namespace Tests\Feature;
use App\Http\Middleware\EnsurePlatformSession;
use App\Models\Appointment;
use App\Models\Branch;
use App\Models\CareQueueTicket;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\User;
use App\Services\Care\AppointmentService;
use App\Services\Care\CareQueueContexts;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class CareQueueBridgeTest extends TestCase
{
use RefreshDatabase;
protected User $owner;
protected Organization $organization;
protected Branch $branch;
protected Patient $patient;
protected function setUp(): void
{
parent::setUp();
$this->withoutMiddleware(EnsurePlatformSession::class);
$this->owner = User::create([
'public_id' => 'bridge-owner',
'name' => 'Owner',
'email' => 'bridge@example.com',
]);
$this->organization = Organization::create([
'owner_ref' => $this->owner->public_id,
'name' => 'Bridge Clinic',
'slug' => 'bridge-clinic',
'settings' => [
'onboarded' => true,
'facility_type' => 'clinic',
'plan' => 'pro',
'plan_expires_at' => now()->addMonth()->toIso8601String(),
'queue_integration_enabled' => true,
],
]);
Member::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'user_ref' => $this->owner->public_id,
'role' => 'hospital_admin',
]);
$this->branch = Branch::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'name' => 'Main',
'is_active' => true,
]);
$this->patient = Patient::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_number' => 'P-100',
'first_name' => 'Ada',
'last_name' => 'Lovelace',
'gender' => 'female',
'date_of_birth' => '1990-01-01',
]);
}
public function test_check_in_issues_consultation_ticket_when_integration_on(): void
{
Http::fake();
$practitioner = \App\Models\Practitioner::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'name' => 'Dr. Bridge',
'room' => 'Room 1',
'is_active' => true,
]);
$appointment = Appointment::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_id' => $this->patient->id,
'practitioner_id' => $practitioner->id,
'type' => Appointment::TYPE_SCHEDULED,
'status' => Appointment::STATUS_SCHEDULED,
'scheduled_at' => now()->addHour(),
]);
$result = app(AppointmentService::class)->checkIn($appointment, $this->owner->public_id, $this->owner->public_id);
$this->assertNotNull($result->queue_ticket_uuid);
$this->assertSame('waiting', $result->queue_ticket_status);
$this->assertStringStartsWith('C', (string) $result->queue_ticket_number);
$this->assertSame('Room 1', $result->queue_destination);
$this->assertSame('Dr. Bridge', $result->queue_staff_display_name);
$this->assertDatabaseHas('care_queue_tickets', [
'uuid' => $result->queue_ticket_uuid,
'ticket_number' => $result->queue_ticket_number,
'status' => CareQueueTicket::STATUS_WAITING,
'care_entity' => 'appointment',
'care_entity_id' => $result->id,
]);
Http::assertNothingSent();
}
public function test_check_in_skips_ticket_when_integration_off(): void
{
Http::fake();
$settings = $this->organization->settings;
$settings['queue_integration_enabled'] = false;
$this->organization->update(['settings' => $settings]);
$appointment = Appointment::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_id' => $this->patient->id,
'type' => Appointment::TYPE_SCHEDULED,
'status' => Appointment::STATUS_SCHEDULED,
'scheduled_at' => now()->addHour(),
]);
$result = app(AppointmentService::class)->checkIn(
$appointment->fresh(['organization', 'patient']),
$this->owner->public_id,
$this->owner->public_id,
);
$this->assertNull($result->queue_ticket_uuid);
Http::assertNothingSent();
}
public function test_waiting_list_shows_ticket_number_when_integration_on(): void
{
Appointment::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_id' => $this->patient->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_WAITING,
'scheduled_at' => now(),
'waiting_at' => now(),
'queue_position' => 1,
'queue_ticket_uuid' => 'dddddddd-dddd-dddd-dddd-dddddddddddd',
'queue_ticket_number' => 'C042',
'queue_ticket_status' => 'waiting',
]);
$this->actingAs($this->owner)
->get(route('care.queue.index'))
->assertOk()
->assertSee('C042')
->assertSee('Call next')
->assertSee('Serve');
}
public function test_call_next_backfills_missing_ticket_then_calls(): void
{
Http::fake();
$practitioner = \App\Models\Practitioner::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'name' => 'Dr. Backfill',
'room' => 'Room 3',
'is_active' => true,
]);
Appointment::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_id' => $this->patient->id,
'practitioner_id' => $practitioner->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_WAITING,
'scheduled_at' => now(),
'waiting_at' => now(),
'queue_position' => 1,
]);
$this->actingAs($this->owner)
->from(route('care.queue.index'))
->post(route('care.queue.call-next'), [
'branch_id' => $this->branch->id,
'practitioner_id' => $practitioner->id,
])
->assertRedirect(route('care.queue.index'))
->assertSessionHas('success');
$appointment = Appointment::query()->where('patient_id', $this->patient->id)->first();
$this->assertNotNull($appointment?->queue_ticket_uuid);
$this->assertSame('called', $appointment->queue_ticket_status);
$this->assertDatabaseHas('care_queue_tickets', [
'uuid' => $appointment->queue_ticket_uuid,
'status' => CareQueueTicket::STATUS_CALLED,
]);
Http::assertNothingSent();
}
public function test_call_next_empty_queue_flashes_info_not_error(): void
{
Http::fake();
$this->actingAs($this->owner)
->from(route('care.queue.index'))
->post(route('care.queue.call-next'), ['branch_id' => $this->branch->id])
->assertRedirect(route('care.queue.index'))
->assertSessionHas('info', 'No patients waiting at your consultation service point.')
->assertSessionMissing('error');
Http::assertNothingSent();
}
public function test_recall_reannounces_called_ticket_from_patient_queue(): void
{
Http::fake();
$practitioner = \App\Models\Practitioner::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'name' => 'Dr. Recall',
'room' => 'Room 2',
'is_active' => true,
]);
$appointment = Appointment::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_id' => $this->patient->id,
'practitioner_id' => $practitioner->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_SCHEDULED,
'scheduled_at' => now(),
]);
$checkedIn = app(AppointmentService::class)->checkIn(
$appointment->fresh(['organization', 'patient', 'practitioner']),
$this->owner->public_id,
$this->owner->public_id,
);
$bridge = app(\App\Services\Care\CareQueueBridge::class);
$called = $bridge->callNext(
$this->organization->fresh(),
CareQueueContexts::CONSULTATION,
(int) $this->branch->id,
null,
$practitioner->id,
);
$this->assertNotNull($called['ticket']);
$appointment = $checkedIn->fresh();
$this->assertSame('called', $appointment->queue_ticket_status);
$this->actingAs($this->owner)
->from(route('care.queue.index'))
->post(route('care.queue.recall', $appointment))
->assertRedirect(route('care.queue.index'))
->assertSessionHas('success');
$this->assertSame('called', $appointment->fresh()->queue_ticket_status);
$this->actingAs($this->owner)
->get(route('care.queue.index', ['branch_id' => $this->branch->id]))
->assertOk()
->assertSee('Call again')
->assertSee($appointment->queue_ticket_number);
Http::assertNothingSent();
}
public function test_patient_queue_sync_issues_specialty_tickets(): void
{
Http::fake();
$department = \App\Models\Department::create([
'owner_ref' => $this->owner->public_id,
'branch_id' => $this->branch->id,
'name' => 'Dentistry',
'type' => 'dental',
'is_active' => true,
]);
$practitioner = \App\Models\Practitioner::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'department_id' => $department->id,
'name' => 'Dentistry Clinic',
'room' => 'Dental Bay 1',
'is_active' => true,
]);
$settings = $this->organization->settings;
$settings['specialty_modules'] = ['dentistry' => true];
$settings['specialty_module_provisioning'] = [
'dentistry' => [
'active' => true,
'department_ids' => [$department->id],
'queues' => [[
'module' => 'dentistry',
'branch_id' => $this->branch->id,
'branch_name' => 'Main',
'name' => 'Dentistry',
'prefix' => 'DEN',
'active' => true,
'synced' => false,
]],
],
];
$this->organization->update(['settings' => $settings]);
Appointment::create([
'owner_ref' => $this->owner->public_id,
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'patient_id' => $this->patient->id,
'practitioner_id' => $practitioner->id,
'department_id' => $department->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_WAITING,
'scheduled_at' => now(),
'waiting_at' => now(),
'queue_position' => 1,
'reason' => 'Toothache',
]);
$this->actingAs($this->owner)
->get(route('care.queue.index', ['branch_id' => $this->branch->id]))
->assertOk()
->assertSee('Toothache');
$appointment = Appointment::query()->where('patient_id', $this->patient->id)->first();
$this->assertNotNull($appointment?->queue_ticket_uuid);
$this->assertStringStartsWith('DEN', (string) $appointment->queue_ticket_number);
$this->assertDatabaseHas('care_service_queues', [
'organization_id' => $this->organization->id,
'branch_id' => $this->branch->id,
'context' => 'dentistry',
]);
$this->assertDatabaseHas('care_queue_tickets', [
'uuid' => $appointment->queue_ticket_uuid,
'ticket_number' => $appointment->queue_ticket_number,
'status' => CareQueueTicket::STATUS_WAITING,
]);
Http::assertNothingSent();
}
}