Skip to content

Commit 5d280c9

Browse files
authored
Adding Lcore 714 health endpoint (#744)
* Adding health endpoint integration tests. * Removing liveness probe endpoint test when config is not loaded. * Removing unused imports. * Adding test for provider health statuses. * Adding test for provider health statuses. * Fixing pylinter imports order.
1 parent 9cce972 commit 5d280c9

File tree

2 files changed

+190
-1
lines changed

2 files changed

+190
-1
lines changed

tests/integration/conftest.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Generator
55

66
import pytest
7-
from fastapi import Request
7+
from fastapi import Request, Response
88

99
from sqlalchemy import create_engine
1010
from sqlalchemy.orm import sessionmaker, Session
@@ -117,6 +117,12 @@ def test_request_fixture() -> Request:
117117
)
118118

119119

120+
@pytest.fixture(name="test_response")
121+
def test_response_fixture() -> Response:
122+
"""Create a test FastAPI Response object with proper scope."""
123+
return Response(content="", status_code=200, media_type="application/json")
124+
125+
120126
@pytest.fixture(name="test_auth")
121127
async def test_auth_fixture(test_request: Request) -> AuthTuple:
122128
"""Create authentication using real noop auth module.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""Integration tests for the /health endpoint."""
2+
3+
from typing import Generator, Any
4+
import pytest
5+
from pytest_mock import MockerFixture, AsyncMockType
6+
from llama_stack.providers.datatypes import HealthStatus
7+
8+
from fastapi import Response, status
9+
from authentication.interface import AuthTuple
10+
11+
from configuration import AppConfig
12+
from app.endpoints.health import (
13+
liveness_probe_get_method,
14+
readiness_probe_get_method,
15+
get_providers_health_statuses,
16+
)
17+
18+
19+
@pytest.fixture(name="mock_llama_stack_client_health")
20+
def mock_llama_stack_client_fixture(
21+
mocker: MockerFixture,
22+
) -> Generator[Any, None, None]:
23+
"""Mock only the external Llama Stack client.
24+
25+
This is the only external dependency we mock for integration tests,
26+
as it represents an external service call.
27+
"""
28+
mock_holder_class = mocker.patch("app.endpoints.health.AsyncLlamaStackClientHolder")
29+
30+
mock_client = mocker.AsyncMock()
31+
# Mock the version endpoint to return a known version
32+
mock_client.inspect.version.return_value = []
33+
34+
# Create a mock holder instance
35+
mock_holder_instance = mock_holder_class.return_value
36+
mock_holder_instance.get_client.return_value = mock_client
37+
38+
yield mock_client
39+
40+
41+
@pytest.mark.asyncio
42+
async def test_health_liveness(
43+
test_config: AppConfig,
44+
test_auth: AuthTuple,
45+
) -> None:
46+
"""Test that liveness probe endpoint is alive
47+
48+
This integration test verifies:
49+
- Endpoint handler integrates with configuration system
50+
- Real noop authentication is used
51+
- Response structure matches expected format
52+
53+
Args:
54+
test_config: Loads test configuration
55+
test_auth: noop authentication tuple
56+
"""
57+
_ = test_config
58+
59+
response = await liveness_probe_get_method(auth=test_auth)
60+
61+
# Verify that service is alive
62+
assert response.alive is True
63+
64+
65+
@pytest.mark.asyncio
66+
async def test_health_readiness_provider_statuses(
67+
mock_llama_stack_client_health: AsyncMockType,
68+
mocker: MockerFixture,
69+
) -> None:
70+
"""Test that get_providers_health_statuses correctly retrieves and returns
71+
provider health statuses.
72+
73+
This integration test verifies:
74+
- Function correctly retrieves provider list from Llama Stack client
75+
- Both healthy and unhealthy providers are properly processed
76+
- Provider health status, ID, and error messages are correctly mapped
77+
- Multiple providers with different health states are handled correctly
78+
79+
Args:
80+
mock_llama_stack_client_health: Mocked Llama Stack client
81+
mocker: pytest-mock fixture for creating mock objects
82+
"""
83+
# Arrange: Set up mock provider list with mixed health statuses
84+
mock_llama_stack_client_health.providers.list.return_value = [
85+
mocker.Mock(
86+
provider_id="unhealthy-provider-1",
87+
health={
88+
"status": HealthStatus.ERROR.value,
89+
"message": "Database connection failed",
90+
},
91+
),
92+
mocker.Mock(
93+
provider_id="unhealthy-provider-2",
94+
health={
95+
"status": HealthStatus.ERROR.value,
96+
"message": "Service unavailable",
97+
},
98+
),
99+
mocker.Mock(
100+
provider_id="healthy-provider", health={"status": "ok", "message": ""}
101+
),
102+
]
103+
104+
# Call the function to retrieve provider health statuses
105+
result = await get_providers_health_statuses()
106+
107+
# Verify providers
108+
assert result[0].provider_id == "unhealthy-provider-1"
109+
assert result[0].status == "Error"
110+
assert result[0].message == "Database connection failed"
111+
112+
assert result[1].provider_id == "unhealthy-provider-2"
113+
assert result[1].status == "Error"
114+
assert result[1].message == "Service unavailable"
115+
116+
assert result[2].provider_id == "healthy-provider"
117+
assert result[2].status == "ok"
118+
assert result[2].message == ""
119+
120+
121+
@pytest.mark.asyncio
122+
async def test_health_readiness_client_error(
123+
test_response: Response,
124+
test_auth: AuthTuple,
125+
) -> None:
126+
"""Test that readiness probe endpoint handles uninitialized client gracefully.
127+
128+
This integration test verifies:
129+
- Endpoint handles missing client initialization gracefully
130+
- Error is caught and returned as proper health status
131+
- Service returns 503 status code for unhealthy state
132+
- Error message includes details about initialization failure
133+
134+
Args:
135+
test_response: FastAPI response object
136+
test_auth: noop authentication tuple
137+
"""
138+
result = await readiness_probe_get_method(auth=test_auth, response=test_response)
139+
140+
# Verify HTTP status code is 503 (Service Unavailable)
141+
assert test_response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
142+
143+
# Verify that service returns error response when client not initialized
144+
assert result.ready is False
145+
assert "Providers not healthy" in result.reason
146+
assert "unknown" in result.reason
147+
148+
# Verify the response includes provider error details
149+
assert len(result.providers) == 1
150+
assert result.providers[0].provider_id == "unknown"
151+
assert result.providers[0].status == "Error"
152+
assert (
153+
"AsyncLlamaStackClient has not been initialised" in result.providers[0].message
154+
)
155+
156+
157+
@pytest.mark.asyncio
158+
async def test_health_readiness(
159+
mock_llama_stack_client_health: AsyncMockType,
160+
test_response: Response,
161+
test_auth: AuthTuple,
162+
) -> None:
163+
"""Test that readiness probe endpoint returns readiness status.
164+
165+
This integration test verifies:
166+
- Endpoint handler integrates with configuration system
167+
- Configuration values are correctly accessed
168+
- Real noop authentication is used
169+
- Response structure matches expected format
170+
171+
Args:
172+
mock_llama_stack_client_health: Mocked Llama Stack client
173+
test_response: FastAPI response object
174+
test_auth: noop authentication tuple
175+
"""
176+
_ = mock_llama_stack_client_health
177+
178+
result = await readiness_probe_get_method(auth=test_auth, response=test_response)
179+
180+
# Verify that service returns readiness response
181+
assert result.ready is True
182+
assert result.reason == "All providers are healthy"
183+
assert result.providers is not None

0 commit comments

Comments
 (0)