withoutMiddleware(EnsurePlatformSession::class); $this->user = User::create([ 'public_id' => 'device-admin-001', 'name' => 'Device Admin', 'email' => 'devices@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->user->public_id, 'name' => 'Device Clinic', 'slug' => 'device-clinic', 'timezone' => 'UTC', 'settings' => [ 'onboarded' => true, 'plan' => 'pro', 'plan_expires_at' => now()->addYear()->toIso8601String(), ], ]); Member::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $this->user->public_id, 'role' => 'hospital_admin', ]); $this->branch = Branch::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main', 'is_active' => true, ]); } public function test_admin_can_register_browser_wedge_scanner(): void { $this->actingAs($this->user) ->post(route('care.devices.store'), [ 'name' => 'Front desk scanner', 'type' => 'barcode_scanner', 'connection_mode' => 'browser_wedge', 'branch_id' => $this->branch->id, ]) ->assertRedirect(); $this->assertDatabaseHas('care_devices', [ 'name' => 'Front desk scanner', 'type' => 'barcode_scanner', 'connection_mode' => 'browser_wedge', 'organization_id' => $this->organization->id, ]); } public function test_free_plan_cannot_register_agent_thermometer(): void { $this->organization->update([ 'settings' => ['onboarded' => true, 'plan' => 'free'], ]); $this->actingAs($this->user) ->from(route('care.devices.create')) ->post(route('care.devices.store'), [ 'name' => 'Ward thermometer', 'type' => 'thermometer', 'connection_mode' => 'agent', 'branch_id' => $this->branch->id, ]) ->assertRedirect(route('care.devices.create')) ->assertSessionHas('error'); $this->assertDatabaseMissing('care_devices', ['name' => 'Ward thermometer']); } public function test_device_token_heartbeat_and_vitals_provenance(): void { $devices = app(DeviceService::class); $device = Device::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'name' => 'Agent thermometer', 'type' => 'thermometer', 'connection_mode' => Device::MODE_AGENT, 'status' => Device::STATUS_OFFLINE, ]); $token = $devices->issueToken($device); $patient = Patient::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-DEV-0001', 'first_name' => 'Ama', 'last_name' => 'Mensah', ]); $visit = Visit::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'status' => Visit::STATUS_IN_PROGRESS, 'checked_in_at' => now(), ]); $consultation = Consultation::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'visit_id' => $visit->id, 'patient_id' => $patient->id, 'status' => Consultation::STATUS_DRAFT, 'started_at' => now(), ]); $this->postJson(route('api.device-agent.heartbeat'), [ 'agent_version' => '0.1.0', 'hostname' => 'test-host', ], [ 'X-Care-Device-Token' => $token, ]) ->assertOk() ->assertJsonPath('status', 'active'); $device->refresh(); $this->assertSame(Device::STATUS_ACTIVE, $device->status); $this->assertNotNull($device->last_seen_at); $this->postJson(route('api.device-agent.vitals'), [ 'consultation_uuid' => $consultation->uuid, 'vitals' => [ 'temperature' => 37.1, 'pulse' => 78, ], 'raw' => ['driver' => 'test'], ], [ 'X-Care-Device-Token' => $token, ]) ->assertCreated() ->assertJsonPath('source', 'device'); $vital = VitalSign::first(); $this->assertNotNull($vital); $this->assertSame(VitalSign::SOURCE_DEVICE, $vital->source); $this->assertSame($device->id, $vital->device_id); $this->assertSame('37.1', (string) $vital->temperature); $this->assertSame('test', $vital->raw_payload['driver'] ?? null); } public function test_invalid_device_token_is_rejected(): void { $this->postJson(route('api.device-agent.heartbeat'), [], [ 'X-Care-Device-Token' => 'not-a-real-token', ])->assertUnauthorized(); } public function test_patient_scan_lookup_exact_patient_number(): void { $patient = Patient::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-SCAN-9999', 'first_name' => 'Kofi', 'last_name' => 'Asante', ]); $this->actingAs($this->user) ->getJson(route('care.patients.scan', ['code' => 'LC-SCAN-9999'])) ->assertOk() ->assertJsonPath('patient.patient_number', 'LC-SCAN-9999') ->assertJsonPath('patient.uuid', $patient->uuid); $this->actingAs($this->user) ->getJson(route('care.patients.scan', ['code' => 'UNKNOWN-CODE'])) ->assertNotFound(); } public function test_agent_can_collect_sample_barcode(): void { $devices = app(DeviceService::class); $device = Device::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'name' => 'Lab scanner agent', 'type' => 'barcode_scanner', 'connection_mode' => Device::MODE_AGENT, 'status' => Device::STATUS_OFFLINE, ]); $token = $devices->issueToken($device); $patient = Patient::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-LAB-0001', 'first_name' => 'Efua', 'last_name' => 'Boateng', ]); $visit = Visit::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'status' => Visit::STATUS_OPEN, 'checked_in_at' => now(), ]); $type = InvestigationType::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'name' => 'FBC', 'code' => 'FBC', 'category' => 'blood', 'is_active' => true, ]); $investigation = InvestigationRequest::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'visit_id' => $visit->id, 'patient_id' => $patient->id, 'investigation_type_id' => $type->id, 'status' => InvestigationRequest::STATUS_PENDING, 'priority' => 'routine', ]); $this->postJson(route('api.device-agent.samples.collect'), [ 'investigation_uuid' => $investigation->uuid, 'sample_barcode' => 'SMP-AGENT-1', ], [ 'Authorization' => 'Bearer '.$token, ]) ->assertOk() ->assertJsonPath('sample_barcode', 'SMP-AGENT-1') ->assertJsonPath('status', InvestigationRequest::STATUS_SAMPLE_COLLECTED); } public function test_patient_label_prints(): void { $patient = Patient::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-LBL-0001', 'first_name' => 'Yaw', 'last_name' => 'Owusu', ]); $this->actingAs($this->user) ->get(route('care.patients.label', $patient)) ->assertOk() ->assertSee('LC-LBL-0001') ->assertSee('Yaw Owusu'); } }