""" Unit tests for Healthcare Models Tests for healthcare module models: - Patient - Appointment Author: Claude """ import pytest from django.test import TestCase from django.contrib.auth import get_user_model from django.utils import timezone from decimal import Decimal from datetime import date, time, timedelta from backend.src.core.models.tenant import Tenant from backend.src.core.models.user import User from backend.src.modules.healthcare.models.patient import Patient from backend.src.modules.healthcare.models.appointment import Appointment User = get_user_model() class PatientModelTest(TestCase): """Test cases for Patient model""" def setUp(self): self.tenant = Tenant.objects.create( name='Test Healthcare Sdn Bhd', schema_name='test_healthcare', domain='testhealthcare.com', business_type='healthcare' ) self.user = User.objects.create_user( username='doctor', email='doctor@test.com', password='test123', tenant=self.tenant, role='staff' ) self.patient_data = { 'tenant': self.tenant, 'patient_id': 'P2024010001', 'first_name': 'John', 'last_name': 'Doe', 'ic_number': '000101-01-0001', 'passport_number': '', 'nationality': 'Malaysian', 'gender': 'male', 'date_of_birth': date(1990, 1, 1), 'blood_type': 'O+', 'email': 'john.doe@test.com', 'phone': '+60123456789', 'emergency_contact_name': 'Jane Doe', 'emergency_contact_phone': '+60123456788', 'emergency_contact_relationship': 'Spouse', 'address': '123 Test Street', 'city': 'Kuala Lumpur', 'state': 'KUL', 'postal_code': '50000', 'medical_history': 'No significant medical history', 'allergies': 'None known', 'current_medications': 'None', 'chronic_conditions': 'None', 'last_visit_date': None, 'is_active': True, 'created_by': self.user } def test_create_patient(self): """Test creating a new patient""" patient = Patient.objects.create(**self.patient_data) self.assertEqual(patient.tenant, self.tenant) self.assertEqual(patient.patient_id, self.patient_data['patient_id']) self.assertEqual(patient.first_name, self.patient_data['first_name']) self.assertEqual(patient.last_name, self.patient_data['last_name']) self.assertEqual(patient.ic_number, self.patient_data['ic_number']) self.assertEqual(patient.gender, self.patient_data['gender']) self.assertEqual(patient.blood_type, self.patient_data['blood_type']) self.assertTrue(patient.is_active) def test_patient_string_representation(self): """Test patient string representation""" patient = Patient.objects.create(**self.patient_data) self.assertEqual(str(patient), f"{patient.first_name} {patient.last_name} ({patient.patient_id})") def test_patient_full_name(self): """Test patient full name property""" patient = Patient.objects.create(**self.patient_data) self.assertEqual(patient.full_name, f"{patient.first_name} {patient.last_name}") def test_patient_age(self): """Test patient age calculation""" patient = Patient.objects.create(**self.patient_data) # Age should be calculated based on date of birth today = date.today() expected_age = today.year - patient.date_of_birth.year if today.month < patient.date_of_birth.month or (today.month == patient.date_of_birth.month and today.day < patient.date_of_birth.day): expected_age -= 1 self.assertEqual(patient.age, expected_age) def test_patient_malaysian_ic_validation(self): """Test Malaysian IC number validation""" # Valid IC number patient = Patient.objects.create(**self.patient_data) self.assertEqual(patient.ic_number, self.patient_data['ic_number']) # Invalid IC number format invalid_data = self.patient_data.copy() invalid_data['ic_number'] = '123' with self.assertRaises(Exception): Patient.objects.create(**invalid_data) def test_patient_gender_choices(self): """Test patient gender validation""" invalid_data = self.patient_data.copy() invalid_data['gender'] = 'invalid_gender' with self.assertRaises(Exception): Patient.objects.create(**invalid_data) def test_patient_blood_type_choices(self): """Test patient blood type validation""" invalid_data = self.patient_data.copy() invalid_data['blood_type'] = 'Z+' with self.assertRaises(Exception): Patient.objects.create(**invalid_data) def test_patient_phone_validation(self): """Test Malaysian phone number validation""" # Valid Malaysian phone number patient = Patient.objects.create(**self.patient_data) self.assertEqual(patient.phone, self.patient_data['phone']) # Invalid phone number invalid_data = self.patient_data.copy() invalid_data['phone'] = '12345' with self.assertRaises(Exception): Patient.objects.create(**invalid_data) def test_patient_medical_info_validation(self): """Test patient medical information validation""" # Test with medical conditions data = self.patient_data.copy() data['chronic_conditions'] = 'Diabetes, Hypertension' data['allergies'] = 'Penicillin, Sulfa drugs' patient = Patient.objects.create(**data) self.assertEqual(patient.chronic_conditions, 'Diabetes, Hypertension') self.assertEqual(patient.allergies, 'Penicillin, Sulfa drugs') class AppointmentModelTest(TestCase): """Test cases for Appointment model""" def setUp(self): self.tenant = Tenant.objects.create( name='Test Healthcare Sdn Bhd', schema_name='test_healthcare', domain='testhealthcare.com', business_type='healthcare' ) self.doctor = User.objects.create_user( username='doctor', email='doctor@test.com', password='test123', tenant=self.tenant, role='staff' ) self.patient = Patient.objects.create( tenant=self.tenant, patient_id='P2024010001', first_name='John', last_name='Doe', ic_number='000101-01-0001', gender='male', date_of_birth=date(1990, 1, 1), blood_type='O+', phone='+60123456789', created_by=self.doctor ) self.appointment_data = { 'tenant': self.tenant, 'patient': self.patient, 'doctor': self.doctor, 'appointment_number': 'APT-2024010001', 'appointment_date': date.today() + timedelta(days=1), 'appointment_time': time(10, 0), 'end_time': time(10, 30), 'appointment_type': 'consultation', 'status': 'scheduled', 'reason': 'General checkup', 'notes': '', 'is_telemedicine': False, 'telemedicine_link': '', 'reminder_sent': False, 'created_by': self.doctor } def test_create_appointment(self): """Test creating a new appointment""" appointment = Appointment.objects.create(**self.appointment_data) self.assertEqual(appointment.tenant, self.tenant) self.assertEqual(appointment.patient, self.patient) self.assertEqual(appointment.doctor, self.doctor) self.assertEqual(appointment.appointment_number, self.appointment_data['appointment_number']) self.assertEqual(appointment.status, self.appointment_data['status']) self.assertEqual(appointment.appointment_type, self.appointment_data['appointment_type']) self.assertFalse(appointment.is_telemedicine) def test_appointment_string_representation(self): """Test appointment string representation""" appointment = Appointment.objects.create(**self.appointment_data) expected = f"{self.patient.full_name} - {appointment.appointment_date} at {appointment.appointment_time}" self.assertEqual(str(appointment), expected) def test_appointment_duration(self): """Test appointment duration calculation""" appointment = Appointment.objects.create(**self.appointment_data) # Duration should be 30 minutes self.assertEqual(appointment.duration, 30) def test_appointment_is_upcoming(self): """Test appointment upcoming status""" # Future appointment appointment = Appointment.objects.create(**self.appointment_data) self.assertTrue(appointment.is_upcoming) # Past appointment appointment.appointment_date = date.today() - timedelta(days=1) appointment.save() self.assertFalse(appointment.is_upcoming) def test_appointment_status_choices(self): """Test appointment status validation""" invalid_data = self.appointment_data.copy() invalid_data['status'] = 'invalid_status' with self.assertRaises(Exception): Appointment.objects.create(**invalid_data) def test_appointment_type_choices(self): """Test appointment type validation""" invalid_data = self.appointment_data.copy() invalid_data['appointment_type'] = 'invalid_type' with self.assertRaises(Exception): Appointment.objects.create(**invalid_data) def test_appointment_time_validation(self): """Test appointment time validation""" # Valid time range appointment = Appointment.objects.create(**self.appointment_data) self.assertEqual(appointment.appointment_time, self.appointment_data['appointment_time']) self.assertEqual(appointment.end_time, self.appointment_data['end_time']) # Invalid time range (end before start) invalid_data = self.appointment_data.copy() invalid_data['appointment_time'] = time(11, 0) invalid_data['end_time'] = time(10, 30) with self.assertRaises(Exception): Appointment.objects.create(**invalid_data) def test_appointment_conflict_detection(self): """Test appointment conflict detection""" # Create first appointment appointment1 = Appointment.objects.create(**self.appointment_data) # Try to create conflicting appointment conflict_data = self.appointment_data.copy() conflict_data['appointment_number'] = 'APT-2024010002' conflict_data['appointment_time'] = time(10, 15) conflict_data['end_time'] = time(10, 45) # This should not raise an exception but conflict detection should be available appointment2 = Appointment.objects.create(**conflict_data) # Check if there's a conflict self.assertTrue( appointment1.appointment_date == appointment2.appointment_date and appointment1.doctor == appointment2.doctor and ( (appointment1.appointment_time <= appointment2.appointment_time < appointment1.end_time) or (appointment2.appointment_time <= appointment1.appointment_time < appointment2.end_time) ) ) def test_telemedicine_appointment(self): """Test telemedicine appointment features""" data = self.appointment_data.copy() data['is_telemedicine'] = True data['telemedicine_link'] = 'https://meet.test.com/room/12345' appointment = Appointment.objects.create(**data) self.assertTrue(appointment.is_telemedicine) self.assertEqual(appointment.telemedicine_link, data['telemedicine_link']) def test_appointment_reminder_features(self): """Test appointment reminder features""" appointment = Appointment.objects.create(**self.appointment_data) # Initially no reminder sent self.assertFalse(appointment.reminder_sent) # Mark reminder as sent appointment.reminder_sent = True appointment.reminder_sent_at = timezone.now() appointment.save() self.assertTrue(appointment.reminder_sent) self.assertIsNotNone(appointment.reminder_sent_at)