Skip to content

Commit 7b90e0e

Browse files
authored
test: suppress expected error logs in SSE test (#3886)
Our unit test outputs are filled with all kinds of obscene logs. This makes it really hard to spot real issues quickly. The problem is that these logs are necessary to output at the given logging level when the server is operating normally. It's just that we don't want to see some of them (especially the noisy ones) during tests. This PR begins the cleanup. We pytest's caplog fixture to for suppression.
1 parent f8eaa40 commit 7b90e0e

File tree

5 files changed

+60
-17
lines changed

5 files changed

+60
-17
lines changed

tests/unit/conftest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
# This source code is licensed under the terms described in the LICENSE file in
55
# the root directory of this source tree.
66

7+
import logging # allow-direct-logging
78
import os
89
import warnings
910

11+
import pytest
12+
1013

1114
def pytest_sessionstart(session) -> None:
1215
if "LLAMA_STACK_LOGGING" not in os.environ:
@@ -17,4 +20,10 @@ def pytest_sessionstart(session) -> None:
1720
warnings.filterwarnings("ignore", category=PendingDeprecationWarning)
1821

1922

23+
@pytest.fixture(autouse=True)
24+
def suppress_httpx_logs(caplog):
25+
"""Suppress httpx INFO logs for all unit tests"""
26+
caplog.set_level(logging.WARNING, logger="httpx")
27+
28+
2029
pytest_plugins = ["tests.unit.fixtures"]

tests/unit/server/test_auth.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import base64
88
import json
9+
import logging # allow-direct-logging
910
from unittest.mock import AsyncMock, Mock, patch
1011

1112
import pytest
@@ -27,6 +28,13 @@
2728
)
2829

2930

31+
@pytest.fixture
32+
def suppress_auth_errors(caplog):
33+
"""Suppress expected ERROR/WARNING logs for tests that deliberately trigger authentication errors"""
34+
caplog.set_level(logging.CRITICAL, logger="llama_stack.core.server.auth")
35+
caplog.set_level(logging.CRITICAL, logger="llama_stack.core.server.auth_providers")
36+
37+
3038
class MockResponse:
3139
def __init__(self, status_code, json_data):
3240
self.status_code = status_code
@@ -237,20 +245,20 @@ def test_valid_http_authentication(http_client, valid_api_key):
237245

238246

239247
@patch("httpx.AsyncClient.post", new=mock_post_failure)
240-
def test_invalid_http_authentication(http_client, invalid_api_key):
248+
def test_invalid_http_authentication(http_client, invalid_api_key, suppress_auth_errors):
241249
response = http_client.get("/test", headers={"Authorization": f"Bearer {invalid_api_key}"})
242250
assert response.status_code == 401
243251
assert "Authentication failed" in response.json()["error"]["message"]
244252

245253

246254
@patch("httpx.AsyncClient.post", new=mock_post_exception)
247-
def test_http_auth_service_error(http_client, valid_api_key):
255+
def test_http_auth_service_error(http_client, valid_api_key, suppress_auth_errors):
248256
response = http_client.get("/test", headers={"Authorization": f"Bearer {valid_api_key}"})
249257
assert response.status_code == 401
250258
assert "Authentication service error" in response.json()["error"]["message"]
251259

252260

253-
def test_http_auth_request_payload(http_client, valid_api_key, mock_auth_endpoint):
261+
def test_http_auth_request_payload(http_client, valid_api_key, mock_auth_endpoint, suppress_auth_errors):
254262
with patch("httpx.AsyncClient.post") as mock_post:
255263
mock_response = MockResponse(200, {"message": "Authentication successful"})
256264
mock_post.return_value = mock_response
@@ -420,7 +428,7 @@ def test_valid_oauth2_authentication(oauth2_client, jwt_token_valid, mock_jwks_u
420428

421429

422430
@patch("httpx.AsyncClient.get", new=mock_jwks_response)
423-
def test_invalid_oauth2_authentication(oauth2_client, invalid_token):
431+
def test_invalid_oauth2_authentication(oauth2_client, invalid_token, suppress_auth_errors):
424432
response = oauth2_client.get("/test", headers={"Authorization": f"Bearer {invalid_token}"})
425433
assert response.status_code == 401
426434
assert "Invalid JWT token" in response.json()["error"]["message"]
@@ -465,7 +473,7 @@ def oauth2_client_with_jwks_token(oauth2_app_with_jwks_token):
465473

466474

467475
@patch("httpx.AsyncClient.get", new=mock_auth_jwks_response)
468-
def test_oauth2_with_jwks_token_expected(oauth2_client, jwt_token_valid):
476+
def test_oauth2_with_jwks_token_expected(oauth2_client, jwt_token_valid, suppress_auth_errors):
469477
response = oauth2_client.get("/test", headers={"Authorization": f"Bearer {jwt_token_valid}"})
470478
assert response.status_code == 401
471479

@@ -726,21 +734,21 @@ def test_valid_introspection_authentication(introspection_client, valid_api_key)
726734

727735

728736
@patch("httpx.AsyncClient.post", new=mock_introspection_inactive)
729-
def test_inactive_introspection_authentication(introspection_client, invalid_api_key):
737+
def test_inactive_introspection_authentication(introspection_client, invalid_api_key, suppress_auth_errors):
730738
response = introspection_client.get("/test", headers={"Authorization": f"Bearer {invalid_api_key}"})
731739
assert response.status_code == 401
732740
assert "Token not active" in response.json()["error"]["message"]
733741

734742

735743
@patch("httpx.AsyncClient.post", new=mock_introspection_invalid)
736-
def test_invalid_introspection_authentication(introspection_client, invalid_api_key):
744+
def test_invalid_introspection_authentication(introspection_client, invalid_api_key, suppress_auth_errors):
737745
response = introspection_client.get("/test", headers={"Authorization": f"Bearer {invalid_api_key}"})
738746
assert response.status_code == 401
739747
assert "Not JSON" in response.json()["error"]["message"]
740748

741749

742750
@patch("httpx.AsyncClient.post", new=mock_introspection_failed)
743-
def test_failed_introspection_authentication(introspection_client, invalid_api_key):
751+
def test_failed_introspection_authentication(introspection_client, invalid_api_key, suppress_auth_errors):
744752
response = introspection_client.get("/test", headers={"Authorization": f"Bearer {invalid_api_key}"})
745753
assert response.status_code == 401
746754
assert "Token introspection failed: 500" in response.json()["error"]["message"]
@@ -957,20 +965,22 @@ def test_valid_kubernetes_auth_authentication(kubernetes_auth_client, valid_toke
957965

958966

959967
@patch("httpx.AsyncClient.post", new=mock_kubernetes_selfsubjectreview_failure)
960-
def test_invalid_kubernetes_auth_authentication(kubernetes_auth_client, invalid_token):
968+
def test_invalid_kubernetes_auth_authentication(kubernetes_auth_client, invalid_token, suppress_auth_errors):
961969
response = kubernetes_auth_client.get("/test", headers={"Authorization": f"Bearer {invalid_token}"})
962970
assert response.status_code == 401
963971
assert "Invalid token" in response.json()["error"]["message"]
964972

965973

966974
@patch("httpx.AsyncClient.post", new=mock_kubernetes_selfsubjectreview_http_error)
967-
def test_kubernetes_auth_http_error(kubernetes_auth_client, valid_token):
975+
def test_kubernetes_auth_http_error(kubernetes_auth_client, valid_token, suppress_auth_errors):
968976
response = kubernetes_auth_client.get("/test", headers={"Authorization": f"Bearer {valid_token}"})
969977
assert response.status_code == 401
970978
assert "Token validation failed" in response.json()["error"]["message"]
971979

972980

973-
def test_kubernetes_auth_request_payload(kubernetes_auth_client, valid_token, mock_kubernetes_api_server):
981+
def test_kubernetes_auth_request_payload(
982+
kubernetes_auth_client, valid_token, mock_kubernetes_api_server, suppress_auth_errors
983+
):
974984
with patch("httpx.AsyncClient.post") as mock_post:
975985
mock_response = MockResponse(
976986
200,

tests/unit/server/test_auth_github.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# This source code is licensed under the terms described in the LICENSE file in
55
# the root directory of this source tree.
66

7+
import logging # allow-direct-logging
78
from unittest.mock import AsyncMock, patch
89

910
import httpx
@@ -15,6 +16,13 @@
1516
from llama_stack.core.server.auth import AuthenticationMiddleware
1617

1718

19+
@pytest.fixture
20+
def suppress_auth_errors(caplog):
21+
"""Suppress expected ERROR logs for tests that deliberately trigger authentication errors"""
22+
caplog.set_level(logging.CRITICAL, logger="llama_stack.core.server.auth")
23+
caplog.set_level(logging.CRITICAL, logger="llama_stack.core.server.auth_providers")
24+
25+
1826
class MockResponse:
1927
def __init__(self, status_code, json_data):
2028
self.status_code = status_code
@@ -119,7 +127,7 @@ def test_authenticated_endpoint_with_valid_github_token(mock_client_class, githu
119127

120128

121129
@patch("llama_stack.core.server.auth_providers.httpx.AsyncClient")
122-
def test_authenticated_endpoint_with_invalid_github_token(mock_client_class, github_token_client):
130+
def test_authenticated_endpoint_with_invalid_github_token(mock_client_class, github_token_client, suppress_auth_errors):
123131
"""Test accessing protected endpoint with invalid GitHub token"""
124132
# Mock the GitHub API to return 401 Unauthorized
125133
mock_client = AsyncMock()

tests/unit/server/test_quota.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# This source code is licensed under the terms described in the LICENSE file in
55
# the root directory of this source tree.
66

7+
import logging # allow-direct-logging
78
from uuid import uuid4
89

910
import pytest
@@ -17,6 +18,12 @@
1718
from llama_stack.providers.utils.kvstore import register_kvstore_backends
1819

1920

21+
@pytest.fixture
22+
def suppress_quota_warnings(caplog):
23+
"""Suppress expected WARNING logs for SQLite backend and quota exceeded"""
24+
caplog.set_level(logging.CRITICAL, logger="llama_stack.core.server.quota")
25+
26+
2027
class InjectClientIDMiddleware(BaseHTTPMiddleware):
2128
"""
2229
Middleware that injects 'authenticated_client_id' to mimic AuthenticationMiddleware.
@@ -70,13 +77,13 @@ async def test_endpoint():
7077
return app
7178

7279

73-
def test_authenticated_quota_allows_up_to_limit(auth_app):
80+
def test_authenticated_quota_allows_up_to_limit(auth_app, suppress_quota_warnings):
7481
client = TestClient(auth_app)
7582
assert client.get("/test").status_code == 200
7683
assert client.get("/test").status_code == 200
7784

7885

79-
def test_authenticated_quota_blocks_after_limit(auth_app):
86+
def test_authenticated_quota_blocks_after_limit(auth_app, suppress_quota_warnings):
8087
client = TestClient(auth_app)
8188
client.get("/test")
8289
client.get("/test")
@@ -85,7 +92,7 @@ def test_authenticated_quota_blocks_after_limit(auth_app):
8592
assert resp.json()["error"]["message"] == "Quota exceeded"
8693

8794

88-
def test_anonymous_quota_allows_up_to_limit(tmp_path, request):
95+
def test_anonymous_quota_allows_up_to_limit(tmp_path, request, suppress_quota_warnings):
8996
inner_app = FastAPI()
9097

9198
@inner_app.get("/test")
@@ -107,7 +114,7 @@ async def test_endpoint():
107114
assert client.get("/test").status_code == 200
108115

109116

110-
def test_anonymous_quota_blocks_after_limit(tmp_path, request):
117+
def test_anonymous_quota_blocks_after_limit(tmp_path, request, suppress_quota_warnings):
111118
inner_app = FastAPI()
112119

113120
@inner_app.get("/test")

tests/unit/server/test_sse.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@
55
# the root directory of this source tree.
66

77
import asyncio
8+
import logging # allow-direct-logging
89
from unittest.mock import AsyncMock, MagicMock
910

11+
import pytest
12+
1013
from llama_stack.apis.common.responses import PaginatedResponse
1114
from llama_stack.core.server.server import create_dynamic_typed_route, create_sse_event, sse_generator
1215

1316

17+
@pytest.fixture
18+
def suppress_sse_errors(caplog):
19+
"""Suppress expected ERROR logs for tests that deliberately trigger SSE errors"""
20+
caplog.set_level(logging.CRITICAL, logger="llama_stack.core.server.server")
21+
22+
1423
async def test_sse_generator_basic():
1524
# An AsyncIterator wrapped in an Awaitable, just like our web methods
1625
async def async_event_gen():
@@ -70,7 +79,7 @@ async def async_event_gen():
7079
assert len(seen_events) == 0
7180

7281

73-
async def test_sse_generator_error_before_response_starts():
82+
async def test_sse_generator_error_before_response_starts(suppress_sse_errors):
7483
# Raise an error before the response starts
7584
async def async_event_gen():
7685
raise Exception("Test error")

0 commit comments

Comments
 (0)