Files
ladill-care/tests/Feature/CareQueueBridgeTest.php
T
isaaccladandCursor 015a4cc7fe
Deploy Ladill Care / deploy (push) Successful in 1m45s
Wire Care lists into Ladill Queue workflows instead of reception panels.
When Queue integration is on, role pages issue tickets into their own
department queues and drive Call next → Serve → Complete on the native
lists. Integration off leaves existing UI unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 16:27:48 +00:00

194 lines
6.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Http\Middleware\EnsurePlatformSession;
use App\Models\Appointment;
use App\Models\Branch;
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);
config([
'care.queue.api_url' => 'https://queue.test/api/v1',
'care.queue.api_key' => 'care-test-key',
]);
$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,
]);
$settings = $this->organization->settings;
$settings['department_queue_provisioning'] = [
CareQueueContexts::CONSULTATION => [
'queues' => [[
'branch_id' => $this->branch->id,
'branch_name' => 'Main',
'name' => 'Consultation',
'prefix' => 'C',
'active' => true,
'synced' => true,
'queue_uuid' => 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'counter_uuid' => 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'queue_external_key' => CareQueueContexts::queueExternalKey(CareQueueContexts::CONSULTATION, $this->branch->id),
'counter_external_key' => CareQueueContexts::counterExternalKey(CareQueueContexts::CONSULTATION, $this->branch->id),
]],
],
];
$this->organization->update(['settings' => $settings]);
$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(function (\Illuminate\Http\Client\Request $request) {
if (str_contains($request->url(), '/tickets') && $request->method() === 'POST') {
return Http::response([
'data' => [
'uuid' => 'cccccccc-cccc-cccc-cccc-cccccccccccc',
'ticket_number' => 'C001',
'status' => 'waiting',
],
], 201);
}
return Http::response(['message' => 'unexpected '.$request->url()], 500);
});
$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, $this->owner->public_id, $this->owner->public_id);
$this->assertSame('C001', $result->queue_ticket_number);
$this->assertSame('waiting', $result->queue_ticket_status);
$this->assertSame('cccccccc-cccc-cccc-cccc-cccccccccccc', $result->queue_ticket_uuid);
Http::assertSent(fn ($request) => str_contains($request->url(), '/tickets')
&& ($request['service_queue_id'] ?? null) === 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
&& ($request['customer_name'] ?? null) === 'Ada Lovelace');
}
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');
}
}