/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ markTestIncomplete( 'Multi-doctor clinic data access not implemented yet - TDD RED phase' ); // ARRANGE: Setup multi-doctor clinic scenario $clinic1_id = $this->create_test_clinic(); $clinic2_id = $this->create_test_clinic(); // Create additional doctors $doctor2_id = $this->factory->user->create( array( 'user_login' => 'doctor2', 'user_email' => 'doctor2@clinic.com', 'role' => 'doctor', ) ); $doctor3_id = $this->factory->user->create( array( 'user_login' => 'doctor3', 'user_email' => 'doctor3@clinic.com', 'role' => 'doctor', ) ); global $wpdb; // Map doctors to clinics $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $this->doctor_user, 'clinic_id' => $clinic1_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $doctor2_id, 'clinic_id' => $clinic1_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $doctor3_id, 'clinic_id' => $clinic2_id ) ); // Create patients in different clinics $patient1_id = $this->factory->user->create( array( 'role' => 'patient' ) ); $patient2_id = $this->factory->user->create( array( 'role' => 'patient' ) ); $patient3_id = $this->factory->user->create( array( 'role' => 'patient' ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $patient1_id, 'clinic_id' => $clinic1_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $patient2_id, 'clinic_id' => $clinic1_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $patient3_id, 'clinic_id' => $clinic2_id ) ); // STEP 1: Doctor 1 creates appointment with Patient 1 $appointment1_id = $this->create_test_appointment( $clinic1_id, $this->doctor_user, $patient1_id ); // Doctor 1 creates encounter $encounter1_response = $this->make_request( '/wp-json/care/v1/encounters', 'POST', array( 'appointment_id' => $appointment1_id, 'description' => 'First encounter by Doctor 1', 'diagnosis' => 'Common cold', ), $this->doctor_user ); $this->assertRestResponse( $encounter1_response, 201 ); $encounter1_id = $encounter1_response->get_data()['id']; // STEP 2: Doctor 2 should be able to access same patient data (same clinic) $patient_access_response = $this->make_request( "/wp-json/care/v1/patients/{$patient1_id}", 'GET', array(), $doctor2_id ); $this->assertRestResponse( $patient_access_response, 200 ); $patient_data = $patient_access_response->get_data(); $this->assertEquals( $patient1_id, $patient_data['id'] ); $this->assertEquals( $clinic1_id, $patient_data['clinic_id'] ); // STEP 3: Doctor 2 should see Doctor 1's encounter for same patient $encounters_response = $this->make_request( "/wp-json/care/v1/patients/{$patient1_id}/encounters", 'GET', array(), $doctor2_id ); $this->assertRestResponse( $encounters_response, 200 ); $encounters = $encounters_response->get_data(); $this->assertCount( 1, $encounters ); $this->assertEquals( $encounter1_id, $encounters[0]['id'] ); $this->assertEquals( $this->doctor_user, $encounters[0]['doctor_id'] ); // STEP 4: Doctor 2 can add notes to the encounter $update_response = $this->make_request( "/wp-json/care/v1/encounters/{$encounter1_id}", 'PUT', array( 'description' => 'First encounter by Doctor 1. Additional notes by Doctor 2: Patient responded well to treatment.', ), $doctor2_id ); $this->assertRestResponse( $update_response, 200 ); // STEP 5: Doctor 3 (different clinic) should NOT access Patient 1 $cross_clinic_response = $this->make_request( "/wp-json/care/v1/patients/{$patient1_id}", 'GET', array(), $doctor3_id ); $this->assertRestResponse( $cross_clinic_response, 403 ); $error_data = $cross_clinic_response->get_data(); $this->assertEquals( 'clinic_access_denied', $error_data['code'] ); // STEP 6: Doctor 3 should NOT see encounters from different clinic $cross_encounters_response = $this->make_request( "/wp-json/care/v1/encounters", 'GET', array( 'patient_id' => $patient1_id ), $doctor3_id ); $this->assertRestResponse( $cross_encounters_response, 403 ); // STEP 7: Verify clinic-filtered patient lists $clinic1_patients_response = $this->make_request( '/wp-json/care/v1/patients', 'GET', array(), $this->doctor_user ); $this->assertRestResponse( $clinic1_patients_response, 200 ); $clinic1_patients = $clinic1_patients_response->get_data()['data']; $clinic1_patient_ids = wp_list_pluck( $clinic1_patients, 'id' ); // Should include patients from clinic 1 only $this->assertContains( $patient1_id, $clinic1_patient_ids ); $this->assertContains( $patient2_id, $clinic1_patient_ids ); $this->assertNotContains( $patient3_id, $clinic1_patient_ids ); // STEP 8: Verify appointment scheduling across doctors in same clinic $appointment2_id = $this->create_test_appointment( $clinic1_id, $doctor2_id, $patient2_id ); // Doctor 1 should see Doctor 2's appointments in clinic view $clinic_appointments_response = $this->make_request( '/wp-json/care/v1/appointments', 'GET', array( 'clinic_id' => $clinic1_id ), $this->doctor_user ); $this->assertRestResponse( $clinic_appointments_response, 200 ); $appointments = $clinic_appointments_response->get_data()['data']; $appointment_ids = wp_list_pluck( $appointments, 'id' ); $this->assertContains( $appointment1_id, $appointment_ids ); $this->assertContains( $appointment2_id, $appointment_ids ); } /** * Test clinic admin permissions and data access. * * @test */ public function test_clinic_admin_data_access() { // This test will fail initially as clinic admin roles aren't implemented $this->markTestIncomplete( 'Clinic admin permissions not implemented yet - TDD RED phase' ); // ARRANGE: Setup clinic with admin $clinic_id = $this->create_test_clinic(); $clinic_admin_id = $this->factory->user->create( array( 'user_login' => 'clinic_admin', 'user_email' => 'admin@clinic.com', 'role' => 'administrator', ) ); global $wpdb; // Update clinic to have admin $wpdb->update( $wpdb->prefix . 'kc_clinics', array( 'clinic_admin_id' => $clinic_admin_id ), array( 'id' => $clinic_id ) ); // Map doctor and patients to clinic $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $this->doctor_user, 'clinic_id' => $clinic_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $this->patient_user, 'clinic_id' => $clinic_id ) ); // Create appointment and encounter $appointment_id = $this->create_test_appointment( $clinic_id, $this->doctor_user, $this->patient_user ); $encounter_response = $this->make_request( '/wp-json/care/v1/encounters', 'POST', array( 'appointment_id' => $appointment_id, 'description' => 'Test encounter for admin access', ), $this->doctor_user ); $encounter_id = $encounter_response->get_data()['id']; // ACT & ASSERT: Clinic admin should have full access to clinic data // Access patient data $patient_response = $this->make_request( "/wp-json/care/v1/patients/{$this->patient_user}", 'GET', array(), $clinic_admin_id ); $this->assertRestResponse( $patient_response, 200 ); // Access encounter data $encounter_response = $this->make_request( "/wp-json/care/v1/encounters/{$encounter_id}", 'GET', array(), $clinic_admin_id ); $this->assertRestResponse( $encounter_response, 200 ); // View clinic statistics $stats_response = $this->make_request( "/wp-json/care/v1/clinics/{$clinic_id}/statistics", 'GET', array(), $clinic_admin_id ); $this->assertRestResponse( $stats_response, 200 ); $stats = $stats_response->get_data(); $this->assertArrayHasKey( 'total_patients', $stats ); $this->assertArrayHasKey( 'total_appointments', $stats ); $this->assertArrayHasKey( 'total_encounters', $stats ); } /** * Test data access auditing and logging. * * @test */ public function test_clinic_data_access_auditing() { // This test will fail initially as auditing isn't implemented $this->markTestIncomplete( 'Data access auditing not implemented yet - TDD RED phase' ); // ARRANGE: Setup scenario with different doctors $clinic_id = $this->create_test_clinic(); $doctor2_id = $this->factory->user->create( array( 'role' => 'doctor' ) ); global $wpdb; $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $this->doctor_user, 'clinic_id' => $clinic_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $doctor2_id, 'clinic_id' => $clinic_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $this->patient_user, 'clinic_id' => $clinic_id ) ); // Track audit log entries $audit_entries = array(); add_action( 'kivicare_api_audit_log', function( $action, $resource_type, $resource_id, $user_id ) use ( &$audit_entries ) { $audit_entries[] = compact( 'action', 'resource_type', 'resource_id', 'user_id' ); }, 10, 4 ); // ACT: Multiple data access operations $this->make_request( "/wp-json/care/v1/patients/{$this->patient_user}", 'GET', array(), $this->doctor_user ); $this->make_request( "/wp-json/care/v1/patients/{$this->patient_user}", 'GET', array(), $doctor2_id ); $this->make_request( "/wp-json/care/v1/patients/{$this->patient_user}", 'PUT', array( 'phone' => '+351999888777' ), $this->doctor_user ); // ASSERT: Audit entries were created $this->assertCount( 3, $audit_entries ); $this->assertEquals( 'read', $audit_entries[0]['action'] ); $this->assertEquals( 'patient', $audit_entries[0]['resource_type'] ); $this->assertEquals( $this->patient_user, $audit_entries[0]['resource_id'] ); $this->assertEquals( $this->doctor_user, $audit_entries[0]['user_id'] ); $this->assertEquals( 'update', $audit_entries[2]['action'] ); } /** * Test clinic data isolation and security. * * @test */ public function test_clinic_data_isolation_security() { // This test will fail initially as security isolation isn't implemented $this->markTestIncomplete( 'Clinic data isolation security not implemented yet - TDD RED phase' ); // ARRANGE: Two separate clinics with sensitive data $clinic1_id = $this->create_test_clinic(); $clinic2_id = $this->create_test_clinic(); $doctor_clinic1 = $this->factory->user->create( array( 'role' => 'doctor' ) ); $doctor_clinic2 = $this->factory->user->create( array( 'role' => 'doctor' ) ); $patient_clinic1 = $this->factory->user->create( array( 'role' => 'patient' ) ); $patient_clinic2 = $this->factory->user->create( array( 'role' => 'patient' ) ); global $wpdb; // Map to respective clinics $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $doctor_clinic1, 'clinic_id' => $clinic1_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_doctor_clinic_mappings', array( 'doctor_id' => $doctor_clinic2, 'clinic_id' => $clinic2_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $patient_clinic1, 'clinic_id' => $clinic1_id ) ); $wpdb->insert( $wpdb->prefix . 'kc_patient_clinic_mappings', array( 'patient_id' => $patient_clinic2, 'clinic_id' => $clinic2_id ) ); // Create sensitive encounters $appointment1_id = $this->create_test_appointment( $clinic1_id, $doctor_clinic1, $patient_clinic1 ); $appointment2_id = $this->create_test_appointment( $clinic2_id, $doctor_clinic2, $patient_clinic2 ); $sensitive_encounter1 = $this->make_request( '/wp-json/care/v1/encounters', 'POST', array( 'appointment_id' => $appointment1_id, 'description' => 'CONFIDENTIAL: Mental health consultation - Depression treatment', 'diagnosis' => 'Major Depressive Disorder (F32.9)', ), $doctor_clinic1 ); $sensitive_encounter2 = $this->make_request( '/wp-json/care/v1/encounters', 'POST', array( 'appointment_id' => $appointment2_id, 'description' => 'CONFIDENTIAL: Substance abuse treatment consultation', 'diagnosis' => 'Alcohol Use Disorder (F10.20)', ), $doctor_clinic2 ); $encounter1_id = $sensitive_encounter1->get_data()['id']; $encounter2_id = $sensitive_encounter2->get_data()['id']; // Security test scenarios $security_tests = array( // Cross-clinic patient access array( 'test' => 'Cross-clinic patient access', 'request' => "/wp-json/care/v1/patients/{$patient_clinic2}", 'method' => 'GET', 'user_id' => $doctor_clinic1, 'expected' => 403, ), // Cross-clinic encounter access array( 'test' => 'Cross-clinic encounter access', 'request' => "/wp-json/care/v1/encounters/{$encounter2_id}", 'method' => 'GET', 'user_id' => $doctor_clinic1, 'expected' => 403, ), // Direct database manipulation attempts via API array( 'test' => 'SQL injection attempt', 'request' => '/wp-json/care/v1/patients', 'method' => 'GET', 'data' => array( 'clinic_id' => "1 OR 1=1; DROP TABLE {$wpdb->prefix}kc_clinics; --" ), 'user_id' => $doctor_clinic1, 'expected' => 400, ), ); foreach ( $security_tests as $test ) { $response = $this->make_request( $test['request'], $test['method'], isset( $test['data'] ) ? $test['data'] : array(), $test['user_id'] ); $this->assertRestResponse( $response, $test['expected'], "Failed security test: {$test['test']}" ); } // Verify no data leakage in responses $clinic1_patients_response = $this->make_request( '/wp-json/care/v1/patients', 'GET', array(), $doctor_clinic1 ); $patients = $clinic1_patients_response->get_data()['data']; foreach ( $patients as $patient ) { $this->assertEquals( $clinic1_id, $patient['clinic_id'], 'Patient from wrong clinic returned in response' ); } } }