test: expand auth and encryption test coverage with 299 new test lines

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-09 21:16:55 +00:00
parent 91755ee5e6
commit acb8951dd9
2 changed files with 299 additions and 1 deletions
+189 -1
View File
@@ -1,9 +1,11 @@
"""Tests for app/auth.py module."""
import hashlib
import os
import pytest
from unittest.mock import MagicMock, AsyncMock, patch
from starlette.testclient import TestClient
from fastapi import Request
from fastapi import Request, status
from starlette.responses import RedirectResponse
from app.auth import get_current_user, get_gravatar_url, require_login
@@ -26,6 +28,13 @@ class TestGetCurrentUser:
result = get_current_user(mock_request)
assert result is None
def test_returns_none_when_user_is_none(self):
"""Test that get_current_user returns None when user is None."""
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": None}
result = get_current_user(mock_request)
assert result is None
@pytest.mark.unit
class TestGetGravatarUrl:
@@ -65,6 +74,103 @@ class TestRequireLogin:
# When AUTH_ENABLED is False, the decorator returns the function unchanged
assert decorated is my_func
@pytest.mark.asyncio
async def test_redirects_to_login_when_no_user(self):
"""Test that require_login redirects to /login when no user in session and auth is enabled."""
with patch("app.auth.AUTH_ENABLED", True):
# Import fresh to get patched AUTH_ENABLED
from app.auth import require_login
@require_login
async def protected_endpoint(request: Request):
return {"message": "success"}
mock_request = MagicMock(spec=Request)
mock_request.session = {}
mock_request.url = MagicMock()
mock_request.url.__str__ = MagicMock(return_value="http://test.com/protected")
result = await protected_endpoint(mock_request)
assert isinstance(result, RedirectResponse)
assert result.status_code == status.HTTP_302_FOUND
assert "/login" in str(result.headers.get("location"))
@pytest.mark.asyncio
async def test_saves_redirect_url_when_not_authenticated(self):
"""Test that require_login saves the original URL in session."""
with patch("app.auth.AUTH_ENABLED", True):
from app.auth import require_login
@require_login
async def protected_endpoint(request: Request):
return {"message": "success"}
mock_request = MagicMock(spec=Request)
mock_request.session = {}
mock_request.url = MagicMock()
original_url = "http://test.com/protected/page?param=value"
mock_request.url.__str__ = MagicMock(return_value=original_url)
await protected_endpoint(mock_request)
assert mock_request.session.get("redirect_after_login") == original_url
@pytest.mark.asyncio
async def test_allows_access_when_user_in_session(self):
"""Test that require_login allows access when user is in session."""
with patch("app.auth.AUTH_ENABLED", True):
from app.auth import require_login
@require_login
async def protected_endpoint(request: Request):
return {"message": "success", "user": request.session.get("user")}
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": {"id": "test_user", "name": "Test"}}
result = await protected_endpoint(mock_request)
assert result["message"] == "success"
assert result["user"]["id"] == "test_user"
@pytest.mark.asyncio
async def test_handles_async_functions(self):
"""Test that require_login correctly wraps async functions."""
with patch("app.auth.AUTH_ENABLED", True):
from app.auth import require_login
@require_login
async def async_endpoint(request: Request, param: str):
return {"message": "async", "param": param}
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": {"id": "test"}}
result = await async_endpoint(mock_request, param="test_value")
assert result["message"] == "async"
assert result["param"] == "test_value"
def test_handles_sync_functions(self):
"""Test that require_login correctly wraps sync functions."""
with patch("app.auth.AUTH_ENABLED", True):
from app.auth import require_login
@require_login
def sync_endpoint(request: Request, param: str):
return {"message": "sync", "param": param}
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": {"id": "test"}}
# Call the decorated sync function
import asyncio
result = asyncio.run(sync_endpoint(request=mock_request, param="test_value"))
assert result["message"] == "sync"
assert result["param"] == "test_value"
@pytest.mark.integration
class TestWhoamiEndpoint:
@@ -84,3 +190,85 @@ class TestWhoamiEndpoint:
assert response.status_code == 200
data = response.json()
assert "message" in data
@pytest.mark.integration
class TestAuthEndpoints:
"""Integration tests for authentication endpoints."""
def test_login_page_not_available_when_auth_disabled(self, client):
"""Test that login page returns 404 when auth is disabled (default in tests)."""
# In the test environment, AUTH_ENABLED is False by default
response = client.get("/login")
# When auth is disabled, the auth routes are not registered
assert response.status_code == 404
def test_oauth_login_not_available_when_auth_disabled(self, client):
"""Test that /oauth-login returns 404 when auth is disabled."""
response = client.get("/oauth-login")
assert response.status_code == 404
def test_logout_not_available_when_auth_disabled(self, client):
"""Test that /logout returns 404 when auth is disabled."""
response = client.get("/logout")
assert response.status_code == 404
def test_auth_post_not_available_when_auth_disabled(self, client):
"""Test that POST /auth returns 404 when auth is disabled."""
response = client.post("/auth", data={"username": "admin", "password": "test"})
assert response.status_code == 404
@pytest.mark.unit
class TestSessionValidation:
"""Tests for session validation edge cases."""
def test_empty_user_object(self):
"""Test get_current_user with empty user object."""
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": {}}
result = get_current_user(mock_request)
assert result == {}
def test_user_object_missing_id(self):
"""Test session with user missing id field."""
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": {"name": "Test", "email": "test@example.com"}}
result = get_current_user(mock_request)
# Should still return the user object even if id is missing
assert result["name"] == "Test"
assert "id" not in result
def test_user_object_with_extra_fields(self):
"""Test session with user having extra fields."""
mock_request = MagicMock(spec=Request)
user = {
"id": "123",
"name": "Test",
"email": "test@example.com",
"is_admin": True,
"groups": ["admin"],
"picture": "https://example.com/pic.jpg",
}
mock_request.session = {"user": user}
result = get_current_user(mock_request)
assert result == user
@pytest.mark.asyncio
async def test_require_login_with_user_missing_required_fields(self):
"""Test require_login with user object missing typical fields."""
with patch("app.auth.AUTH_ENABLED", True):
from app.auth import require_login
@require_login
async def protected_endpoint(request: Request):
return {"message": "success"}
# User object exists but is minimal
mock_request = MagicMock(spec=Request)
mock_request.session = {"user": {"id": "123"}} # Missing name, email, etc.
result = await protected_endpoint(mock_request)
# Should still allow access as long as user key exists
assert result["message"] == "success"