import pytest from fastapi.testclient import TestClient from unittest.mock import patch from ea_chatbot.api.main import app from ea_chatbot.history.models import User from ea_chatbot.api.utils import create_access_token client = TestClient(app) @pytest.fixture def mock_user(): return User( id="user-123", username="test@example.com", display_name="Test User", password_hash="hashed_password" ) def test_v1_prefix(): """Test that routes are prefixed with /api/v1.""" # This should now be 404 response = client.get("/auth/me") assert response.status_code == 404 # This should be 401 (unauthorized) instead of 404 response = client.get("/api/v1/auth/me") assert response.status_code == 401 def test_login_sets_cookie(): """Test that login sets the access_token cookie.""" with patch("ea_chatbot.api.routers.auth.history_manager") as mock_hm: mock_hm.authenticate_user.return_value = User(id="1", username="test@example.com") response = client.post( "/api/v1/auth/login", data={"username": "test@example.com", "password": "password123"} ) assert response.status_code == 200 assert "access_token" in response.cookies # Check for HttpOnly in Set-Cookie header set_cookie = response.headers.get("set-cookie", "") assert "access_token" in set_cookie assert "HttpOnly" in set_cookie def test_register_sets_cookie(): """Test that register sets the access_token cookie.""" with patch("ea_chatbot.api.routers.auth.history_manager") as mock_hm: mock_hm.get_user.return_value = None mock_hm.create_user.return_value = User(id="1", username="new@example.com", display_name="New") response = client.post( "/api/v1/auth/register", json={"email": "new@example.com", "password": "password123", "display_name": "New"} ) assert response.status_code == 201 assert "access_token" in response.cookies def test_auth_via_cookie(): """Test that protected routes work with the access_token cookie.""" token = create_access_token(data={"sub": "123"}) with patch("ea_chatbot.api.dependencies.history_manager") as mock_hm: mock_hm.get_user_by_id.return_value = User(id="123", username="test@example.com", display_name="Test") # Pass token via cookie instead of header client.cookies.set("access_token", token) response = client.get("/api/v1/auth/me") assert response.status_code == 200 assert response.json()["email"] == "test@example.com" def test_logout_clears_cookie(): """Test that logout endpoint clears the cookie.""" response = client.post("/api/v1/auth/logout") assert response.status_code == 200 # Cookie should be expired/empty cookie = response.cookies.get("access_token") assert not cookie or cookie == "" def test_oidc_callback_redirects_with_cookie(): """Test that OIDC callback sets cookie and redirects.""" with patch("ea_chatbot.api.routers.auth.oidc_client") as mock_oidc, \ patch("ea_chatbot.api.routers.auth.OIDCSession.decrypt") as mock_decrypt, \ patch("ea_chatbot.api.routers.auth.history_manager") as mock_hm: mock_decrypt.return_value = { "state": "test_state", "nonce": "test_nonce", "code_verifier": "test_verifier" } mock_oidc.exchange_code_for_token.return_value = {"id_token": "fake_id_token"} mock_oidc.validate_id_token.return_value = {"email": "sso@example.com", "name": "SSO User"} mock_hm.sync_user_from_oidc.return_value = User(id="sso-123", username="sso@example.com", display_name="SSO User") # Set the session cookie client.cookies.set("oidc_session", "fake_token") # Follow_redirects=False to catch the 302 response = client.get("/api/v1/auth/oidc/callback?code=some-code&state=test_state", follow_redirects=False) assert response.status_code == 302 assert "access_token" in response.cookies # Should redirect to FRONTEND_URL (default http://localhost:5173) assert "http://localhost:5173" in response.headers["location"]