Skip to content

Commit b52ea28

Browse files
authored
Merge pull request #752 from tisnik/lcore-740-type-hints-for-endpoint-handlers
LCORE-740: type hints for endpoint handlers
2 parents 6e2c199 + d62afc2 commit b52ea28

File tree

8 files changed

+524
-278
lines changed

8 files changed

+524
-278
lines changed

tests/unit/app/endpoints/test_conversations.py

Lines changed: 199 additions & 106 deletions
Large diffs are not rendered by default.

tests/unit/app/endpoints/test_conversations_v2.py

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
"""Unit tests for the /conversations REST API endpoints."""
44

5-
from pytest_mock import MockerFixture
5+
from pytest_mock import MockerFixture, MockType
66
import pytest
77
from fastapi import HTTPException, status
88

@@ -122,7 +122,7 @@ def test_transform_message_with_empty_referenced_documents(self) -> None:
122122

123123

124124
@pytest.fixture
125-
def mock_configuration(mocker: MockerFixture):
125+
def mock_configuration(mocker: MockerFixture) -> MockType:
126126
"""Mock configuration with conversation cache."""
127127
mock_config = mocker.Mock()
128128
mock_cache = mocker.Mock()
@@ -133,27 +133,31 @@ def mock_configuration(mocker: MockerFixture):
133133
class TestCheckValidConversationId:
134134
"""Test cases for the check_valid_conversation_id function."""
135135

136-
def test_valid_conversation_id(self, mocker: MockerFixture):
136+
def test_valid_conversation_id(self, mocker: MockerFixture) -> None:
137137
"""Test with a valid conversation ID."""
138138
mocker.patch("app.endpoints.conversations_v2.check_suid", return_value=True)
139139
# Should not raise an exception
140140
check_valid_conversation_id(VALID_CONVERSATION_ID)
141141

142-
def test_invalid_conversation_id(self, mocker: MockerFixture):
142+
def test_invalid_conversation_id(self, mocker: MockerFixture) -> None:
143143
"""Test with an invalid conversation ID."""
144144
mocker.patch("app.endpoints.conversations_v2.check_suid", return_value=False)
145145

146146
with pytest.raises(HTTPException) as exc_info:
147147
check_valid_conversation_id(INVALID_CONVERSATION_ID)
148148

149149
assert exc_info.value.status_code == status.HTTP_400_BAD_REQUEST
150-
assert "Invalid conversation ID format" in exc_info.value.detail["response"]
150+
detail = exc_info.value.detail
151+
assert isinstance(detail, dict)
152+
assert "Invalid conversation ID format" in detail["response"]
151153

152154

153155
class TestCheckConversationExistence:
154156
"""Test cases for the check_conversation_existence function."""
155157

156-
def test_conversation_exists(self, mocker, mock_configuration):
158+
def test_conversation_exists(
159+
self, mocker: MockerFixture, mock_configuration: MockType
160+
) -> None:
157161
"""Test when conversation exists."""
158162
mock_configuration.conversation_cache.list.return_value = [
159163
mocker.Mock(conversation_id=VALID_CONVERSATION_ID)
@@ -163,7 +167,9 @@ def test_conversation_exists(self, mocker, mock_configuration):
163167
# Should not raise an exception
164168
check_conversation_existence("user_id", VALID_CONVERSATION_ID)
165169

166-
def test_conversation_not_exists(self, mocker, mock_configuration):
170+
def test_conversation_not_exists(
171+
self, mocker: MockerFixture, mock_configuration: MockType
172+
) -> None:
167173
"""Test when conversation does not exist."""
168174
mock_configuration.conversation_cache.list.return_value = []
169175
mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration)
@@ -172,14 +178,16 @@ def test_conversation_not_exists(self, mocker, mock_configuration):
172178
check_conversation_existence("user_id", VALID_CONVERSATION_ID)
173179

174180
assert exc_info.value.status_code == status.HTTP_404_NOT_FOUND
175-
assert "Conversation not found" in exc_info.value.detail["response"]
181+
detail = exc_info.value.detail
182+
assert isinstance(detail, dict)
183+
assert "Conversation not found" in detail["response"]
176184

177185

178186
class TestUpdateConversationEndpoint:
179187
"""Test cases for the PUT /conversations/{conversation_id} endpoint."""
180188

181189
@pytest.mark.asyncio
182-
async def test_configuration_not_loaded(self, mocker: MockerFixture):
190+
async def test_configuration_not_loaded(self, mocker: MockerFixture) -> None:
183191
"""Test the endpoint when configuration is not loaded."""
184192
mock_authorization_resolvers(mocker)
185193
mocker.patch("app.endpoints.conversations_v2.configuration", None)
@@ -197,8 +205,8 @@ async def test_configuration_not_loaded(self, mocker: MockerFixture):
197205

198206
@pytest.mark.asyncio
199207
async def test_invalid_conversation_id_format(
200-
self, mocker: MockerFixture, mock_configuration
201-
):
208+
self, mocker: MockerFixture, mock_configuration: MockType
209+
) -> None:
202210
"""Test the endpoint with an invalid conversation ID format."""
203211
mock_authorization_resolvers(mocker)
204212
mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration)
@@ -214,10 +222,14 @@ async def test_invalid_conversation_id_format(
214222
)
215223

216224
assert exc_info.value.status_code == status.HTTP_400_BAD_REQUEST
217-
assert "Invalid conversation ID format" in exc_info.value.detail["response"]
225+
detail = exc_info.value.detail
226+
assert isinstance(detail, dict)
227+
assert "Invalid conversation ID format" in detail["response"]
218228

219229
@pytest.mark.asyncio
220-
async def test_conversation_cache_not_configured(self, mocker: MockerFixture):
230+
async def test_conversation_cache_not_configured(
231+
self, mocker: MockerFixture
232+
) -> None:
221233
"""Test the endpoint when conversation cache is not configured."""
222234
mock_authorization_resolvers(mocker)
223235
mock_config = mocker.Mock()
@@ -235,14 +247,14 @@ async def test_conversation_cache_not_configured(self, mocker: MockerFixture):
235247
)
236248

237249
assert exc_info.value.status_code == status.HTTP_404_NOT_FOUND
238-
assert (
239-
"Conversation cache is not configured" in exc_info.value.detail["response"]
240-
)
250+
detail = exc_info.value.detail
251+
assert isinstance(detail, dict)
252+
assert "Conversation cache is not configured" in detail["response"]
241253

242254
@pytest.mark.asyncio
243255
async def test_conversation_not_found(
244-
self, mocker: MockerFixture, mock_configuration
245-
):
256+
self, mocker: MockerFixture, mock_configuration: MockType
257+
) -> None:
246258
"""Test the endpoint when conversation does not exist."""
247259
mock_authorization_resolvers(mocker)
248260
mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration)
@@ -259,10 +271,14 @@ async def test_conversation_not_found(
259271
)
260272

261273
assert exc_info.value.status_code == status.HTTP_404_NOT_FOUND
262-
assert "Conversation not found" in exc_info.value.detail["response"]
274+
detail = exc_info.value.detail
275+
assert isinstance(detail, dict)
276+
assert "Conversation not found" in detail["response"]
263277

264278
@pytest.mark.asyncio
265-
async def test_successful_update(self, mocker: MockerFixture, mock_configuration):
279+
async def test_successful_update(
280+
self, mocker: MockerFixture, mock_configuration: MockType
281+
) -> None:
266282
"""Test successful topic summary update."""
267283
mock_authorization_resolvers(mocker)
268284
mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration)

tests/unit/app/endpoints/test_metrics.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
"""Unit tests for the /metrics REST API endpoint."""
22

33
import pytest
4+
from pytest_mock import MockerFixture
45
from fastapi import Request
56

7+
from authentication.interface import AuthTuple
68
from app.endpoints.metrics import metrics_endpoint_handler
79
from tests.unit.utils.auth_helpers import mock_authorization_resolvers
810

911

1012
@pytest.mark.asyncio
11-
async def test_metrics_endpoint(mocker):
13+
async def test_metrics_endpoint(mocker: MockerFixture) -> None:
1214
"""Test the metrics endpoint handler."""
1315
mock_authorization_resolvers(mocker)
1416

@@ -20,7 +22,7 @@ async def test_metrics_endpoint(mocker):
2022
"type": "http",
2123
}
2224
)
23-
auth = ("test_user", "token", {})
25+
auth: AuthTuple = ("test_user", "token", {})
2426
response = await metrics_endpoint_handler(auth=auth, request=request)
2527
assert response is not None
2628
assert response.status_code == 200

tests/unit/app/endpoints/test_models.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
"""Unit tests for the /models REST API endpoint."""
22

3+
from typing import Any
34
import pytest
45

56
from fastapi import HTTPException, Request, status
7+
from pytest_mock import MockerFixture
68

79
from llama_stack_client import APIConnectionError
810

11+
from authentication.interface import AuthTuple
912
from app.endpoints.models import models_endpoint_handler
1013
from configuration import AppConfig
1114
from tests.unit.utils.auth_helpers import mock_authorization_resolvers
1215

1316

1417
@pytest.mark.asyncio
15-
async def test_models_endpoint_handler_configuration_not_loaded(mocker):
18+
async def test_models_endpoint_handler_configuration_not_loaded(
19+
mocker: MockerFixture,
20+
) -> None:
1621
"""Test the models endpoint handler if configuration is not loaded."""
1722
mock_authorization_resolvers(mocker)
1823

@@ -29,7 +34,7 @@ async def test_models_endpoint_handler_configuration_not_loaded(mocker):
2934
"headers": [(b"authorization", b"Bearer invalid-token")],
3035
}
3136
)
32-
auth = ("user_id", "user_name", "token")
37+
auth: AuthTuple = ("user_id", "user_name", "token")
3338

3439
with pytest.raises(HTTPException) as e:
3540
await models_endpoint_handler(request=request, auth=auth)
@@ -38,12 +43,14 @@ async def test_models_endpoint_handler_configuration_not_loaded(mocker):
3843

3944

4045
@pytest.mark.asyncio
41-
async def test_models_endpoint_handler_improper_llama_stack_configuration(mocker):
46+
async def test_models_endpoint_handler_improper_llama_stack_configuration(
47+
mocker: MockerFixture,
48+
) -> None:
4249
"""Test the models endpoint handler if Llama Stack configuration is not proper."""
4350
mock_authorization_resolvers(mocker)
4451

4552
# configuration for tests
46-
config_dict = {
53+
config_dict: dict[str, Any] = {
4754
"name": "test",
4855
"service": {
4956
"host": "localhost",
@@ -80,20 +87,22 @@ async def test_models_endpoint_handler_improper_llama_stack_configuration(mocker
8087
"headers": [(b"authorization", b"Bearer invalid-token")],
8188
}
8289
)
83-
auth = ("test_user", "token", {})
90+
auth: AuthTuple = ("test_user", "token", {})
8491
with pytest.raises(HTTPException) as e:
8592
await models_endpoint_handler(request=request, auth=auth)
8693
assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
8794
assert e.detail["response"] == "Llama stack is not configured"
8895

8996

9097
@pytest.mark.asyncio
91-
async def test_models_endpoint_handler_configuration_loaded(mocker):
98+
async def test_models_endpoint_handler_configuration_loaded(
99+
mocker: MockerFixture,
100+
) -> None:
92101
"""Test the models endpoint handler if configuration is loaded."""
93102
mock_authorization_resolvers(mocker)
94103

95104
# configuration for tests
96-
config_dict = {
105+
config_dict: dict[str, Any] = {
97106
"name": "foo",
98107
"service": {
99108
"host": "localhost",
@@ -124,7 +133,7 @@ async def test_models_endpoint_handler_configuration_loaded(mocker):
124133
"headers": [(b"authorization", b"Bearer invalid-token")],
125134
}
126135
)
127-
auth = ("test_user", "token", {})
136+
auth: AuthTuple = ("test_user", "token", {})
128137

129138
with pytest.raises(HTTPException) as e:
130139
await models_endpoint_handler(request=request, auth=auth)
@@ -133,12 +142,14 @@ async def test_models_endpoint_handler_configuration_loaded(mocker):
133142

134143

135144
@pytest.mark.asyncio
136-
async def test_models_endpoint_handler_unable_to_retrieve_models_list(mocker):
145+
async def test_models_endpoint_handler_unable_to_retrieve_models_list(
146+
mocker: MockerFixture,
147+
) -> None:
137148
"""Test the models endpoint handler if configuration is loaded."""
138149
mock_authorization_resolvers(mocker)
139150

140151
# configuration for tests
141-
config_dict = {
152+
config_dict: dict[str, Any] = {
142153
"name": "foo",
143154
"service": {
144155
"host": "localhost",
@@ -177,18 +188,20 @@ async def test_models_endpoint_handler_unable_to_retrieve_models_list(mocker):
177188
"headers": [(b"authorization", b"Bearer invalid-token")],
178189
}
179190
)
180-
auth = ("test_user", "token", {})
191+
auth: AuthTuple = ("test_user", "token", {})
181192
response = await models_endpoint_handler(request=request, auth=auth)
182193
assert response is not None
183194

184195

185196
@pytest.mark.asyncio
186-
async def test_models_endpoint_llama_stack_connection_error(mocker):
197+
async def test_models_endpoint_llama_stack_connection_error(
198+
mocker: MockerFixture,
199+
) -> None:
187200
"""Test the model endpoint when LlamaStack connection fails."""
188201
mock_authorization_resolvers(mocker)
189202

190203
# configuration for tests
191-
config_dict = {
204+
config_dict: dict[str, Any] = {
192205
"name": "foo",
193206
"service": {
194207
"host": "localhost",
@@ -214,7 +227,7 @@ async def test_models_endpoint_llama_stack_connection_error(mocker):
214227
# mock AsyncLlamaStackClientHolder to raise APIConnectionError
215228
# when models.list() method is called
216229
mock_client = mocker.AsyncMock()
217-
mock_client.models.list.side_effect = APIConnectionError(request=None)
230+
mock_client.models.list.side_effect = APIConnectionError(request=None) # type: ignore
218231
mock_client_holder = mocker.patch(
219232
"app.endpoints.models.AsyncLlamaStackClientHolder"
220233
)
@@ -229,7 +242,7 @@ async def test_models_endpoint_llama_stack_connection_error(mocker):
229242
"headers": [(b"authorization", b"Bearer invalid-token")],
230243
}
231244
)
232-
auth = ("test_user", "token", {})
245+
auth: AuthTuple = ("test_user", "token", {})
233246

234247
with pytest.raises(HTTPException) as e:
235248
await models_endpoint_handler(request=request, auth=auth)

0 commit comments

Comments
 (0)