DevWizard-Vandan
fix(api): mount Next.js frontend as default app and prefix all API routes under /api
cede2ad | """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, | |
| ) | |
| 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 | |
| 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 | |