""" Contract test for POST /healthcare/patients endpoint. This test MUST fail before implementation. """ import pytest from django.test import TestCase from django.urls import reverse from rest_framework.test import APIClient from rest_framework import status import json class HealthcarePatientsPostContractTest(TestCase): def setUp(self): self.client = APIClient() self.patients_url = '/api/v1/healthcare/patients/' # Tenant authentication header self.tenant_auth = {'HTTP_AUTHORIZATION': 'Bearer tenant_token'} # Valid patient data self.patient_data = { 'ic_number': '900101-10-1234', 'name': 'Ahmad bin Hassan', 'gender': 'MALE', 'date_of_birth': '1990-01-01', 'phone': '+60123456789', 'email': 'ahmad.hassan@example.com', 'address': { 'street': '123 Jalan Healthcare', 'city': 'Kuala Lumpur', 'state': 'Wilayah Persekutuan', 'postal_code': '50400', 'country': 'Malaysia' }, 'blood_type': 'O+', 'allergies': ['Penicillin', 'Peanuts'], 'medications': ['Metformin 500mg', 'Lisinopril 10mg'], 'emergency_contacts': [ { 'name': 'Siti binti Ibrahim', 'relationship': 'Spouse', 'phone': '+60198765432', 'email': 'siti.ibrahim@example.com' } ], 'insurance': { 'provider': 'Malaysia National Insurance', 'policy_number': 'MNI-123456789', 'coverage_details': 'Full coverage', 'expiry_date': '2024-12-31' }, 'medical_history': { 'conditions': ['Type 2 Diabetes', 'Hypertension'], 'surgeries': ['Appendectomy (2015)'], 'family_history': ['Diabetes (paternal)', 'Hypertension (maternal)'], 'immunizations': ['COVID-19 Vaccine (2023)', 'Flu Vaccine (2023)'] } } def test_create_patient_success(self): """Test successful patient creation.""" response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json', **self.tenant_auth ) # This should fail before implementation assert response.status_code == status.HTTP_201_CREATED data = response.json() assert 'id' in data assert data['ic_number'] == self.patient_data['ic_number'] assert data['name'] == self.patient_data['name'] assert data['gender'] == self.patient_data['gender'] assert data['date_of_birth'] == self.patient_data['date_of_birth'] assert data['age'] == 34 # Calculated from DOB assert data['status'] == 'ACTIVE' # Default status # Should have timestamps assert 'created_at' in data assert 'updated_at' in data # Should have tenant_id from context assert 'tenant_id' in data # Should include medical information assert data['blood_type'] == self.patient_data['blood_type'] assert data['allergies'] == self.patient_data['allergies'] assert data['medications'] == self.patient_data['medications'] def test_create_patient_unauthorized(self): """Test patient creation without authentication.""" response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json' ) assert response.status_code == status.HTTP_401_UNAUTHORIZED def test_create_patient_missing_required_fields(self): """Test patient creation with missing required fields.""" incomplete_data = self.patient_data.copy() del incomplete_data['ic_number'] response = self.client.post( self.patients_url, data=json.dumps(incomplete_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST data = response.json() assert 'ic_number' in data.get('errors', {}) def test_create_patient_invalid_ic_number(self): """Test patient creation with invalid IC number format.""" invalid_data = self.patient_data.copy() invalid_data['ic_number'] = 'invalid-ic-format' response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_invalid_gender(self): """Test patient creation with invalid gender.""" invalid_data = self.patient_data.copy() invalid_data['gender'] = 'INVALID_GENDER' response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_invalid_blood_type(self): """Test patient creation with invalid blood type.""" invalid_data = self.patient_data.copy() invalid_data['blood_type'] = 'INVALID_BLOOD_TYPE' response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_future_birth_date(self): """Test patient creation with future birth date.""" invalid_data = self.patient_data.copy() invalid_data['date_of_birth'] = '2050-01-01' # Future date response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_duplicate_ic_number(self): """Test patient creation with duplicate IC number.""" # First request should succeed (if implemented) first_response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json', **self.tenant_auth ) if first_response.status_code == status.HTTP_201_CREATED: # Second request with same IC number should fail second_response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json', **self.tenant_auth ) assert second_response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_with_minimal_data(self): """Test patient creation with minimal required data.""" minimal_data = { 'ic_number': '950505-05-5678', 'name': 'Lee Mei Lin', 'gender': 'FEMALE', 'date_of_birth': '1995-05-05' } response = self.client.post( self.patients_url, data=json.dumps(minimal_data), content_type='application/json', **self.tenant_auth ) if response.status_code == status.HTTP_201_CREATED: data = response.json() assert data['ic_number'] == minimal_data['ic_number'] assert data['name'] == minimal_data['name'] # Optional fields should have default values assert data['blood_type'] == 'UNKNOWN' assert data['allergies'] == [] def test_create_patient_invalid_email(self): """Test patient creation with invalid email format.""" invalid_data = self.patient_data.copy() invalid_data['email'] = 'invalid-email-format' response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_malformed_address(self): """Test patient creation with malformed address JSON.""" invalid_data = self.patient_data.copy() invalid_data['address'] = 'invalid address format' response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_missing_address_fields(self): """Test patient creation with missing address fields.""" invalid_data = self.patient_data.copy() invalid_data['address'] = {'street': '123 Street'} # Missing required fields response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_invalid_emergency_contact(self): """Test patient creation with invalid emergency contact.""" invalid_data = self.patient_data.copy() invalid_data['emergency_contacts'] = [ { 'name': 'Emergency Contact', # Missing required relationship and phone } ] response = self.client.post( self.patients_url, data=json.dumps(invalid_data), content_type='application/json', **self.tenant_auth ) assert response.status_code == status.HTTP_400_BAD_REQUEST def test_create_patient_age_calculation(self): """Test that age is calculated correctly from date of birth.""" response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json', **self.tenant_auth ) if response.status_code == status.HTTP_201_CREATED: data = response.json() # Age should be calculated based on current date and birth date # This will be validated once implementation exists assert isinstance(data['age'], int) assert data['age'] > 0 def test_create_patient_tenant_isolation(self): """Test that patient creation respects tenant isolation.""" response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json', **self.tenant_auth ) if response.status_code == status.HTTP_201_CREATED: data = response.json() # Patient should be created in the authenticated tenant's context assert 'tenant_id' in data # This will be validated once implementation exists def test_create_patient_data_privacy_compliance(self): """Test that patient creation handles sensitive data according to PDPA.""" response = self.client.post( self.patients_url, data=json.dumps(self.patient_data), content_type='application/json', **self.tenant_auth ) if response.status_code == status.HTTP_201_CREATED: data = response.json() # Sensitive medical data should be stored and handled properly assert 'allergies' in data assert 'medications' in data assert 'medical_history' in data # IC number should be handled with special care for privacy assert data['ic_number'] == self.patient_data['ic_number'] def test_create_patient_with_consent_info(self): """Test patient creation with consent information.""" consent_data = self.patient_data.copy() consent_data['consents'] = [ { 'type': 'TREATMENT', 'given_at': '2024-01-15T10:00:00Z', 'expires_at': '2025-01-15T10:00:00Z', 'notes': 'Consent for general treatment' }, { 'type': 'DATA_SHARING', 'given_at': '2024-01-15T10:00:00Z', 'expires_at': None, 'notes': 'Consent to share data with insurance provider' } ] response = self.client.post( self.patients_url, data=json.dumps(consent_data), content_type='application/json', **self.tenant_auth ) if response.status_code == status.HTTP_201_CREATED: data = response.json() assert 'consents' in data assert len(data['consents']) == 2 assert data['consents'][0]['type'] == 'TREATMENT'