query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('lab_request') . " ( id INTEGER PRIMARY KEY, patient_id INTEGER, patient_name TEXT, lab_type INTEGER, charge_id INTEGER, report_name TEXT, Report_Data TEXT, report_data_Impression TEXT, status INTEGER, Request_Date TEXT, reported_time TEXT, collected_time TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('charge_master') . " ( id INTEGER PRIMARY KEY, charge_name TEXT, charge_type TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('opd_prescription') . " ( id INTEGER PRIMARY KEY, p_id INTEGER, opd_id INTEGER, date_opd_visit TEXT, session_id INTEGER, p_datetime TEXT, bp TEXT, pulse TEXT, temp TEXT, spo2 TEXT, weight TEXT, height TEXT, complaints TEXT, diagnosis TEXT, Provisional_diagnosis TEXT, advice TEXT, investigation TEXT, Finding_Examinations TEXT, diagnosis_json TEXT, complaint_snomed_json TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('opd_prescrption_prescribed') . " ( id INTEGER PRIMARY KEY, prescription_id INTEGER, medicine_name TEXT, dosage_summary TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('opd_master') . " ( opd_id INTEGER PRIMARY KEY, p_id INTEGER, apointment_date TEXT, opd_book_date TEXT, doc_name TEXT, opd_status INTEGER )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('health_records') . " ( id INTEGER PRIMARY KEY, patient_id INTEGER, abha_id TEXT, hi_type TEXT, entity_type TEXT, entity_id TEXT, fhir_bundle TEXT, care_context_reference TEXT, consent_handle TEXT, push_status TEXT, created_at TEXT, updated_at TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('patient_master') . " ( id INTEGER PRIMARY KEY, p_code TEXT, p_fname TEXT, dob TEXT, age TEXT, gender INTEGER, abha_id TEXT, abha_address TEXT, mphone1 TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('ipd_master') . " ( id INTEGER PRIMARY KEY, p_id INTEGER, discharge_date TEXT, register_date TEXT, ipd_status INTEGER )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('immunization_records') . " ( id INTEGER PRIMARY KEY, patient_id INTEGER, vaccine_name TEXT, given_date TEXT, abdm_care_context_reference TEXT )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('invoice_master') . " ( id INTEGER PRIMARY KEY, invoice_code TEXT, attach_id INTEGER, attach_type INTEGER, inv_date TEXT, net_amount REAL )"); $db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('record_links') . " ( id INTEGER PRIMARY KEY AUTOINCREMENT, health_record_id INTEGER, abdm_txn_id TEXT, care_context_reference TEXT, abha_id TEXT, link_status TEXT, response_json TEXT, linked_at TEXT, created_at TEXT, updated_at TEXT )"); $db->resetDataCache(); $db->table('charge_master')->emptyTable(); $db->table('lab_request')->emptyTable(); $db->table('opd_prescription')->emptyTable(); $db->table('opd_master')->emptyTable(); $db->table('health_records')->emptyTable(); $db->table('patient_master')->emptyTable(); $db->table('ipd_master')->emptyTable(); $db->table('record_links')->emptyTable(); $db->table('immunization_records')->emptyTable(); $db->table('invoice_master')->emptyTable(); $db->table('patient_master')->insert([ 'id' => 12, 'p_code' => 'P26071000012', 'p_fname' => 'KESHAV SINGH', 'dob' => '1976-06-30', 'age' => null, 'gender' => 1, 'abha_id' => '91747451787143', 'abha_address' => 'singhkeshav301976@sbx', 'mphone1' => '7817828379', ]); $db->table('charge_master')->insert([ 'id' => 19, 'charge_name' => 'USG WHOLE ABDOMEN', ]); $db->table('lab_request')->insert([ 'id' => 31, 'patient_id' => 12, 'lab_type' => 6, 'charge_id' => 19, 'report_name' => 'USG WHOLE ABDOMEN', 'Report_Data' => 'Liver is normal. Gall bladder normal.', 'report_data_Impression' => 'Normal study.', 'status' => 1, 'Request_Date' => '2026-09-29', 'reported_time' => '2026-09-29 11:00:00', ]); $db->table('opd_prescription')->insert([ 'id' => 101, 'p_id' => 12, 'date_opd_visit' => '2026-09-28', 'session_id' => 50, 'p_datetime' => '2026-09-28 10:00:00', ]); $this->gateway = new AbdmGateway(); $this->gateway->initController( \Config\Services::request(), \Config\Services::response(), \Config\Services::logger() ); } public function testCareContextsDiscoveryWithTargetTask(): void { $reflector = new ReflectionClass($this->gateway); $method = $reflector->getMethod('findCareContextsForPatient'); $method->setAccessible(true); // Test with patient 12 and task_type='radiology_report_publish', entity_id='31' [$v3List, $fullList] = $method->invoke( $this->gateway, 12, 'P-12', 'KESHAV SINGH', 'radiology_report_publish', '31', 591 ); $this->assertNotEmpty($fullList, 'Care contexts list should not be empty for patient 12'); // Verify the primary context is the radiology report #31 $primaryCtx = $fullList[0]; $this->assertTrue($primaryCtx['is_primary'] ?? false, 'First care context should be marked as primary'); $this->assertSame('DiagnosticReportRecord', $primaryCtx['record_type']); $this->assertStringStartsWith('RAD-31-', $primaryCtx['careContextId']); $this->assertTrue($primaryCtx['is_fhir_ready']); // Verify that v3List also contains referenceNumber $this->assertSame($primaryCtx['careContextId'], $v3List[0]['referenceNumber']); // Verify secondary contexts (e.g. OPD prescription) are also discovered $hasOpd = false; foreach ($fullList as $ctx) { if ($ctx['record_type'] === 'OPConsultRecord') { $hasOpd = true; $this->assertFalse($ctx['is_primary']); } } $this->assertTrue($hasOpd, 'Patient OPD consultations should also be included in care contexts'); } public function testCareContextsDiscoveryDraftStatus(): void { $db = \Config\Database::connect(); // Insert incomplete lab request (status = 0 and empty findings) $db->table('lab_request')->insert([ 'id' => 99, 'patient_id' => 12, 'lab_type' => 1, 'charge_id' => 0, 'report_name' => 'Draft Blood Test', 'Report_Data' => '', 'report_data_Impression' => '', 'status' => 0, 'Request_Date' => '2026-09-29', ]); $reflector = new ReflectionClass($this->gateway); $method = $reflector->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3List, $fullList] = $method->invoke( $this->gateway, 12, 'P-12', 'KESHAV SINGH' ); $draftCtx = null; foreach ($fullList as $ctx) { if (str_starts_with($ctx['careContextId'], 'LAB-99-')) { $draftCtx = $ctx; break; } } $this->assertNotNull($draftCtx); $this->assertFalse($draftCtx['is_fhir_ready'], 'Incomplete lab report should not be marked FHIR ready'); } public function testHipPatientCareContextsEndpoint(): void { $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest'; $_REQUEST['patient_id'] = '12'; $_REQUEST['abha_address'] = '91747451787143'; $_REQUEST['task_type'] = 'radiology_report_publish'; $_REQUEST['entity_id'] = '31'; $_REQUEST['task_id'] = '591'; $req = service('request'); $req->setHeader('X-Requested-With', 'XMLHttpRequest'); $this->gateway->initController($req, service('response'), service('logger')); $res = $this->gateway->hipPatientCareContexts(); $body = json_decode($res->getBody(), true); $this->assertSame(1, $body['ok'] ?? 0); $this->assertNotEmpty($body['care_contexts'] ?? []); } public function testCareContextsDiscoveryWithIpdAndImmunization(): void { $db = \Config\Database::connect(); $db->table('ipd_master')->insert([ 'id' => 55, 'p_id' => 12, 'discharge_date' => '2026-09-20', 'register_date' => '2026-09-15', 'ipd_status' => 1, ]); $db->table('immunization_records')->insert([ 'id' => 77, 'patient_id' => 12, 'vaccine_name' => 'Covaxin', 'given_date' => '2026-09-18', 'abdm_care_context_reference' => 'IMM-77-20260918', ]); $reflector = new ReflectionClass($this->gateway); $method = $reflector->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3List, $fullList] = $method->invoke( $this->gateway, 12, 'P-12', 'KESHAV SINGH' ); $foundIpd = false; $foundImm = false; foreach ($fullList as $ctx) { if ($ctx['record_type'] === 'DischargeSummaryRecord') { $foundIpd = true; $this->assertStringContainsString('DISCHARGE-55-', $ctx['careContextId']); } if ($ctx['record_type'] === 'ImmunizationRecord') { $foundImm = true; $this->assertSame('IMM-77-20260918', $ctx['careContextId']); } } $this->assertTrue($foundIpd, 'IPD DischargeSummaryRecord should be discovered'); $this->assertTrue($foundImm, 'ImmunizationRecord should be discovered'); } public function testImmunizationTaskReturnsPrimaryCareContextInDiscovery(): void { $db = \Config\Database::connect(); $db->table('immunization_records')->insert([ 'id' => 11, 'patient_id' => 11, 'vaccine_name' => 'PCV', 'given_date' => '2026-10-06 01:54:00', 'abdm_care_context_reference' => 'IMM-11', ]); $reflector = new ReflectionClass($this->gateway); $method = $reflector->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3List, $fullList] = $method->invoke( $this->gateway, 11, 'P-11', 'DEVENDER SINGH', 'immunization_record_publish', '11' ); $this->assertNotEmpty($fullList); $primary = $fullList[0]; $this->assertSame('IMM-11', $primary['careContextId']); $this->assertSame('ImmunizationRecord', $primary['record_type']); $this->assertTrue($primary['is_primary']); $this->assertTrue($primary['is_fhir_ready']); $this->assertStringContainsString('PCV', $primary['display']); } public function testShareDiagnosisReportBundleFormatsRadiologyCareContext(): void { $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest'; $_POST['lab_req_id'] = '31'; $_POST['patient_id'] = '12'; $_POST['abha_id'] = 'singhkeshav301976@sbx'; $_POST['care_context_reference'] = 'RAD-31-20260929'; $req = service('request'); $req->setHeader('X-Requested-With', 'XMLHttpRequest'); $this->gateway->initController($req, service('response'), service('logger')); $mockConnector = $this->createMock(\App\Libraries\Abdm\AbdmConnectorInterface::class); $mockConnector->method('pushRecord') ->willReturnCallback(function (array $data) { return [ 'ok' => 1, 'http_code' => 201, 'status' => 'queued', 'queue_id' => $data['care_context_reference'], 'record_id' => 999, ]; }); $refGateway = new ReflectionClass($this->gateway); $prop = $refGateway->getProperty('connector'); $prop->setAccessible(true); $prop->setValue($this->gateway, $mockConnector); $res = $this->gateway->shareDiagnosisReportBundle(); $body = json_decode($res->getBody(), true); $this->assertSame(1, $body['ok'] ?? 0); $this->assertSame('RAD-31-20260929', $body['care_context_reference'] ?? ''); } public function testDiscoversOpdMasterVisitWhenPrescriptionNotCreated(): void { $db = \Config\Database::connect(); $db->table('opd_master')->insert([ 'opd_id' => 77, 'p_id' => 12, 'apointment_date' => '2026-09-29 14:30:00', 'opd_book_date' => '2026-09-29 14:00:00', 'doc_name' => 'NIDHI PANDEY', ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3, $full] = $method->invoke($this->gateway, 12, 'P26071000012', 'KESHAV SINGH'); $refs = array_column($v3, 'referenceNumber'); $this->assertContains('OPD-12-S77-20260929', $refs); } public function testDiscoversInvoiceCareContextWhenInvoiceTaskRequested(): void { $db = \Config\Database::connect(); $db->table('invoice_master')->emptyTable(); $db->table('invoice_master')->insert([ 'id' => 88, 'invoice_code' => 'INV-88', 'attach_id' => 12, 'attach_type' => 0, 'inv_date' => '2026-09-30', 'net_amount' => 450.00, ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3, $full] = $method->invoke( $this->gateway, 12, 'P26071000012', 'KESHAV SINGH', 'invoice_publish', '88', 0, 'charges_invoice' ); $refs = array_column($v3, 'referenceNumber'); $this->assertContains('INVOICE-CHG-88-2026-09-30', $refs); $invoiceCtx = null; foreach ($full as $ctx) { if ($ctx['careContextId'] === 'INVOICE-CHG-88-2026-09-30') { $invoiceCtx = $ctx; break; } } $this->assertNotNull($invoiceCtx); $this->assertSame('InvoiceRecord', $invoiceCtx['record_type']); $this->assertTrue($invoiceCtx['is_primary']); $this->assertTrue($invoiceCtx['is_fhir_ready']); } public function testDiscoversPrescriptionAndWellnessCareContextsWhenPresent(): void { $db = \Config\Database::connect(); $db->table('opd_prescription')->insert([ 'id' => 901, 'p_id' => 15350, 'opd_id' => 15350, 'session_id' => 901, 'date_opd_visit' => '2026-10-06', 'bp' => '120/80', 'pulse' => '72', 'temp' => '98.4', 'spo2' => '98', 'weight' => '70', 'height' => '175', ]); $db->table('opd_prescrption_prescribed')->insert([ 'id' => 101, 'prescription_id' => 901, 'medicine_name' => 'CAP PANTOP DSR', 'dosage_summary' => '1 Tab | OD | 5 days', ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3, $full] = $method->invoke($this->gateway, 15350, 'P-15350', 'DEVENDER SINGH'); $refs = array_column($v3, 'referenceNumber'); $this->assertContains('OPD-15350-S901-20261006', $refs); $this->assertContains('PRESC-15350-S901-20261006', $refs); $this->assertContains('WELLNESS-901-20261006', $refs); $types = array_column($full, 'record_type'); $this->assertContains('OPConsultRecord', $types); $this->assertContains('PrescriptionRecord', $types); $this->assertContains('WellnessRecord', $types); } public function testDiscoversWellnessTaskBoardCareContext(): void { $db = \Config\Database::connect(); $db->table('opd_prescription')->insert([ 'id' => 33723, 'p_id' => 15350, 'opd_id' => 15350, 'session_id' => 33723, 'date_opd_visit' => '2026-10-06', 'bp' => '130/85', 'pulse' => '75', ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3, $full] = $method->invoke( $this->gateway, 15350, 'P-15350', 'DEVENDER SINGH', 'wellness_record_publish', '33723' ); $refs = array_column($v3, 'referenceNumber'); $this->assertContains('WELLNESS-33723-20261006', $refs); $primary = $full[0]; $this->assertSame('WELLNESS-33723-20261006', $primary['careContextId']); $this->assertSame('WellnessRecord', $primary['record_type']); $this->assertTrue($primary['is_primary']); } public function testUnfilledOpdConsultShowsSingleDraftRecordWithDoctorName(): void { $db = \Config\Database::connect(); $db->table('opd_master')->insert([ 'opd_id' => 33793, 'p_id' => 15353, 'apointment_date' => '2026-10-06 14:00:00', 'opd_book_date' => '2026-10-06 14:00:00', 'doc_name' => 'Jayashri Joshi', 'opd_status' => 1, // Booked / waiting (not done) ]); $db->table('opd_prescription')->insert([ 'id' => 33727, 'opd_id' => 33793, 'p_id' => 15353, 'session_id' => 0, 'date_opd_visit' => '2026-10-06', 'complaints' => '', 'diagnosis' => '', ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3, $full] = $method->invoke($this->gateway, 15353, 'P26081015353', 'DHAIRYA SINGH BISHT'); // Verify only ONE OPD consult record is returned (no duplicate!) $opdConsults = array_values(array_filter($full, fn ($c) => ($c['record_type'] ?? '') === 'OPConsultRecord')); $this->assertCount(1, $opdConsults, 'Should return exactly 1 consolidated OPD consultation record, not duplicates'); $consult = $opdConsults[0]; $this->assertStringContainsString('Dr. Jayashri Joshi', $consult['display'], 'Should include the doctor name'); $this->assertFalse($consult['is_fhir_ready'], 'Unfilled booking should NOT be marked FHIR ready'); } public function testCompletedOpdConsultShowsFhirReady(): void { $db = \Config\Database::connect(); $db->table('opd_master')->insert([ 'opd_id' => 33794, 'p_id' => 15353, 'apointment_date' => '2026-10-06 15:00:00', 'opd_book_date' => '2026-10-06 15:00:00', 'doc_name' => 'Jayashri Joshi', 'opd_status' => 2, // Visit Done ]); $db->table('opd_prescription')->insert([ 'id' => 33728, 'opd_id' => 33794, 'p_id' => 15353, 'session_id' => 0, 'date_opd_visit' => '2026-10-06', 'complaints' => 'Fever and Cough', 'diagnosis' => 'Viral Pharyngitis', ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); [$v3, $full] = $method->invoke($this->gateway, 15353, 'P26081015353', 'DHAIRYA SINGH BISHT'); $opdConsults = array_values(array_filter($full, fn ($c) => ($c['record_type'] ?? '') === 'OPConsultRecord' && str_contains($c['careContextId'], '33728'))); $this->assertCount(1, $opdConsults); $this->assertTrue($opdConsults[0]['is_fhir_ready'], 'Completed consultation with findings must be marked FHIR ready'); } public function testCareContextsPartitioningByAbhaAddress(): void { $db = \Config\Database::connect(); // Setup patient 999 $db->table('patient_master')->insert([ 'id' => 999, 'p_code' => 'P-999', 'p_fname' => 'PARTITION TEST', 'dob' => '1985-01-01', 'gender' => 1, 'abha_id' => '91999999999999', 'abha_address' => 'newuser@sbx', 'mphone1' => '9999999999', ]); // Insert 2 prescriptions for patient 999 $db->table('opd_prescription')->insert([ 'id' => 701, 'opd_id' => 701, 'p_id' => 999, 'session_id' => 701, 'date_opd_visit' => '2026-08-01', 'complaints' => 'Old visit complaints', ]); $db->table('opd_prescription')->insert([ 'id' => 702, 'opd_id' => 702, 'p_id' => 999, 'session_id' => 702, 'date_opd_visit' => '2026-10-22', 'complaints' => 'New visit complaints', ]); // Link old prescription #701 to 'olduser@sbx' $oldRef = 'OPD-999-S701-20260801'; $db->table('record_links')->insert([ 'care_context_reference' => $oldRef, 'abha_id' => 'olduser@sbx', 'link_status' => 'linked', 'created_at' => '2026-08-01 10:00:00', ]); // Link new prescription #702 to 'newuser@sbx' $newRef = 'OPD-999-S702-20261022'; $db->table('record_links')->insert([ 'care_context_reference' => $newRef, 'abha_id' => 'newuser@sbx', 'link_status' => 'linked', 'created_at' => '2026-10-22 10:00:00', ]); $refGateway = new ReflectionClass($this->gateway); $method = $refGateway->getMethod('findCareContextsForPatient'); $method->setAccessible(true); // Discovery for 'newuser@sbx': MUST contain $newRef and MUST NOT contain $oldRef [$v3New, $fullNew] = $method->invoke($this->gateway, 999, 'P-999', 'PARTITION TEST', '', '', 0, '', false, 'newuser@sbx'); $newRefs = array_column($v3New, 'referenceNumber'); $this->assertContains($newRef, $newRefs, 'New ABHA address should discover its own linked record'); $this->assertNotContains($oldRef, $newRefs, 'New ABHA address MUST NOT discover records linked to olduser@sbx'); // Discovery for 'olduser@sbx': MUST contain $oldRef and MUST NOT contain $newRef [$v3Old, $fullOld] = $method->invoke($this->gateway, 999, 'P-999', 'PARTITION TEST', '', '', 0, '', false, 'olduser@sbx'); $oldRefs = array_column($v3Old, 'referenceNumber'); $this->assertContains($oldRef, $oldRefs, 'Old ABHA address should discover its own linked record'); $this->assertNotContains($newRef, $oldRefs, 'Old ABHA address MUST NOT discover records linked to newuser@sbx'); } }