File size: 8,073 Bytes
532e8ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# tests/routes/test_patient_routes.py

from datetime import datetime, timezone
from unittest.mock import MagicMock

import pytest
from fastapi import FastAPI, status
from fastapi.testclient import TestClient

from src.api.routes.patient import router as patient_router
from src.core.state import get_state
from src.models.patient import Patient
from src.models.session import Session

# --- Test Setup: Mocking and Dependency Injection ---

# Mock the service layer that the API routes depend on.
mock_memory_manager = MagicMock()

# This mock AppState and override function will replace the real dependencies.
class MockAppState:
	def __init__(self):
		self.memory_manager = mock_memory_manager

def override_get_state() -> MockAppState:
	return MockAppState()

# Create a FastAPI app instance for testing and apply the dependency override.
app = FastAPI()
app.include_router(patient_router)
app.dependency_overrides[get_state] = override_get_state


# --- Fixtures ---

@pytest.fixture
def client():
	"""Provides a TestClient for making requests to the app."""
	with TestClient(app) as c:
		yield c

@pytest.fixture(autouse=True)
def reset_mocks():
	"""Resets mocks before each test to ensure test isolation."""
	mock_memory_manager.reset_mock()


# --- Test Data ---

fake_patient_dict = {
	"_id": "patient123",
	"name": "Jane Doe",
	"age": 42,
	"sex": "Female",
	"ethnicity": "Caucasian",
	"created_at": datetime.now(timezone.utc).isoformat(),
	"updated_at": datetime.now(timezone.utc).isoformat(),
}
fake_patient = Patient.model_validate(fake_patient_dict)

fake_session_dict = {
	"_id": "session456",
	"account_id": "doctor789",
	"patient_id": "patient123",
	"title": "Checkup",
	"created_at": datetime.now(timezone.utc).isoformat(),
	"updated_at": datetime.now(timezone.utc).isoformat(),
	"messages": [],
}
fake_session = Session.model_validate(fake_session_dict)


# --- Tests for GET /patient ---

def test_search_patients_success(client: TestClient):
	"""Tests successfully searching for patients with a query."""
	mock_memory_manager.search_patients.return_value = [fake_patient]

	response = client.get("/patient?q=Jane&limit=5")

	assert response.status_code == status.HTTP_200_OK
	assert len(response.json()) == 1
	assert response.json()[0]["name"] == "Jane Doe"
	mock_memory_manager.search_patients.assert_called_once_with("Jane", limit=5)

def test_search_patients_requires_query(client: TestClient):
	"""Tests that a 400 error is returned if the search query 'q' is missing."""
	response = client.get("/patient")

	assert response.status_code == status.HTTP_400_BAD_REQUEST
	assert response.json()["detail"] == "A search query 'q' is required."


# --- Tests for POST /patient ---

def test_create_patient_success(client: TestClient):
	"""Tests successful creation of a new patient profile."""
	new_patient_id = "new_patient_abc"
	mock_memory_manager.create_patient.return_value = new_patient_id
	mock_memory_manager.get_patient_by_id.return_value = fake_patient

	patient_data = {"name": "Jane Doe", "age": 42, "sex": "Female", "ethnicity": "Caucasian"}
	response = client.post("/patient", json=patient_data)

	assert response.status_code == status.HTTP_201_CREATED
	assert response.json()["name"] == "Jane Doe"
	mock_memory_manager.create_patient.assert_called_once()
	mock_memory_manager.get_patient_by_id.assert_called_once_with(new_patient_id)

def test_create_patient_invalid_data(client: TestClient):
	"""Tests failure when the memory manager cannot create a patient (e.g., bad data)."""
	mock_memory_manager.create_patient.return_value = None

	patient_data = {"name": "Invalid", "age": -5, "sex": "F", "ethnicity": "Unknown"}
	response = client.post("/patient", json=patient_data)

	assert response.status_code == status.HTTP_400_BAD_REQUEST
	assert response.json()["detail"] == "Patient could not be created due to invalid data."

def test_create_patient_not_found_after_creation(client: TestClient):
	"""Tests the edge case where the patient can't be retrieved after creation."""
	new_patient_id = "new_patient_abc"
	mock_memory_manager.create_patient.return_value = new_patient_id
	mock_memory_manager.get_patient_by_id.return_value = None

	patient_data = {"name": "Jane Doe", "age": 42, "sex": "Female", "ethnicity": "Caucasian"}
	response = client.post("/patient", json=patient_data)

	assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
	assert response.json()["detail"] == "Could not find newly created patient."


# --- Tests for GET /patient/{patient_id} ---

def test_get_patient_by_id_success(client: TestClient):
	"""Tests successfully retrieving a single patient by their ID."""
	mock_memory_manager.get_patient_by_id.return_value = fake_patient

	response = client.get(f"/patient/{fake_patient.id}")

	assert response.status_code == status.HTTP_200_OK
	assert response.json()["name"] == fake_patient.name
	mock_memory_manager.get_patient_by_id.assert_called_once_with(str(fake_patient.id))

def test_get_patient_by_id_not_found(client: TestClient):
	"""Tests that a 404 error is returned for a non-existent patient ID."""
	mock_memory_manager.get_patient_by_id.return_value = None

	response = client.get("/patient/non_existent_id")

	assert response.status_code == status.HTTP_404_NOT_FOUND
	assert response.json()["detail"] == "Patient not found"


# --- Tests for PATCH /patient/{patient_id} ---

def test_update_patient_success(client: TestClient):
	"""Tests a successful patient profile update."""
	mock_memory_manager.update_patient_profile.return_value = 1  # 1 document modified
	mock_memory_manager.get_patient_by_id.return_value = fake_patient

	update_data = {"age": 43}
	response = client.patch(f"/patient/{fake_patient.id}", json=update_data)

	assert response.status_code == status.HTTP_200_OK
	assert response.json()["name"] == fake_patient.name
	mock_memory_manager.update_patient_profile.assert_called_once_with(str(fake_patient.id), update_data)

def test_update_patient_no_fields_provided(client: TestClient):
	"""Tests that a 400 error is returned if the update request body is empty."""
	response = client.patch(f"/patient/{fake_patient.id}", json={})

	assert response.status_code == status.HTTP_400_BAD_REQUEST
	assert response.json()["detail"] == "No update fields provided."

def test_update_patient_not_found(client: TestClient):
	"""Tests updating a non-existent patient."""
	mock_memory_manager.update_patient_profile.return_value = 0
	# The route logic then checks if the patient exists. Simulate it not existing.
	mock_memory_manager.get_patient_by_id.return_value = None

	response = client.patch("/patient/non_existent_id", json={"age": 50})

	assert response.status_code == status.HTTP_404_NOT_FOUND
	assert response.json()["detail"] == "Patient not found"
	# Check that get_patient_by_id was called as part of the 404 check
	mock_memory_manager.get_patient_by_id.assert_called_once_with("non_existent_id")


# --- Tests for GET /patient/{patient_id}/session ---

def test_list_sessions_for_patient_success(client: TestClient):
	"""Tests successfully listing all sessions for a given patient."""
	mock_memory_manager.get_patient_by_id.return_value = fake_patient
	mock_memory_manager.list_patient_sessions.return_value = [fake_session]

	response = client.get(f"/patient/{fake_patient.id}/session")

	assert response.status_code == status.HTTP_200_OK
	assert len(response.json()) == 1
	assert response.json()[0]["title"] == "Checkup"
	# Verify it first checked that the patient exists
	mock_memory_manager.get_patient_by_id.assert_called_once_with(str(fake_patient.id))
	mock_memory_manager.list_patient_sessions.assert_called_once_with(str(fake_patient.id))

def test_list_sessions_for_patient_not_found(client: TestClient):
	"""Tests listing sessions for a non-existent patient."""
	mock_memory_manager.get_patient_by_id.return_value = None

	response = client.get("/patient/non_existent_id/session")

	assert response.status_code == status.HTTP_404_NOT_FOUND
	assert response.json()["detail"] == "Patient not found"
	mock_memory_manager.list_patient_sessions.assert_not_called()