* @since 1.0.0 */ namespace Care_API\Tests\Unit\Models; use Care_API\Models\Doctor; use Care_API\Models\Clinic; class DoctorTest extends \Care_API_Test_Case { /** * Mock wpdb for database operations */ private $mock_wpdb; /** * Setup before each test */ public function setUp(): void { parent::setUp(); // Mock wpdb global global $wpdb; $this->mock_wpdb = $this->createMock('wpdb'); $wpdb = $this->mock_wpdb; } /** * Test doctor creation with specializations * * @covers Doctor::create * @covers Doctor::validate_doctor_data */ public function test_doctor_creation_with_specializations() { // Arrange $valid_doctor_data = array( 'first_name' => 'Dr. António', 'last_name' => 'Carvalho', 'user_email' => 'dr.carvalho@clinica.com', 'specialization' => 'Cardiologia', 'qualification' => 'MD, PhD em Cardiologia', 'experience_years' => 15, 'mobile_number' => '+351912345678', 'address' => 'Av. da República, 50', 'city' => 'Porto', 'country' => 'Portugal', 'license_number' => 'OM12345', 'consultation_fee' => 75.00, 'languages' => array('Portuguese', 'English', 'Spanish'), 'working_hours' => array( 'monday' => array('start_time' => '09:00', 'end_time' => '17:00'), 'tuesday' => array('start_time' => '09:00', 'end_time' => '17:00'), 'wednesday' => array('start_time' => '09:00', 'end_time' => '17:00'), 'thursday' => array('start_time' => '09:00', 'end_time' => '17:00'), 'friday' => array('start_time' => '09:00', 'end_time' => '17:00') ), 'clinic_id' => 1 ); // Mock WordPress functions $this->mock_wp_functions_for_doctor_creation(); // Act $result = Doctor::create($valid_doctor_data); // Assert $this->assertIsInt($result, 'Doctor creation should return user ID'); $this->assertGreaterThan(0, $result, 'User ID should be positive'); } /** * Test doctor creation with missing required specialization * * @covers Doctor::create * @covers Doctor::validate_doctor_data */ public function test_doctor_creation_missing_specialization() { // Arrange $invalid_doctor_data = array( 'first_name' => 'Dr. Maria', 'last_name' => 'Silva', 'user_email' => 'dr.silva@clinica.com', // Missing required field: specialization 'qualification' => 'MD' ); // Act $result = Doctor::create($invalid_doctor_data); // Assert $this->assertInstanceOf('WP_Error', $result, 'Should return WP_Error for missing specialization'); $this->assertEquals('doctor_validation_failed', $result->get_error_code()); $error_data = $result->get_error_data(); $this->assertArrayHasKey('errors', $error_data); $this->assertContains("Field 'specialization' is required", $error_data['errors']); } /** * Test doctor creation with invalid consultation fee * * @covers Doctor::validate_doctor_data */ public function test_doctor_creation_invalid_consultation_fee() { // Arrange $doctor_data_invalid_fee = array( 'first_name' => 'Dr. João', 'last_name' => 'Costa', 'user_email' => 'dr.costa@clinica.com', 'specialization' => 'Dermatologia', 'qualification' => 'MD', 'consultation_fee' => 'not_a_number' // Invalid fee ); // Act $result = Doctor::create($doctor_data_invalid_fee); // Assert $this->assertInstanceOf('WP_Error', $result); $error_data = $result->get_error_data(); $this->assertContains('Invalid consultation fee. Must be a number', $error_data['errors']); } /** * Test doctor creation with invalid experience years * * @covers Doctor::validate_doctor_data */ public function test_doctor_creation_invalid_experience_years() { // Arrange $doctor_data_invalid_experience = array( 'first_name' => 'Dr. Pedro', 'last_name' => 'Martins', 'user_email' => 'dr.martins@clinica.com', 'specialization' => 'Pediatria', 'qualification' => 'MD', 'experience_years' => -5 // Invalid negative experience ); // Act $result = Doctor::create($doctor_data_invalid_experience); // Assert $this->assertInstanceOf('WP_Error', $result); $error_data = $result->get_error_data(); $this->assertContains('Invalid experience years. Must be a positive number', $error_data['errors']); } /** * Test doctor working hours validation and update * * @covers Doctor::update_schedule * @covers Doctor::validate_working_hours */ public function test_doctor_schedule_validation() { // Arrange $doctor_id = $this->create_test_doctor(); $valid_working_hours = array( 'monday' => array('start_time' => '08:00', 'end_time' => '16:00'), 'tuesday' => array('start_time' => '08:30', 'end_time' => '17:30'), 'wednesday' => array('start_time' => '09:00', 'end_time' => '18:00'), 'thursday' => array('start_time' => '08:00', 'end_time' => '16:00'), 'friday' => array('start_time' => '08:00', 'end_time' => '15:00') ); // Mock doctor exists check $this->mock_doctor_exists(true); // Mock update_user_meta global $wp_test_expectations; $wp_test_expectations['update_user_meta'] = true; // Act $result = Doctor::update_schedule($doctor_id, $valid_working_hours); // Assert $this->assertTrue($result, 'Working hours should be successfully updated'); } /** * Test doctor schedule validation with invalid time format * * @covers Doctor::validate_working_hours */ public function test_doctor_schedule_invalid_time_format() { // Arrange $doctor_id = $this->create_test_doctor(); $invalid_working_hours = array( 'monday' => array('start_time' => '25:00', 'end_time' => '16:00'), // Invalid hour 'tuesday' => array('start_time' => '08:00', 'end_time' => '17:70') // Invalid minutes ); // Mock doctor exists check $this->mock_doctor_exists(true); // Act $result = Doctor::update_schedule($doctor_id, $invalid_working_hours); // Assert $this->assertInstanceOf('WP_Error', $result); $this->assertEquals('invalid_time_format', $result->get_error_code()); } /** * Test doctor schedule validation with invalid day * * @covers Doctor::validate_working_hours */ public function test_doctor_schedule_invalid_day() { // Arrange $doctor_id = $this->create_test_doctor(); $invalid_working_hours = array( 'invalid_day' => array('start_time' => '09:00', 'end_time' => '17:00') ); // Mock doctor exists check $this->mock_doctor_exists(true); // Act $result = Doctor::update_schedule($doctor_id, $invalid_working_hours); // Assert $this->assertInstanceOf('WP_Error', $result); $this->assertEquals('invalid_day', $result->get_error_code()); } /** * Test doctor statistics calculation * * @covers Doctor::get_statistics */ public function test_doctor_statistics() { // Arrange $doctor_id = $this->create_test_doctor(); // Mock database queries for statistics $this->mock_wpdb->expects($this->exactly(6)) ->method('get_var') ->willReturnOnConsecutiveCalls( 25, // total_appointments 18, // unique_patients 3, // appointments_today 8, // appointments_this_week 22, // appointments_this_month 15 // completed_encounters ); // Mock get_user_meta for consultation fee global $wp_test_expectations; $wp_test_expectations['get_user_meta'] = 75.00; // Act $statistics = Doctor::get_statistics($doctor_id); // Assert $this->assertIsArray($statistics, 'Statistics should be an array'); $this->assertArrayHasKey('total_appointments', $statistics); $this->assertArrayHasKey('total_patients', $statistics); $this->assertArrayHasKey('appointments_today', $statistics); $this->assertArrayHasKey('appointments_this_week', $statistics); $this->assertArrayHasKey('appointments_this_month', $statistics); $this->assertArrayHasKey('completed_encounters', $statistics); $this->assertArrayHasKey('revenue_this_month', $statistics); $this->assertEquals(25, $statistics['total_appointments']); $this->assertEquals(18, $statistics['total_patients']); $this->assertEquals(3, $statistics['appointments_today']); $this->assertEquals(8, $statistics['appointments_this_week']); $this->assertEquals(22, $statistics['appointments_this_month']); $this->assertEquals(15, $statistics['completed_encounters']); $this->assertEquals(1650.00, $statistics['revenue_this_month']); // 22 * 75.00 } /** * Test doctor appointments retrieval with filters * * @covers Doctor::get_appointments */ public function test_doctor_appointments_retrieval() { // Arrange $doctor_id = $this->create_test_doctor(); $appointment_filters = array( 'status' => 1, // scheduled 'date_from' => '2024-01-01', 'date_to' => '2024-01-31', 'limit' => 10 ); // Mock database query results $mock_appointments = array( array( 'id' => 1, 'appointment_start_date' => '2024-01-15', 'appointment_start_time' => '10:00:00', 'appointment_end_date' => '2024-01-15', 'appointment_end_time' => '10:30:00', 'visit_type' => 'consultation', 'status' => 1, 'patient_id' => 100, 'patient_name' => 'João Silva', 'clinic_id' => 1, 'clinic_name' => 'Clínica Central', 'description' => 'Consulta de rotina', 'appointment_report' => '', 'created_at' => '2024-01-10 14:00:00' ), array( 'id' => 2, 'appointment_start_date' => '2024-01-16', 'appointment_start_time' => '14:00:00', 'appointment_end_date' => '2024-01-16', 'appointment_end_time' => '14:30:00', 'visit_type' => 'follow_up', 'status' => 1, 'patient_id' => 101, 'patient_name' => 'Maria Santos', 'clinic_id' => 1, 'clinic_name' => 'Clínica Central', 'description' => 'Consulta de seguimento', 'appointment_report' => '', 'created_at' => '2024-01-12 09:00:00' ) ); $this->mock_wpdb->expects($this->once()) ->method('get_results') ->willReturn($mock_appointments); // Act $appointments = Doctor::get_appointments($doctor_id, $appointment_filters); // Assert $this->assertIsArray($appointments, 'Appointments should be an array'); $this->assertCount(2, $appointments, 'Should return 2 appointments'); // Verify appointment structure $appointment = $appointments[0]; $this->assertArrayHasKey('id', $appointment); $this->assertArrayHasKey('start_date', $appointment); $this->assertArrayHasKey('start_time', $appointment); $this->assertArrayHasKey('patient', $appointment); $this->assertArrayHasKey('clinic', $appointment); $this->assertEquals(1, $appointment['id']); $this->assertEquals('2024-01-15', $appointment['start_date']); $this->assertEquals('consultation', $appointment['visit_type']); $this->assertEquals('João Silva', $appointment['patient']['name']); } /** * Test doctor clinic assignments (multiple clinics) * * @covers Doctor::assign_to_clinic * @covers Doctor::get_doctor_full_data */ public function test_doctor_multiple_clinic_assignments() { // Arrange $doctor_id = $this->create_test_doctor(); $clinic_id_1 = 1; $clinic_id_2 = 2; // Mock clinic exists checks $clinic_mock = $this->createMock('Care_API\Models\Clinic'); $clinic_mock->method('exists')->willReturn(true); // Mock wpdb operations for first assignment $this->mock_wpdb->expects($this->exactly(2)) ->method('get_var') ->willReturn(0); // No existing mappings $this->mock_wpdb->expects($this->exactly(2)) ->method('insert') ->willReturn(1); // Successful inserts // Act $result1 = Doctor::assign_to_clinic($doctor_id, $clinic_id_1); $result2 = Doctor::assign_to_clinic($doctor_id, $clinic_id_2); // Assert $this->assertTrue($result1, 'Doctor should be assigned to first clinic'); $this->assertTrue($result2, 'Doctor should be assigned to second clinic'); } /** * Helper method to create test doctor */ private function create_test_doctor() { return $this->factory->user->create(array( 'user_login' => 'test_doctor_' . wp_rand(1000, 9999), 'user_email' => 'testdoctor' . wp_rand(1000, 9999) . '@example.com', 'first_name' => 'Dr. Test', 'last_name' => 'Doctor', 'role' => 'kivicare_doctor' )); } /** * Helper method to mock WordPress functions for doctor creation */ private function mock_wp_functions_for_doctor_creation() { global $wp_test_expectations; // Mock successful user creation $wp_test_expectations['wp_insert_user'] = 123; $wp_test_expectations['is_email'] = true; $wp_test_expectations['get_user_by'] = false; // No existing user $wp_test_expectations['username_exists'] = false; // Username available $wp_test_expectations['sanitize_email'] = function($email) { return $email; }; $wp_test_expectations['sanitize_text_field'] = function($text) { return $text; }; $wp_test_expectations['sanitize_textarea_field'] = function($text) { return $text; }; $wp_test_expectations['wp_generate_password'] = 'test_password'; $wp_test_expectations['current_time'] = '2024-01-15 10:30:00'; $wp_test_expectations['update_user_meta'] = true; $wp_test_expectations['wp_json_encode'] = function($data) { return json_encode($data); }; } /** * Helper method to mock doctor exists check */ private function mock_doctor_exists($exists = true) { global $wp_test_expectations; if ($exists) { $mock_user = (object) array( 'ID' => 123, 'roles' => array('kivicare_doctor') ); $wp_test_expectations['get_user_by'] = $mock_user; } else { $wp_test_expectations['get_user_by'] = false; } } }