589 lines
17 KiB
Markdown
589 lines
17 KiB
Markdown
# API Testing Guide - Tenhal Healthcare Platform
|
|
|
|
## Setup for Testing
|
|
|
|
### 1. Install Required Packages
|
|
```bash
|
|
pip install requests pytest pytest-django
|
|
```
|
|
|
|
### 2. Start Development Server
|
|
```bash
|
|
python manage.py runserver
|
|
```
|
|
|
|
### 3. Create Test User (Django Shell)
|
|
```python
|
|
python manage.py shell
|
|
|
|
from core.models import User, Tenant
|
|
|
|
# Create tenant
|
|
tenant = Tenant.objects.create(name="Test Clinic")
|
|
|
|
# Create test user
|
|
user = User.objects.create_user(
|
|
username='testuser',
|
|
email='test@example.com',
|
|
password='testpass123',
|
|
tenant=tenant,
|
|
role='DOCTOR'
|
|
)
|
|
```
|
|
|
|
## Testing with Python Requests
|
|
|
|
### Basic Setup
|
|
```python
|
|
import requests
|
|
import json
|
|
|
|
BASE_URL = 'http://localhost:8000/api/v1'
|
|
|
|
# Login to get session
|
|
session = requests.Session()
|
|
login_response = session.post(
|
|
'http://localhost:8000/accounts/login/',
|
|
data={'username': 'testuser', 'password': 'testpass123'}
|
|
)
|
|
```
|
|
|
|
### Test 1: List Patients
|
|
```python
|
|
def test_list_patients():
|
|
response = session.get(f'{BASE_URL}/patients/')
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
print(f"Found {data['count']} patients")
|
|
return data
|
|
|
|
# Run test
|
|
patients = test_list_patients()
|
|
```
|
|
|
|
### Test 2: Create Patient
|
|
```python
|
|
def test_create_patient():
|
|
patient_data = {
|
|
'first_name_en': 'John',
|
|
'last_name_en': 'Doe',
|
|
'date_of_birth': '1990-01-01',
|
|
'gender': 'MALE',
|
|
'phone': '+966501234567',
|
|
'email': 'john.doe@example.com'
|
|
}
|
|
|
|
response = session.post(
|
|
f'{BASE_URL}/patients/',
|
|
json=patient_data
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
patient = response.json()
|
|
print(f"Created patient: {patient['id']}")
|
|
return patient
|
|
|
|
# Run test
|
|
new_patient = test_create_patient()
|
|
```
|
|
|
|
### Test 3: Get Patient Details
|
|
```python
|
|
def test_get_patient(patient_id):
|
|
response = session.get(f'{BASE_URL}/patients/{patient_id}/')
|
|
assert response.status_code == 200
|
|
patient = response.json()
|
|
print(f"Patient: {patient['full_name']}")
|
|
return patient
|
|
|
|
# Run test
|
|
patient_details = test_get_patient(new_patient['id'])
|
|
```
|
|
|
|
### Test 4: Search Patients
|
|
```python
|
|
def test_search_patients(query):
|
|
response = session.get(
|
|
f'{BASE_URL}/patients/',
|
|
params={'search': query}
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
print(f"Found {data['count']} patients matching '{query}'")
|
|
return data
|
|
|
|
# Run test
|
|
search_results = test_search_patients('john')
|
|
```
|
|
|
|
### Test 5: Book Appointment
|
|
```python
|
|
def test_book_appointment(patient_id, clinic_id, provider_id):
|
|
appointment_data = {
|
|
'patient': patient_id,
|
|
'clinic': clinic_id,
|
|
'provider': provider_id,
|
|
'service_type': 'CONSULTATION',
|
|
'scheduled_date': '2025-10-15',
|
|
'scheduled_time': '10:00:00',
|
|
'duration': 30,
|
|
'notes': 'First visit'
|
|
}
|
|
|
|
response = session.post(
|
|
f'{BASE_URL}/appointments/',
|
|
json=appointment_data
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
appointment = response.json()
|
|
print(f"Booked appointment: {appointment['appointment_number']}")
|
|
return appointment
|
|
|
|
# Run test (replace with actual IDs)
|
|
# appointment = test_book_appointment(patient_id, clinic_id, provider_id)
|
|
```
|
|
|
|
### Test 6: Confirm Appointment
|
|
```python
|
|
def test_confirm_appointment(appointment_id):
|
|
response = session.post(
|
|
f'{BASE_URL}/appointments/{appointment_id}/confirm/',
|
|
json={'method': 'SMS'}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
appointment = response.json()
|
|
print(f"Confirmed appointment: {appointment['status']}")
|
|
return appointment
|
|
|
|
# Run test
|
|
# confirmed = test_confirm_appointment(appointment['id'])
|
|
```
|
|
|
|
### Test 7: Create Invoice
|
|
```python
|
|
def test_create_invoice(patient_id, service_id):
|
|
invoice_data = {
|
|
'patient': patient_id,
|
|
'issue_date': '2025-10-13',
|
|
'due_date': '2025-10-20',
|
|
'subtotal': '500.00',
|
|
'tax': '75.00',
|
|
'discount': '0.00',
|
|
'line_items': [
|
|
{
|
|
'service': service_id,
|
|
'description': 'Medical Consultation',
|
|
'quantity': 1,
|
|
'unit_price': '500.00'
|
|
}
|
|
]
|
|
}
|
|
|
|
response = session.post(
|
|
f'{BASE_URL}/invoices/',
|
|
json=invoice_data
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
invoice = response.json()
|
|
print(f"Created invoice: {invoice['invoice_number']}")
|
|
return invoice
|
|
|
|
# Run test
|
|
# invoice = test_create_invoice(patient_id, service_id)
|
|
```
|
|
|
|
### Test 8: Record Payment
|
|
```python
|
|
def test_record_payment(invoice_id):
|
|
payment_data = {
|
|
'invoice': invoice_id,
|
|
'payment_date': '2025-10-13',
|
|
'amount': '575.00',
|
|
'method': 'CARD',
|
|
'transaction_id': 'TXN123456',
|
|
'reference': 'Card ending 1234'
|
|
}
|
|
|
|
response = session.post(
|
|
f'{BASE_URL}/payments/',
|
|
json=payment_data
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
payment = response.json()
|
|
print(f"Recorded payment: {payment['id']}")
|
|
return payment
|
|
|
|
# Run test
|
|
# payment = test_record_payment(invoice['id'])
|
|
```
|
|
|
|
### Test 9: Create Referral
|
|
```python
|
|
def test_create_referral(patient_id, from_clinic_id, to_clinic_id):
|
|
referral_data = {
|
|
'patient': patient_id,
|
|
'from_clinic': from_clinic_id,
|
|
'to_clinic': to_clinic_id,
|
|
'reason': 'Patient requires ABA assessment',
|
|
'clinical_summary': '3-year-old with speech delay',
|
|
'urgency': 'ROUTINE'
|
|
}
|
|
|
|
response = session.post(
|
|
f'{BASE_URL}/referrals/',
|
|
json=referral_data
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
referral = response.json()
|
|
print(f"Created referral: {referral['id']}")
|
|
return referral
|
|
|
|
# Run test
|
|
# referral = test_create_referral(patient_id, from_clinic_id, to_clinic_id)
|
|
```
|
|
|
|
### Test 10: Accept Referral
|
|
```python
|
|
def test_accept_referral(referral_id):
|
|
response = session.post(
|
|
f'{BASE_URL}/referrals/{referral_id}/accept/',
|
|
json={'response_notes': 'Referral accepted. Will schedule assessment.'}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
referral = response.json()
|
|
print(f"Accepted referral: {referral['status']}")
|
|
return referral
|
|
|
|
# Run test
|
|
# accepted = test_accept_referral(referral['id'])
|
|
```
|
|
|
|
## Testing with cURL
|
|
|
|
### List Patients
|
|
```bash
|
|
curl -X GET http://localhost:8000/api/v1/patients/ \
|
|
-H "Cookie: sessionid=your-session-id"
|
|
```
|
|
|
|
### Create Patient
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/patients/ \
|
|
-H "Content-Type: application/json" \
|
|
-H "Cookie: sessionid=your-session-id" \
|
|
-d '{
|
|
"first_name_en": "Jane",
|
|
"last_name_en": "Smith",
|
|
"date_of_birth": "1985-05-15",
|
|
"gender": "FEMALE",
|
|
"phone": "+966509876543"
|
|
}'
|
|
```
|
|
|
|
### Book Appointment
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/appointments/ \
|
|
-H "Content-Type: application/json" \
|
|
-H "Cookie: sessionid=your-session-id" \
|
|
-d '{
|
|
"patient": "patient-uuid",
|
|
"clinic": "clinic-uuid",
|
|
"provider": "provider-uuid",
|
|
"scheduled_date": "2025-10-15",
|
|
"scheduled_time": "14:00:00",
|
|
"duration": 30
|
|
}'
|
|
```
|
|
|
|
### Get Today's Appointments
|
|
```bash
|
|
curl -X GET http://localhost:8000/api/v1/appointments/today/ \
|
|
-H "Cookie: sessionid=your-session-id"
|
|
```
|
|
|
|
### Get Appointment Statistics
|
|
```bash
|
|
curl -X GET http://localhost:8000/api/v1/appointments/statistics/ \
|
|
-H "Cookie: sessionid=your-session-id"
|
|
```
|
|
|
|
## Complete Test Suite
|
|
|
|
```python
|
|
import requests
|
|
import json
|
|
from datetime import date, timedelta
|
|
|
|
class TenhalAPITester:
|
|
def __init__(self, base_url='http://localhost:8000'):
|
|
self.base_url = base_url
|
|
self.api_url = f'{base_url}/api/v1'
|
|
self.session = requests.Session()
|
|
|
|
def login(self, username, password):
|
|
"""Login and establish session"""
|
|
response = self.session.post(
|
|
f'{self.base_url}/accounts/login/',
|
|
data={'username': username, 'password': password}
|
|
)
|
|
return response.status_code == 200
|
|
|
|
def test_patient_workflow(self):
|
|
"""Test complete patient workflow"""
|
|
print("\n=== Testing Patient Workflow ===")
|
|
|
|
# 1. Create patient
|
|
patient_data = {
|
|
'first_name_en': 'Test',
|
|
'last_name_en': 'Patient',
|
|
'date_of_birth': '2020-01-01',
|
|
'gender': 'MALE',
|
|
'phone': '+966501111111'
|
|
}
|
|
response = self.session.post(f'{self.api_url}/patients/', json=patient_data)
|
|
assert response.status_code == 201
|
|
patient = response.json()
|
|
print(f"✓ Created patient: {patient['mrn']}")
|
|
|
|
# 2. Get patient details
|
|
response = self.session.get(f"{self.api_url}/patients/{patient['id']}/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Retrieved patient details")
|
|
|
|
# 3. Search for patient
|
|
response = self.session.get(f"{self.api_url}/patients/", params={'search': 'Test'})
|
|
assert response.status_code == 200
|
|
print(f"✓ Searched patients")
|
|
|
|
return patient
|
|
|
|
def test_appointment_workflow(self, patient_id, clinic_id, provider_id):
|
|
"""Test complete appointment workflow"""
|
|
print("\n=== Testing Appointment Workflow ===")
|
|
|
|
# 1. Book appointment
|
|
appointment_data = {
|
|
'patient': patient_id,
|
|
'clinic': clinic_id,
|
|
'provider': provider_id,
|
|
'scheduled_date': str(date.today() + timedelta(days=1)),
|
|
'scheduled_time': '10:00:00',
|
|
'duration': 30
|
|
}
|
|
response = self.session.post(f'{self.api_url}/appointments/', json=appointment_data)
|
|
assert response.status_code == 201
|
|
appointment = response.json()
|
|
print(f"✓ Booked appointment: {appointment['appointment_number']}")
|
|
|
|
# 2. Confirm appointment
|
|
response = self.session.post(
|
|
f"{self.api_url}/appointments/{appointment['id']}/confirm/",
|
|
json={'method': 'SMS'}
|
|
)
|
|
assert response.status_code == 200
|
|
print(f"✓ Confirmed appointment")
|
|
|
|
# 3. Mark arrived
|
|
response = self.session.post(f"{self.api_url}/appointments/{appointment['id']}/arrive/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Marked patient arrived")
|
|
|
|
# 4. Start appointment
|
|
response = self.session.post(f"{self.api_url}/appointments/{appointment['id']}/start/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Started appointment")
|
|
|
|
# 5. Complete appointment
|
|
response = self.session.post(f"{self.api_url}/appointments/{appointment['id']}/complete/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Completed appointment")
|
|
|
|
return appointment
|
|
|
|
def test_finance_workflow(self, patient_id, service_id):
|
|
"""Test complete finance workflow"""
|
|
print("\n=== Testing Finance Workflow ===")
|
|
|
|
# 1. Create invoice
|
|
invoice_data = {
|
|
'patient': patient_id,
|
|
'issue_date': str(date.today()),
|
|
'due_date': str(date.today() + timedelta(days=7)),
|
|
'subtotal': '500.00',
|
|
'tax': '75.00',
|
|
'discount': '0.00',
|
|
'line_items': [{
|
|
'service': service_id,
|
|
'description': 'Test Service',
|
|
'quantity': 1,
|
|
'unit_price': '500.00'
|
|
}]
|
|
}
|
|
response = self.session.post(f'{self.api_url}/invoices/', json=invoice_data)
|
|
assert response.status_code == 201
|
|
invoice = response.json()
|
|
print(f"✓ Created invoice: {invoice['invoice_number']}")
|
|
|
|
# 2. Issue invoice
|
|
response = self.session.post(f"{self.api_url}/invoices/{invoice['id']}/issue/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Issued invoice")
|
|
|
|
# 3. Record payment
|
|
payment_data = {
|
|
'invoice': invoice['id'],
|
|
'payment_date': str(date.today()),
|
|
'amount': '575.00',
|
|
'method': 'CASH'
|
|
}
|
|
response = self.session.post(f'{self.api_url}/payments/', json=payment_data)
|
|
assert response.status_code == 201
|
|
print(f"✓ Recorded payment")
|
|
|
|
# 4. Process payment
|
|
payment = response.json()
|
|
response = self.session.post(f"{self.api_url}/payments/{payment['id']}/process/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Processed payment")
|
|
|
|
return invoice
|
|
|
|
def test_referral_workflow(self, patient_id, from_clinic_id, to_clinic_id):
|
|
"""Test complete referral workflow"""
|
|
print("\n=== Testing Referral Workflow ===")
|
|
|
|
# 1. Create referral
|
|
referral_data = {
|
|
'patient': patient_id,
|
|
'from_clinic': from_clinic_id,
|
|
'to_clinic': to_clinic_id,
|
|
'reason': 'Test referral',
|
|
'urgency': 'ROUTINE'
|
|
}
|
|
response = self.session.post(f'{self.api_url}/referrals/', json=referral_data)
|
|
assert response.status_code == 201
|
|
referral = response.json()
|
|
print(f"✓ Created referral")
|
|
|
|
# 2. Accept referral
|
|
response = self.session.post(
|
|
f"{self.api_url}/referrals/{referral['id']}/accept/",
|
|
json={'response_notes': 'Accepted'}
|
|
)
|
|
assert response.status_code == 200
|
|
print(f"✓ Accepted referral")
|
|
|
|
# 3. Complete referral
|
|
response = self.session.post(f"{self.api_url}/referrals/{referral['id']}/complete/")
|
|
assert response.status_code == 200
|
|
print(f"✓ Completed referral")
|
|
|
|
return referral
|
|
|
|
def run_all_tests(self, clinic_id, provider_id, service_id):
|
|
"""Run all test workflows"""
|
|
print("\n" + "="*50)
|
|
print("TENHAL API TEST SUITE")
|
|
print("="*50)
|
|
|
|
# Test patient workflow
|
|
patient = self.test_patient_workflow()
|
|
|
|
# Test appointment workflow
|
|
appointment = self.test_appointment_workflow(
|
|
patient['id'], clinic_id, provider_id
|
|
)
|
|
|
|
# Test finance workflow
|
|
invoice = self.test_finance_workflow(patient['id'], service_id)
|
|
|
|
# Test referral workflow (need two clinics)
|
|
# referral = self.test_referral_workflow(patient['id'], clinic_id, clinic_id)
|
|
|
|
print("\n" + "="*50)
|
|
print("ALL TESTS PASSED ✓")
|
|
print("="*50)
|
|
|
|
# Usage
|
|
if __name__ == '__main__':
|
|
tester = TenhalAPITester()
|
|
|
|
# Login
|
|
if tester.login('testuser', 'testpass123'):
|
|
print("✓ Logged in successfully")
|
|
|
|
# Run tests (replace with actual IDs)
|
|
# tester.run_all_tests(
|
|
# clinic_id='your-clinic-uuid',
|
|
# provider_id='your-provider-uuid',
|
|
# service_id='your-service-uuid'
|
|
# )
|
|
else:
|
|
print("✗ Login failed")
|
|
```
|
|
|
|
## Performance Testing
|
|
|
|
```python
|
|
import time
|
|
import statistics
|
|
|
|
def performance_test(session, endpoint, iterations=100):
|
|
"""Test API endpoint performance"""
|
|
times = []
|
|
|
|
for i in range(iterations):
|
|
start = time.time()
|
|
response = session.get(f'{BASE_URL}{endpoint}')
|
|
end = time.time()
|
|
times.append(end - start)
|
|
|
|
print(f"\nPerformance Test: {endpoint}")
|
|
print(f"Iterations: {iterations}")
|
|
print(f"Average: {statistics.mean(times):.3f}s")
|
|
print(f"Median: {statistics.median(times):.3f}s")
|
|
print(f"Min: {min(times):.3f}s")
|
|
print(f"Max: {max(times):.3f}s")
|
|
|
|
# Run performance tests
|
|
performance_test(session, '/patients/', iterations=50)
|
|
performance_test(session, '/appointments/', iterations=50)
|
|
```
|
|
|
|
## Error Handling Tests
|
|
|
|
```python
|
|
def test_error_handling():
|
|
"""Test API error responses"""
|
|
|
|
# Test 404
|
|
response = session.get(f'{BASE_URL}/patients/invalid-uuid/')
|
|
assert response.status_code == 404
|
|
print("✓ 404 error handled correctly")
|
|
|
|
# Test 400 (validation error)
|
|
response = session.post(f'{BASE_URL}/patients/', json={})
|
|
assert response.status_code == 400
|
|
print("✓ 400 validation error handled correctly")
|
|
|
|
# Test unauthorized
|
|
unauthorized_session = requests.Session()
|
|
response = unauthorized_session.get(f'{BASE_URL}/patients/')
|
|
assert response.status_code in [401, 403]
|
|
print("✓ Unauthorized access blocked")
|
|
|
|
test_error_handling()
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
This testing guide provides comprehensive examples for testing all major API endpoints. Adapt the test data (UUIDs, dates, etc.) to match your actual database records.
|
|
|
|
For automated testing in CI/CD, consider using pytest with pytest-django for integration tests.
|