feat: Consolidate OAuth2 endpoints into auth module and remove redundant oauth file
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Tests for authentication endpoints."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
@@ -8,6 +9,7 @@ from httpx import AsyncClient
|
||||
|
||||
from app.models.plan import Plan
|
||||
from app.models.user import User
|
||||
from app.services.auth import OAuthUserInfo
|
||||
from app.utils.auth import JWTUtils
|
||||
|
||||
|
||||
@@ -307,3 +309,141 @@ class TestAuthEndpoints:
|
||||
# Test that get_admin_user passes for admin user
|
||||
result = await get_admin_user(admin_user)
|
||||
assert result == admin_user
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_oauth_providers(self, test_client: AsyncClient) -> None:
|
||||
"""Test getting list of OAuth providers."""
|
||||
response = await test_client.get("/api/v1/auth/providers")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "providers" in data
|
||||
assert "google" in data["providers"]
|
||||
assert "github" in data["providers"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_google(self, test_client: AsyncClient) -> None:
|
||||
"""Test OAuth authorization URL generation for Google."""
|
||||
with patch("app.services.oauth.OAuthService.generate_state") as mock_state:
|
||||
mock_state.return_value = "test_state_123"
|
||||
|
||||
response = await test_client.get("/api/v1/auth/google/authorize")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "authorization_url" in data
|
||||
assert "state" in data
|
||||
assert data["state"] == "test_state_123"
|
||||
assert "accounts.google.com" in data["authorization_url"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_github(self, test_client: AsyncClient) -> None:
|
||||
"""Test OAuth authorization URL generation for GitHub."""
|
||||
with patch("app.services.oauth.OAuthService.generate_state") as mock_state:
|
||||
mock_state.return_value = "test_state_456"
|
||||
|
||||
response = await test_client.get("/api/v1/auth/github/authorize")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "authorization_url" in data
|
||||
assert "state" in data
|
||||
assert data["state"] == "test_state_456"
|
||||
assert "github.com" in data["authorization_url"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_invalid_provider(
|
||||
self, test_client: AsyncClient
|
||||
) -> None:
|
||||
"""Test OAuth authorization with invalid provider."""
|
||||
response = await test_client.get("/api/v1/auth/invalid/authorize")
|
||||
|
||||
assert response.status_code == 400
|
||||
data = response.json()
|
||||
assert "Unsupported OAuth provider" in data["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_new_user(
|
||||
self, test_client: AsyncClient, ensure_plans: tuple[Any, Any]
|
||||
) -> None:
|
||||
"""Test OAuth callback for new user creation."""
|
||||
# Mock OAuth user info
|
||||
mock_user_info = OAuthUserInfo(
|
||||
provider="google",
|
||||
provider_user_id="google_123",
|
||||
email="newuser@gmail.com",
|
||||
name="New User",
|
||||
picture="https://example.com/avatar.jpg",
|
||||
)
|
||||
|
||||
# Mock the entire handle_callback method to avoid actual OAuth API calls
|
||||
with patch("app.services.oauth.OAuthService.handle_callback") as mock_callback:
|
||||
mock_callback.return_value = mock_user_info
|
||||
|
||||
response = await test_client.get(
|
||||
"/api/v1/auth/google/callback",
|
||||
params={"code": "auth_code_123", "state": "test_state"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
# OAuth callback should successfully process and redirect to frontend
|
||||
assert response.status_code == 302
|
||||
assert response.headers["location"] == "http://localhost:8001/?auth=success"
|
||||
|
||||
# The fact that we get a 302 redirect means the OAuth login was successful
|
||||
# Detailed cookie testing can be done in integration tests
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_existing_user_link(
|
||||
self, test_client: AsyncClient, test_user: Any, ensure_plans: tuple[Any, Any]
|
||||
) -> None:
|
||||
"""Test OAuth callback for linking to existing user."""
|
||||
# Mock OAuth user info with same email as test user
|
||||
mock_user_info = OAuthUserInfo(
|
||||
provider="github",
|
||||
provider_user_id="github_456",
|
||||
email=test_user.email, # Same email as existing user
|
||||
name="Test User",
|
||||
picture="https://github.com/avatar.jpg",
|
||||
)
|
||||
|
||||
# Mock the entire handle_callback method to avoid actual OAuth API calls
|
||||
with patch("app.services.oauth.OAuthService.handle_callback") as mock_callback:
|
||||
mock_callback.return_value = mock_user_info
|
||||
|
||||
response = await test_client.get(
|
||||
"/api/v1/auth/github/callback",
|
||||
params={"code": "auth_code_456", "state": "test_state"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
# OAuth callback should successfully process and redirect to frontend
|
||||
assert response.status_code == 302
|
||||
assert response.headers["location"] == "http://localhost:8001/?auth=success"
|
||||
|
||||
# The fact that we get a 302 redirect means the OAuth login was successful
|
||||
# Detailed cookie testing can be done in integration tests
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_missing_code(self, test_client: AsyncClient) -> None:
|
||||
"""Test OAuth callback with missing authorization code."""
|
||||
response = await test_client.get(
|
||||
"/api/v1/auth/google/callback",
|
||||
params={"state": "test_state"}, # Missing code parameter
|
||||
)
|
||||
|
||||
assert response.status_code == 422 # Validation error
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_invalid_provider(
|
||||
self, test_client: AsyncClient
|
||||
) -> None:
|
||||
"""Test OAuth callback with invalid provider."""
|
||||
response = await test_client.get(
|
||||
"/api/v1/auth/invalid/callback",
|
||||
params={"code": "auth_code_123", "state": "test_state"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
data = response.json()
|
||||
assert "Unsupported OAuth provider" in data["detail"]
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
"""Tests for OAuth authentication endpoints."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from app.services.oauth import OAuthUserInfo
|
||||
|
||||
|
||||
class TestOAuthEndpoints:
|
||||
"""Test OAuth API endpoints."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_oauth_providers(self, test_client: AsyncClient) -> None:
|
||||
"""Test getting list of OAuth providers."""
|
||||
response = await test_client.get("/api/v1/oauth/providers")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "providers" in data
|
||||
assert "google" in data["providers"]
|
||||
assert "github" in data["providers"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_google(self, test_client: AsyncClient) -> None:
|
||||
"""Test OAuth authorization URL generation for Google."""
|
||||
with patch("app.services.oauth.OAuthService.generate_state") as mock_state:
|
||||
mock_state.return_value = "test_state_123"
|
||||
|
||||
response = await test_client.get("/api/v1/oauth/google/authorize")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "authorization_url" in data
|
||||
assert "state" in data
|
||||
assert data["state"] == "test_state_123"
|
||||
assert "accounts.google.com" in data["authorization_url"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_github(self, test_client: AsyncClient) -> None:
|
||||
"""Test OAuth authorization URL generation for GitHub."""
|
||||
with patch("app.services.oauth.OAuthService.generate_state") as mock_state:
|
||||
mock_state.return_value = "test_state_456"
|
||||
|
||||
response = await test_client.get("/api/v1/oauth/github/authorize")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "authorization_url" in data
|
||||
assert "state" in data
|
||||
assert data["state"] == "test_state_456"
|
||||
assert "github.com" in data["authorization_url"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_authorize_invalid_provider(
|
||||
self, test_client: AsyncClient
|
||||
) -> None:
|
||||
"""Test OAuth authorization with invalid provider."""
|
||||
response = await test_client.get("/api/v1/oauth/invalid/authorize")
|
||||
|
||||
assert response.status_code == 400
|
||||
data = response.json()
|
||||
assert "Unsupported OAuth provider" in data["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_new_user(
|
||||
self, test_client: AsyncClient, ensure_plans: tuple[Any, Any]
|
||||
) -> None:
|
||||
"""Test OAuth callback for new user creation."""
|
||||
# Mock OAuth user info
|
||||
mock_user_info = OAuthUserInfo(
|
||||
provider="google",
|
||||
provider_user_id="google_123",
|
||||
email="newuser@gmail.com",
|
||||
name="New User",
|
||||
picture="https://example.com/avatar.jpg",
|
||||
)
|
||||
|
||||
# Mock the entire handle_callback method to avoid actual OAuth API calls
|
||||
with patch("app.services.oauth.OAuthService.handle_callback") as mock_callback:
|
||||
mock_callback.return_value = mock_user_info
|
||||
|
||||
response = await test_client.get(
|
||||
"/api/v1/oauth/google/callback",
|
||||
params={"code": "auth_code_123", "state": "test_state"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
# OAuth callback should successfully process and redirect to frontend
|
||||
assert response.status_code == 302
|
||||
assert response.headers["location"] == "http://localhost:8001/?auth=success"
|
||||
|
||||
# The fact that we get a 302 redirect means the OAuth login was successful
|
||||
# Detailed cookie testing can be done in integration tests
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_existing_user_link(
|
||||
self, test_client: AsyncClient, test_user: Any, ensure_plans: tuple[Any, Any]
|
||||
) -> None:
|
||||
"""Test OAuth callback for linking to existing user."""
|
||||
# Mock OAuth user info with same email as test user
|
||||
mock_user_info = OAuthUserInfo(
|
||||
provider="github",
|
||||
provider_user_id="github_456",
|
||||
email=test_user.email, # Same email as existing user
|
||||
name="Test User",
|
||||
picture="https://github.com/avatar.jpg",
|
||||
)
|
||||
|
||||
# Mock the entire handle_callback method to avoid actual OAuth API calls
|
||||
with patch("app.services.oauth.OAuthService.handle_callback") as mock_callback:
|
||||
mock_callback.return_value = mock_user_info
|
||||
|
||||
response = await test_client.get(
|
||||
"/api/v1/oauth/github/callback",
|
||||
params={"code": "auth_code_456", "state": "test_state"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
# OAuth callback should successfully process and redirect to frontend
|
||||
assert response.status_code == 302
|
||||
assert response.headers["location"] == "http://localhost:8001/?auth=success"
|
||||
|
||||
# The fact that we get a 302 redirect means the OAuth login was successful
|
||||
# Detailed cookie testing can be done in integration tests
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_missing_code(self, test_client: AsyncClient) -> None:
|
||||
"""Test OAuth callback with missing authorization code."""
|
||||
response = await test_client.get(
|
||||
"/api/v1/oauth/google/callback",
|
||||
params={"state": "test_state"}, # Missing code parameter
|
||||
)
|
||||
|
||||
assert response.status_code == 422 # Validation error
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_oauth_callback_invalid_provider(
|
||||
self, test_client: AsyncClient
|
||||
) -> None:
|
||||
"""Test OAuth callback with invalid provider."""
|
||||
response = await test_client.get(
|
||||
"/api/v1/oauth/invalid/callback",
|
||||
params={"code": "auth_code_123", "state": "test_state"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
data = response.json()
|
||||
assert "Unsupported OAuth provider" in data["detail"]
|
||||
Reference in New Issue
Block a user