Merge pull request #423 from christianlouis/copilot/fix-one-drive-tests
fix(tests): remove Docker registry dependency from OAuth integration tests
This commit is contained in:
+56
-11
@@ -7,6 +7,7 @@ Provides fixtures for:
|
||||
- OAuth test helpers
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Dict, Generator, Optional
|
||||
|
||||
@@ -23,6 +24,13 @@ _REAL_OAUTH_AVAILABLE = all(
|
||||
]
|
||||
)
|
||||
|
||||
# Static fallback OAuth endpoint constants used when Docker is unavailable
|
||||
_STATIC_OAUTH_AUTHORIZE_URL = "http://mock-oauth.test/default/authorize"
|
||||
_STATIC_OAUTH_TOKEN_URL = "http://mock-oauth.test/default/token"
|
||||
_STATIC_OAUTH_USERINFO_URL = "http://mock-oauth.test/default/userinfo"
|
||||
_STATIC_OAUTH_JWKS_URL = "http://mock-oauth.test/default/jwks"
|
||||
_STATIC_OAUTH_ISSUER = "http://mock-oauth.test/default"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def use_real_oauth() -> bool:
|
||||
@@ -45,7 +53,7 @@ def use_real_oauth() -> bool:
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def mock_oauth_server() -> Generator[MockOAuth2ServerContainer, None, None]:
|
||||
def mock_oauth_server() -> Generator[Optional[MockOAuth2ServerContainer], None, None]:
|
||||
"""
|
||||
Provide a mock OAuth2/OIDC server for testing.
|
||||
|
||||
@@ -53,16 +61,31 @@ def mock_oauth_server() -> Generator[MockOAuth2ServerContainer, None, None]:
|
||||
a complete OIDC provider with all necessary endpoints.
|
||||
|
||||
Yields:
|
||||
MockOAuth2ServerContainer: Running mock OAuth server
|
||||
MockOAuth2ServerContainer: Running mock OAuth server, or None if Docker is unavailable
|
||||
"""
|
||||
# Only start if we're not using real OAuth
|
||||
if not _REAL_OAUTH_AVAILABLE or os.environ.get("USE_MOCK_OAUTH", "").lower() in ("true", "1", "yes"):
|
||||
container = MockOAuth2ServerContainer()
|
||||
container.start()
|
||||
|
||||
container = None
|
||||
try:
|
||||
container = MockOAuth2ServerContainer()
|
||||
container.start()
|
||||
# Wait for the server to be ready
|
||||
container.wait_for_ready()
|
||||
except Exception as exc:
|
||||
# Docker not accessible or image pull failed – fall back to static mock config.
|
||||
# Attempt cleanup in case the container was partially started.
|
||||
if container is not None:
|
||||
try:
|
||||
container.stop()
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
logging.getLogger(__name__).warning(
|
||||
"Mock OAuth2 server unavailable (Docker inaccessible): %s – using static fallback config", exc
|
||||
)
|
||||
yield None
|
||||
return
|
||||
|
||||
try:
|
||||
yield container
|
||||
finally:
|
||||
container.stop()
|
||||
@@ -78,7 +101,7 @@ def oauth_config(mock_oauth_server: Optional[MockOAuth2ServerContainer], use_rea
|
||||
Returns either mock OAuth config or real OAuth config based on availability.
|
||||
|
||||
Args:
|
||||
mock_oauth_server: Mock OAuth server fixture (may be None if using real)
|
||||
mock_oauth_server: Mock OAuth server fixture (None if Docker unavailable)
|
||||
use_real_oauth: Whether to use real OAuth credentials
|
||||
|
||||
Returns:
|
||||
@@ -93,11 +116,22 @@ def oauth_config(mock_oauth_server: Optional[MockOAuth2ServerContainer], use_rea
|
||||
"issuer": os.environ["AUTHENTIK_CONFIG_URL"].replace("/.well-known/openid-configuration", ""),
|
||||
"mode": "real",
|
||||
}
|
||||
elif mock_oauth_server is None:
|
||||
# Docker unavailable – use a static in-process mock configuration so
|
||||
# tests that mock the OAuth token exchange still work without a container.
|
||||
return {
|
||||
"client_id": "test-client-id",
|
||||
"client_secret": "test-client-secret",
|
||||
"server_metadata_url": f"{_STATIC_OAUTH_ISSUER}/.well-known/openid-configuration",
|
||||
"authorization_endpoint": _STATIC_OAUTH_AUTHORIZE_URL,
|
||||
"token_endpoint": _STATIC_OAUTH_TOKEN_URL,
|
||||
"userinfo_endpoint": _STATIC_OAUTH_USERINFO_URL,
|
||||
"jwks_uri": _STATIC_OAUTH_JWKS_URL,
|
||||
"issuer": _STATIC_OAUTH_ISSUER,
|
||||
"mode": "static",
|
||||
}
|
||||
else:
|
||||
# Use mock OAuth server
|
||||
if mock_oauth_server is None:
|
||||
pytest.fail("Mock OAuth server not available and real credentials not configured")
|
||||
|
||||
config = mock_oauth_server.get_config()
|
||||
return {
|
||||
"client_id": "test-client-id",
|
||||
@@ -200,12 +234,19 @@ def oauth_enabled_app(oauth_config: Dict[str, str]):
|
||||
auth_module.OAUTH_CONFIGURED = True
|
||||
auth_module.OAUTH_PROVIDER_NAME = oauth_config.get("provider_name", "Test SSO")
|
||||
|
||||
# Register OAuth client
|
||||
# Clear any previously cached client so the new params take effect.
|
||||
# authlib caches created clients in _clients; we must evict before re-registering.
|
||||
auth_module.oauth._clients.pop("authentik", None)
|
||||
auth_module.oauth._registry.pop("authentik", None)
|
||||
|
||||
# Register OAuth client using direct endpoint URLs to avoid HTTP metadata
|
||||
# discovery – this allows tests to work without a running OAuth server.
|
||||
auth_module.oauth.register(
|
||||
name="authentik",
|
||||
client_id=oauth_config["client_id"],
|
||||
client_secret=oauth_config["client_secret"],
|
||||
server_metadata_url=oauth_config["server_metadata_url"],
|
||||
authorize_url=oauth_config.get("authorization_endpoint", _STATIC_OAUTH_AUTHORIZE_URL),
|
||||
access_token_url=oauth_config.get("token_endpoint", _STATIC_OAUTH_TOKEN_URL),
|
||||
client_kwargs={"scope": "openid profile email"},
|
||||
)
|
||||
|
||||
@@ -232,3 +273,7 @@ def oauth_enabled_app(oauth_config: Dict[str, str]):
|
||||
|
||||
# Remove added routes
|
||||
app.router.routes = app.router.routes[:original_route_count]
|
||||
|
||||
# Clean up OAuth registration to avoid cross-test contamination
|
||||
auth_module.oauth._clients.pop("authentik", None)
|
||||
auth_module.oauth._registry.pop("authentik", None)
|
||||
|
||||
@@ -5,7 +5,7 @@ Focuses on uncovered lines: 98-99, 121-143, 160-161, 170-171,
|
||||
324-326, 400-402, 436-438.
|
||||
"""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
@@ -296,7 +296,7 @@ class TestSaveSettingsException:
|
||||
|
||||
def test_save_settings_outer_exception(self, client: TestClient):
|
||||
"""Trigger the outer exception handler in save_onedrive_settings."""
|
||||
with patch("app.api.onedrive.os.path.join", side_effect=Exception("Unexpected boom")):
|
||||
with patch("app.api.onedrive.notify_settings_updated", side_effect=Exception("Unexpected boom")):
|
||||
response = client.post(
|
||||
"/api/onedrive/save-settings",
|
||||
data={
|
||||
@@ -332,11 +332,10 @@ class TestGetFullConfigException:
|
||||
|
||||
def test_get_full_config_exception(self, client: TestClient):
|
||||
"""Trigger the exception handler in get_onedrive_full_config."""
|
||||
from app.config import settings
|
||||
mock_settings = MagicMock()
|
||||
type(mock_settings).onedrive_client_id = PropertyMock(side_effect=Exception("boom"))
|
||||
|
||||
with patch.object(
|
||||
type(settings), "onedrive_client_id", property(fget=lambda self: (_ for _ in ()).throw(Exception("boom")))
|
||||
):
|
||||
with patch("app.api.onedrive.settings", mock_settings):
|
||||
response = client.get("/api/onedrive/get-full-config")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
Reference in New Issue
Block a user