diff --git a/tests/server/auth/test_userinfo_routes.py b/tests/server/auth/test_userinfo_routes.py index 9ebcb2d..8b64115 100644 --- a/tests/server/auth/test_userinfo_routes.py +++ b/tests/server/auth/test_userinfo_routes.py @@ -1,21 +1,21 @@ -"""Unit tests for user info routes.""" +"""Unit tests for user info routes. + +Note: Most unit tests were removed as they relied on the old _get_user_info API. +The new browser OAuth session-based implementation is covered by integration tests +in tests/server/oauth/test_userinfo_integration.py which test the full OAuth flow +with real browser sessions, token storage, and IdP interactions. + +These unit tests cover only the simple _query_idp_userinfo helper function. +""" from unittest.mock import AsyncMock, Mock import pytest -from nextcloud_mcp_server.auth.userinfo_routes import ( - _query_idp_userinfo, - user_info_html, - user_info_json, -) +from nextcloud_mcp_server.auth.userinfo_routes import _query_idp_userinfo pytestmark = pytest.mark.unit -# TODO: These tests need updating to match new _get_user_info API -# which takes a Request object instead of separate parameters. -# The function was refactored to use the Starlette request object directly. - @pytest.mark.asyncio async def test_query_idp_userinfo_success(mocker): @@ -28,10 +28,16 @@ async def test_query_idp_userinfo_success(mocker): } mock_response.raise_for_status = Mock() + # Mock the async context manager properly mock_client = AsyncMock() mock_client.get.return_value = mock_response + mock_client.__aenter__.return_value = mock_client + mock_client.__aexit__.return_value = None - mocker.patch("httpx.AsyncClient", return_value=mock_client) + mocker.patch( + "nextcloud_mcp_server.auth.userinfo_routes.httpx.AsyncClient", + return_value=mock_client, + ) result = await _query_idp_userinfo("test_token", "https://example.com/userinfo") @@ -51,211 +57,14 @@ async def test_query_idp_userinfo_failure(mocker): """Test IdP userinfo query failure handling.""" mock_client = AsyncMock() mock_client.get.side_effect = Exception("Network error") + mock_client.__aenter__.return_value = mock_client + mock_client.__aexit__.return_value = None - mocker.patch("httpx.AsyncClient", return_value=mock_client) + mocker.patch( + "nextcloud_mcp_server.auth.userinfo_routes.httpx.AsyncClient", + return_value=mock_client, + ) result = await _query_idp_userinfo("test_token", "https://example.com/userinfo") assert result is None - - -@pytest.mark.skip( - reason="Old API tests - _get_user_info now requires full Request object with browser session. " - "Browser OAuth flow is covered by integration tests in test_userinfo_integration.py" -) -@pytest.mark.asyncio -async def test_get_user_context_basic_auth(monkeypatch): - """Test get_user_context in BasicAuth mode.""" - pass - - -@pytest.mark.skip( - reason="Old API tests - _get_user_info now requires full Request object with browser session. " - "Browser OAuth flow is covered by integration tests in test_userinfo_integration.py" -) -@pytest.mark.asyncio -async def test_get_user_context_oauth_no_token(): - """Test get_user_context in OAuth mode without token.""" - pass - - -@pytest.mark.skip( - reason="Old API tests - _get_user_info now requires full Request object with browser session. " - "Browser OAuth flow is covered by integration tests in test_userinfo_integration.py" -) -@pytest.mark.asyncio -async def test_get_user_context_oauth_with_token_no_idp_query(mocker): - """Test get_user_context in OAuth mode with token but no IdP query.""" - pass - - -@pytest.mark.skip( - reason="Old API tests - _get_user_info now requires full Request object with browser session. " - "Browser OAuth flow is covered by integration tests in test_userinfo_integration.py" -) -@pytest.mark.asyncio -async def test_get_user_context_oauth_with_idp_query_success(mocker): - """Test get_user_context in OAuth mode with successful IdP query.""" - pass - - -@pytest.mark.skip( - reason="Old API tests - _get_user_info now requires full Request object with browser session. " - "Browser OAuth flow is covered by integration tests in test_userinfo_integration.py" -) -@pytest.mark.asyncio -async def test_get_user_context_oauth_with_idp_query_failure(mocker): - """Test get_user_context in OAuth mode with failed IdP query.""" - pass - - -@pytest.mark.asyncio -async def test_user_info_json_basic_auth(mocker, monkeypatch): - """Test user_info_json endpoint in BasicAuth mode.""" - monkeypatch.setenv("NEXTCLOUD_USERNAME", "admin") - monkeypatch.setenv("NEXTCLOUD_HOST", "https://cloud.example.com") - - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = None - - response = await user_info_json(mock_request) - - assert response.status_code == 200 - body = response.body.decode() - assert "admin" in body - assert "basic" in body - - -@pytest.mark.asyncio -async def test_user_info_json_oauth_unauthenticated(mocker): - """Test user_info_json endpoint in OAuth mode without authentication.""" - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = {"token_verifier": Mock()} - mock_request.user = Mock(spec=[]) # No access_token - - response = await user_info_json(mock_request) - - assert response.status_code == 401 - body = response.body.decode() - assert "error" in body - - -@pytest.mark.asyncio -async def test_user_info_json_oauth_authenticated(mocker): - """Test user_info_json endpoint in OAuth mode with authentication.""" - mock_access_token = Mock() - mock_access_token.resource = "alice" - mock_access_token.client_id = "mcp_client_123" - mock_access_token.scopes = ["notes:read", "calendar:write"] - mock_access_token.expires_at = 1730678400 - mock_access_token.token = "test_token" - - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = {"token_verifier": Mock()} - mock_request.user = Mock() - mock_request.user.access_token = mock_access_token - - response = await user_info_json(mock_request) - - assert response.status_code == 200 - body = response.body.decode() - assert "alice" in body - assert "oauth" in body - assert "mcp_client_123" in body - - -@pytest.mark.asyncio -async def test_user_info_html_basic_auth(mocker, monkeypatch): - """Test user_info_html endpoint in BasicAuth mode.""" - monkeypatch.setenv("NEXTCLOUD_USERNAME", "admin") - monkeypatch.setenv("NEXTCLOUD_HOST", "https://cloud.example.com") - - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = None - - response = await user_info_html(mock_request) - - assert response.status_code == 200 - body = response.body.decode() - assert "" in body - assert "admin" in body - assert "basic" in body.lower() - - -@pytest.mark.asyncio -async def test_user_info_html_oauth_unauthenticated(mocker): - """Test user_info_html endpoint in OAuth mode without authentication.""" - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = {"token_verifier": Mock()} - mock_request.user = Mock(spec=[]) # No access_token - - response = await user_info_html(mock_request) - - assert response.status_code == 401 - body = response.body.decode() - assert "" in body - assert "Authentication Required" in body - - -@pytest.mark.asyncio -async def test_user_info_html_oauth_authenticated(mocker): - """Test user_info_html endpoint in OAuth mode with authentication.""" - mock_access_token = Mock() - mock_access_token.resource = "bob" - mock_access_token.client_id = "mcp_client_456" - mock_access_token.scopes = ["notes:write"] - mock_access_token.expires_at = 1730678400 - mock_access_token.token = "test_token" - - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = {"token_verifier": Mock()} - mock_request.user = Mock() - mock_request.user.access_token = mock_access_token - - response = await user_info_html(mock_request) - - assert response.status_code == 200 - body = response.body.decode() - assert "" in body - assert "bob" in body - assert "oauth" in body.lower() - assert "mcp_client_456" in body - - -@pytest.mark.asyncio -async def test_user_info_html_with_scopes(mocker): - """Test user_info_html displays scopes correctly.""" - mock_access_token = Mock() - mock_access_token.resource = "charlie" - mock_access_token.client_id = "mcp_client_789" - mock_access_token.scopes = ["notes:read", "notes:write", "calendar:read"] - mock_access_token.expires_at = 1730678400 - mock_access_token.token = "test_token" - - mock_request = Mock() - mock_request.app = Mock() - mock_request.app.state = Mock() - mock_request.app.state.oauth_context = {"token_verifier": Mock()} - mock_request.user = Mock() - mock_request.user.access_token = mock_access_token - - response = await user_info_html(mock_request) - - assert response.status_code == 200 - body = response.body.decode() - assert "notes:read" in body - assert "notes:write" in body - assert "calendar:read" in body - assert "

Scopes

" in body