Synthra / tests /api /test_auth.py
DevWizard-Vandan
fix(api): mount Next.js frontend as default app and prefix all API routes under /api
cede2ad
Raw
History Blame Contribute Delete
4.97 kB
"""Tests for the auth router endpoints."""
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
from synthra.execution.exceptions import (
ExecutionAuthenticationError,
VerificationRequiredError,
)
@pytest.fixture
def mock_service_state():
"""Fixture providing a mocked ServiceState."""
state = MagicMock()
state.auth_state = "Logged Out"
state.auth_username = None
state.auth_verification_url = None
state.execution_client = MagicMock()
async def async_noop() -> None:
pass
state.initialize = async_noop
state.shutdown = async_noop
return state
@pytest.fixture
def client(mock_service_state):
"""Provide a TestClient with mocked ServiceState initialization."""
with patch(
"synthra.api.app.ServiceState.get_instance", return_value=mock_service_state
):
from synthra.api.app import create_app
application = create_app()
# Ensure service state is attached to application
application.state.service = mock_service_state
with TestClient(application, raise_server_exceptions=True) as tc:
yield tc
def test_login_success(client, mock_service_state):
"""POST /api/auth/login returns success on valid credentials."""
mock_service_state.execution_client.authenticate.return_value = None
response = client.post(
"/api/auth/login",
json={"username": "test_user", "password": "test_password", "remember": True},
)
assert response.status_code == 200
assert response.json() == {
"status": "success",
"message": None,
"verification_url": None,
}
# Verify session cookie was set
assert "session_id" in response.cookies
assert mock_service_state.auth_state == "Authenticated"
assert mock_service_state.auth_username == "test_user"
def test_login_invalid_credentials(client, mock_service_state):
"""POST /api/auth/login returns error on invalid credentials."""
mock_service_state.execution_client.authenticate.side_effect = (
ExecutionAuthenticationError("Auth failed")
)
response = client.post(
"/api/auth/login",
json={"username": "bad_user", "password": "bad_password", "remember": False},
)
assert response.status_code == 200
assert response.json() == {
"status": "error",
"message": "Invalid username or password",
"verification_url": None,
}
assert "session_id" not in response.cookies
assert mock_service_state.auth_state == "Logged Out"
def test_login_verification_required(client, mock_service_state):
"""POST /api/auth/login returns verification_required status on MFA."""
mock_service_state.execution_client.authenticate.side_effect = (
VerificationRequiredError("https://mfa.example.test")
)
response = client.post(
"/api/auth/login",
json={"username": "mfa_user", "password": "mfa_password", "remember": False},
)
assert response.status_code == 200
assert response.json() == {
"status": "verification_required",
"verification_url": "https://mfa.example.test",
"message": None,
}
assert "session_id" not in response.cookies
assert mock_service_state.auth_state == "Waiting for Biometric Verification"
assert mock_service_state.auth_verification_url == "https://mfa.example.test"
def test_status_unauthenticated(client):
"""GET /api/auth/status returns unauthenticated when no session exists."""
response = client.get("/api/auth/status")
assert response.status_code == 200
assert response.json() == {
"authenticated": False,
"username": None,
"state": "Logged Out",
"verification_url": None,
}
def test_status_authenticated_and_logout(client, mock_service_state):
"""GET /api/auth/status returns authenticated and logout clears session."""
mock_service_state.execution_client.authenticate.return_value = None
# 1. Login to establish session
login_resp = client.post(
"/api/auth/login",
json={"username": "user1", "password": "pass1", "remember": False},
)
assert login_resp.status_code == 200
# 2. Get status with cookie
status_resp = client.get("/api/auth/status")
assert status_resp.status_code == 200
assert status_resp.json() == {
"authenticated": True,
"username": "user1",
"state": "Authenticated",
"verification_url": None,
}
# 3. Invalidate via logout
logout_resp = client.post("/api/auth/logout")
assert logout_resp.status_code == 200
assert logout_resp.json() == {"status": "success", "message": None}
# Cookie should be deleted
# 4. Status should now be unauthenticated
status_resp_after = client.get("/api/auth/status")
assert status_resp_after.status_code == 200
assert status_resp_after.json()["authenticated"] is False