Skip to content

Commit 692f5ff

Browse files
authored
Merge pull request lightspeed-core#930 from tisnik/lcore-1126-updated-docstrings-in-unit-tests
LCORE-1126: updated docstrings in unit tests
2 parents a6fb210 + 671adb2 commit 692f5ff

17 files changed

Lines changed: 1009 additions & 44 deletions

tests/unit/app/endpoints/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ Unit tests for the /query (v2) REST API endpoint using Responses API.
4242
## [test_rags.py](test_rags.py)
4343
Unit tests for the /rags REST API endpoints.
4444

45+
## [test_rlsapi_v1.py](test_rlsapi_v1.py)
46+
Unit tests for the rlsapi v1 /infer REST API endpoint.
47+
4548
## [test_root.py](test_root.py)
4649
Unit tests for the / endpoint handler.
4750

tests/unit/app/endpoints/test_authorized.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,19 @@ async def test_authorized_unauthorized() -> None:
4747

4848
@pytest.mark.asyncio
4949
async def test_authorized_dependency_unauthorized() -> None:
50-
"""Test that auth dependency raises HTTPException with 403 for unauthorized access."""
50+
"""Test that auth dependency raises HTTPException with 403 for unauthorized access.
51+
52+
Verify extract_user_token raises HTTPException with status code 401 and the
53+
expected detail for missing or malformed Authorization headers.
54+
55+
Checks two scenarios:
56+
- Missing Authorization header: HTTPException.status_code == 401,
57+
detail["response"] == "Missing or invalid credentials provided by
58+
client", detail["cause"] == "No Authorization header found".
59+
- Invalid Authorization format: HTTPException.status_code == 401,
60+
detail["response"] == "Missing or invalid credentials provided by
61+
client", detail["cause"] == "No token found in Authorization header".
62+
"""
5163
# Test the auth utility function that would be called by auth dependencies
5264
# This simulates the unauthorized scenario that would prevent the handler from being called
5365

tests/unit/app/endpoints/test_conversations.py

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@
3434

3535
@pytest.fixture
3636
def dummy_request() -> Request:
37-
"""Mock request object for testing."""
37+
"""Mock request object for testing.
38+
39+
Create a mock FastAPI Request configured for tests with full authorization.
40+
41+
The returned Request has state.authorized_actions set to a set containing
42+
every member of Action.
43+
"""
3844
request = Request(
3945
scope={
4046
"type": "http",
@@ -56,7 +62,35 @@ def create_mock_conversation(
5662
last_used_provider: str,
5763
topic_summary: Optional[str] = None,
5864
) -> MockType:
59-
"""Helper function to create a mock conversation object with all required attributes."""
65+
"""Helper function to create a mock conversation object with all required attributes.
66+
67+
Create a mock conversation object with the attributes used by the
68+
conversations list and detail tests.
69+
70+
The returned mock has the following attributes:
71+
- id: the conversation identifier (string)
72+
- created_at.isoformat(): returns the provided created_at string
73+
- last_message_at.isoformat(): returns the provided last_message_at string
74+
- message_count: number of messages in the conversation
75+
- last_used_model: model identifier last used in the conversation
76+
- last_used_provider: provider identifier last used in the conversation
77+
- topic_summary: optional topic summary (may be None or empty string)
78+
79+
Parameters:
80+
mocker (MockerFixture): pytest mocker fixture used to build the mock object.
81+
conversation_id (str): Conversation identifier to assign to the mock.
82+
created_at (str): ISO-formatted created-at timestamp to be returned by
83+
created_at.isoformat().
84+
last_message_at (str): ISO-formatted last-message timestamp to be
85+
returned by last_message_at.isoformat().
86+
message_count (int): Message count to assign to the mock.
87+
last_used_model (str): Last used model string to assign to the mock.
88+
last_used_provider (str): Last used provider string to assign to the mock.
89+
topic_summary (Optional[str]): Optional topic summary to assign to the mock.
90+
91+
Returns:
92+
mock_conversation: A mock object configured with the above attributes.
93+
"""
6094
mock_conversation = mocker.Mock()
6195
mock_conversation.id = conversation_id
6296
mock_conversation.created_at = mocker.Mock()
@@ -73,7 +107,20 @@ def create_mock_conversation(
73107
def mock_database_session(
74108
mocker: MockerFixture, query_result: Optional[list[MockType]] = None
75109
) -> MockType:
76-
"""Helper function to mock get_session with proper context manager support."""
110+
"""Helper function to mock get_session with proper context manager support.
111+
112+
Create and patch a mocked database session and a context-manager-compatible get_session.
113+
114+
Parameters:
115+
mocker (pytest.MockerFixture): Fixture used to create and patch mocks.
116+
query_result (Optional[list]): If provided, configures the
117+
session.query().all() and session.query().filter_by().all() to return
118+
this list.
119+
120+
Returns:
121+
Mock: The mocked session object that will be yielded by the patched
122+
get_session context manager.
123+
"""
77124
mock_session = mocker.Mock()
78125
if query_result is not None:
79126
# Mock both the filtered and unfiltered query paths
@@ -94,7 +141,16 @@ def mock_database_session(
94141

95142
@pytest.fixture(name="setup_configuration")
96143
def setup_configuration_fixture() -> AppConfig:
97-
"""Set up configuration for tests."""
144+
"""Set up configuration for tests.
145+
146+
Create an AppConfig prepopulated with test-friendly default settings.
147+
148+
Returns:
149+
AppConfig: An AppConfig instance initialized from a dictionary
150+
containing defaults suitable for tests (local service host/port,
151+
disabled auth and user-data collection, test Llama Stack API key and
152+
URL, and single worker).
153+
"""
98154
config_dict: dict[str, Any] = {
99155
"name": "test",
100156
"service": {
@@ -123,7 +179,29 @@ def setup_configuration_fixture() -> AppConfig:
123179

124180
@pytest.fixture(name="mock_session_data")
125181
def mock_session_data_fixture() -> dict[str, Any]:
126-
"""Create mock session data for testing."""
182+
"""Create mock session data for testing.
183+
184+
Provide a representative mock session data payload used by tests to
185+
simulate a conversation session.
186+
187+
The returned dictionary contains:
188+
- session_id: conversation identifier.
189+
- session_name: human-readable session name.
190+
- started_at: ISO 8601 timestamp when the session started.
191+
- turns: list of turn objects; each turn includes:
192+
- turn_id: identifier for the turn.
193+
- input_messages: list of input message objects with `content`, `role`,
194+
and optional `context`.
195+
- output_message: assistant response object with `content`, `role`, and
196+
auxiliary fields (e.g., `stop_reason`, `tool_calls`) that tests
197+
expect to be filtered by simplification logic.
198+
- started_at / completed_at: ISO 8601 timestamps for the turn.
199+
- steps: detailed internal steps included to verify they are removed by simplification.
200+
201+
Returns:
202+
dict: A mock session data structure matching the shape produced by the
203+
Llama Stack client for use in unit tests.
204+
"""
127205
return {
128206
"session_id": VALID_CONVERSATION_ID,
129207
"session_name": "test-session",
@@ -165,7 +243,17 @@ def mock_session_data_fixture() -> dict[str, Any]:
165243

166244
@pytest.fixture(name="expected_chat_history")
167245
def expected_chat_history_fixture() -> list[dict[str, Any]]:
168-
"""Create expected simplified chat history for testing."""
246+
"""Create expected simplified chat history for testing.
247+
248+
Expected simplified chat history used by tests.
249+
250+
Returns:
251+
list[dict[str, Any]]: A list of conversation turns. Each turn contains:
252+
- messages: list of message dicts with `content` (str) and `type`
253+
(`"user"` or `"assistant"`)
254+
- started_at: ISO 8601 UTC timestamp string for the turn start
255+
- completed_at: ISO 8601 UTC timestamp string for the turn end
256+
"""
169257
return [
170258
{
171259
"messages": [
@@ -188,7 +276,14 @@ def expected_chat_history_fixture() -> list[dict[str, Any]]:
188276

189277
@pytest.fixture(name="mock_conversation")
190278
def mock_conversation_fixture() -> UserConversation:
191-
"""Create a mock UserConversation object for testing."""
279+
"""Create a mock UserConversation object for testing.
280+
281+
Returns:
282+
mock_conv (UserConversation): A UserConversation initialized with
283+
VALID_CONVERSATION_ID, user_id set to "another_user", message_count 2,
284+
last_used_model "mock-model", last_used_provider "mock-provider", and
285+
topic_summary "Mock topic".
286+
"""
192287
mock_conv = UserConversation()
193288
mock_conv.id = VALID_CONVERSATION_ID
194289
mock_conv.user_id = "another_user" # Different from test auth
@@ -366,7 +461,15 @@ async def test_llama_stack_not_found_error(
366461
setup_configuration: AppConfig,
367462
dummy_request: Request,
368463
) -> None:
369-
"""Test the endpoint when LlamaStack returns NotFoundError."""
464+
"""Test the endpoint when LlamaStack returns NotFoundError.
465+
466+
Verify the GET /conversations/{conversation_id} handler raises an HTTP
467+
404 when the Llama Stack client reports the session as not found.
468+
469+
Asserts that the raised HTTPException contains a response message
470+
indicating the conversation was not found and a cause that includes
471+
"does not exist" and the conversation ID.
472+
"""
370473
mock_authorization_resolvers(mocker)
371474
mocker.patch("app.endpoints.conversations.configuration", setup_configuration)
372475
mocker.patch("app.endpoints.conversations.check_suid", return_value=True)

tests/unit/app/endpoints/test_conversations_v2.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,16 @@ def test_transform_message_with_empty_referenced_documents(self) -> None:
135135

136136
@pytest.fixture
137137
def mock_configuration(mocker: MockerFixture) -> MockType:
138-
"""Mock configuration with conversation cache."""
138+
"""Mock configuration with conversation cache.
139+
140+
Create a mocked configuration object with a mocked `conversation_cache` attribute.
141+
142+
Parameters:
143+
mocker (pytest.MockFixture): The pytest-mock fixture used to create mocks.
144+
145+
Returns:
146+
Mock: A mock configuration object whose `conversation_cache` attribute is a mock.
147+
"""
139148
mock_config = mocker.Mock()
140149
mock_cache = mocker.Mock()
141150
mock_config.conversation_cache = mock_cache
@@ -303,7 +312,15 @@ async def test_successful_retrieval_empty_list(
303312
async def test_with_skip_userid_check(
304313
self, mocker: MockerFixture, mock_configuration: MockType
305314
) -> None:
306-
"""Test the endpoint with skip_userid_check flag."""
315+
"""Test the endpoint with skip_userid_check flag.
316+
317+
Verify the conversations list handler forwards the skip_userid_check
318+
flag from the auth tuple to the conversation cache.
319+
320+
Sets up a mocked configuration and auth tuple with the skip flag set to
321+
True, invokes the handler, and asserts that `conversation_cache.list`
322+
is called with the user ID and `True`.
323+
"""
307324
mock_authorization_resolvers(mocker)
308325
mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration)
309326
mock_configuration.conversation_cache.list.return_value = []
@@ -375,7 +392,18 @@ async def test_invalid_conversation_id_format(
375392
async def test_conversation_cache_not_configured(
376393
self, mocker: MockerFixture
377394
) -> None:
378-
"""Test the endpoint when conversation cache is not configured."""
395+
"""Test the endpoint when conversation cache is not configured.
396+
397+
Verify the conversation GET endpoint raises an HTTP 500 error when the
398+
conversation cache is not configured.
399+
400+
Patches the application configuration so
401+
`conversation_cache_configuration.type` is None and ensures
402+
`check_suid` returns True, then calls
403+
`get_conversation_endpoint_handler` and asserts that it raises an
404+
`HTTPException` with status code 500 and a response detail containing
405+
"Conversation cache not configured".
406+
"""
379407
mock_authorization_resolvers(mocker)
380408
mock_config = mocker.Mock()
381409
mock_config.conversation_cache_configuration = mocker.Mock()
@@ -632,7 +660,17 @@ async def test_unsuccessful_deletion(
632660
async def test_with_skip_userid_check(
633661
self, mocker: MockerFixture, mock_configuration: MockType
634662
) -> None:
635-
"""Test the endpoint with skip_userid_check flag."""
663+
"""Test the endpoint with skip_userid_check flag.
664+
665+
Verifies that providing an auth tuple with the skip-userid flag set
666+
causes the conversation delete handler to call the cache delete method
667+
with the skip flag.
668+
669+
This test patches configuration and SUID validation, supplies an auth
670+
tuple where the third element is True, invokes
671+
delete_conversation_endpoint_handler, and asserts the cache.delete was
672+
called with (user_id, conversation_id, True).
673+
"""
636674
mock_authorization_resolvers(mocker)
637675
mocker.patch("app.endpoints.conversations_v2.configuration", mock_configuration)
638676
mocker.patch("app.endpoints.conversations_v2.check_suid", return_value=True)

tests/unit/app/endpoints/test_health.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,18 @@ class TestGetProvidersHealthStatuses:
126126
"""Test cases for the get_providers_health_statuses function."""
127127

128128
async def test_get_providers_health_statuses(self, mocker: MockerFixture) -> None:
129-
"""Test get_providers_health_statuses with healthy providers."""
129+
"""Test get_providers_health_statuses with healthy providers.
130+
131+
Verify get_providers_health_statuses returns a ProviderHealthStatus
132+
entry for each provider reported by the client.
133+
134+
Mocks an AsyncLlamaStack client whose providers.list() returns three
135+
providers with distinct health dicts, then asserts the function
136+
produces three results with:
137+
- provider1: status OK, message "All good"
138+
- provider2: status NOT_IMPLEMENTED, message "Provider does not implement health check"
139+
- unhealthy_provider: status ERROR, message "Connection failed"
140+
"""
130141
# Mock the imports
131142
mock_lsc = mocker.patch("client.AsyncLlamaStackClientHolder.get_client")
132143

tests/unit/app/endpoints/test_info.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,18 @@ async def test_info_endpoint(mocker: MockerFixture) -> None:
7777

7878
@pytest.mark.asyncio
7979
async def test_info_endpoint_connection_error(mocker: MockerFixture) -> None:
80-
"""Test the info endpoint handler."""
80+
"""Test the info endpoint handler.
81+
82+
Verify that info_endpoint_handler raises an HTTPException with
83+
status 503 when the LlamaStack client cannot connect.
84+
85+
Sets up application configuration and patches the LlamaStack
86+
client so that calling its version inspection raises an
87+
APIConnectionError, then asserts the raised HTTPException has
88+
status code 503 and a detail payload containing a "response" of
89+
"Service unavailable" and a "cause" that includes "Unable to
90+
connect to Llama Stack".
91+
"""
8192
mock_authorization_resolvers(mocker)
8293

8394
# configuration for tests

tests/unit/app/endpoints/test_models.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,17 @@ async def test_models_endpoint_handler_configuration_not_loaded(
4444
async def test_models_endpoint_handler_configuration_loaded(
4545
mocker: MockerFixture,
4646
) -> None:
47-
"""Test the models endpoint handler if configuration is loaded."""
47+
"""Test the models endpoint handler if configuration is loaded.
48+
49+
Verify the models endpoint raises HTTP 503 when configuration is loaded but
50+
the Llama Stack client cannot connect.
51+
52+
Loads an AppConfig from a test dictionary, patches the endpoint's
53+
configuration and AsyncLlamaStackClientHolder so that get_client raises
54+
APIConnectionError, issues a request with an authorization header, and
55+
asserts that calling the handler raises an HTTPException with status 503
56+
and a detail response of "Unable to connect to Llama Stack".
57+
"""
4858
mock_authorization_resolvers(mocker)
4959

5060
# configuration for tests

0 commit comments

Comments
 (0)