File size: 6,631 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
# tests/routes/test_account_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.account import router as account_router
from src.core.state import get_state
from src.data.connection import ActionFailed
from src.models.account import Account

# --- 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(account_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 ---

# A sample account object that can be reused in tests.
fake_account_dict = {
	"_id": "60c72b2f9b1d8b3b3c9d8b1a",
	"name": "Dr. Test",
	"role": "Doctor",
	"specialty": "Testing",
	"created_at": datetime.now(timezone.utc).isoformat(),
	"updated_at": datetime.now(timezone.utc).isoformat(),
	"last_seen": datetime.now(timezone.utc).isoformat(),
}
# Use model_validate to handle the string-based datetimes from the dict.
fake_account = Account.model_validate(fake_account_dict)


# --- Tests for GET /account ---

def test_get_all_accounts_success(client: TestClient):
	"""Tests successfully retrieving all accounts when no query is provided."""
	mock_memory_manager.get_all_accounts.return_value = [fake_account]

	response = client.get("/account?limit=10")

	assert response.status_code == status.HTTP_200_OK
	assert len(response.json()) == 1
	assert response.json()[0]["name"] == "Dr. Test"
	mock_memory_manager.get_all_accounts.assert_called_once_with(limit=10)
	mock_memory_manager.search_accounts.assert_not_called()

def test_search_accounts_success(client: TestClient):
	"""Tests successfully searching for accounts with a query."""
	mock_memory_manager.search_accounts.return_value = [fake_account]

	response = client.get("/account?q=Test&limit=5")

	assert response.status_code == status.HTTP_200_OK
	assert response.json()[0]["name"] == "Dr. Test"
	mock_memory_manager.search_accounts.assert_called_once_with("Test", limit=5)
	mock_memory_manager.get_all_accounts.assert_not_called()

def test_get_accounts_db_error(client: TestClient):
	"""Tests that a 500 error is returned if the database fails."""
	mock_memory_manager.get_all_accounts.side_effect = ActionFailed("DB connection lost")

	response = client.get("/account")

	assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
	assert response.json()["detail"] == "A database error occurred."


# --- Tests for POST /account ---

def test_create_account_success(client: TestClient):
	"""Tests successful creation of a new account."""
	new_account_id = "new_fake_id_123"
	mock_memory_manager.create_account.return_value = new_account_id
	mock_memory_manager.get_account.return_value = fake_account

	account_data = {"name": "Dr. Test", "role": "Doctor", "specialty": "Testing"}
	response = client.post("/account", json=account_data)

	assert response.status_code == status.HTTP_201_CREATED
	assert response.json()["name"] == "Dr. Test"
	mock_memory_manager.create_account.assert_called_once_with(
		name="Dr. Test", role="Doctor", specialty="Testing"
	)
	mock_memory_manager.get_account.assert_called_once_with(new_account_id)

def test_create_account_creation_fails(client: TestClient):
	"""Tests the case where the memory manager fails to return an ID."""
	mock_memory_manager.create_account.return_value = None

	account_data = {"name": "Dr. Fail", "role": "Doctor"}
	response = client.post("/account", json=account_data)

	assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
	assert response.json()["detail"] == "Failed to create account ID."

def test_create_account_not_found_after_creation(client: TestClient):
	"""Tests the edge case where the account can't be retrieved after creation."""
	new_account_id = "new_fake_id_123"
	mock_memory_manager.create_account.return_value = new_account_id
	mock_memory_manager.get_account.return_value = None # Simulate not found

	account_data = {"name": "Dr. Ghost", "role": "Doctor"}
	response = client.post("/account", json=account_data)

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

def test_create_account_action_failed(client: TestClient):
	"""Tests that a 400 error is returned for data-related creation failures."""
	error_message = "Account with this name already exists."
	mock_memory_manager.create_account.side_effect = ActionFailed(error_message)

	account_data = {"name": "Dr. Duplicate", "role": "Doctor"}
	response = client.post("/account", json=account_data)

	assert response.status_code == status.HTTP_400_BAD_REQUEST
	assert response.json()["detail"] == error_message


# --- Tests for GET /account/{account_id} ---

def test_get_account_by_id_success(client: TestClient):
	"""Tests successfully retrieving a single account by its ID."""
	mock_memory_manager.get_account.return_value = fake_account

	response = client.get(f"/account/{fake_account.id}")

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

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

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

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

def test_get_account_by_id_db_error(client: TestClient):
	"""Tests that a 500 error is returned if the database fails during retrieval."""
	mock_memory_manager.get_account.side_effect = ActionFailed("DB connection lost")

	response = client.get(f"/account/{fake_account.id}")

	assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
	assert response.json()["detail"] == "A database error occurred."