import pytest from fastapi.testclient import TestClient from app.api import auth as auth_module from app.db import get_db from app.main import app from app.services import google_oauth @pytest.fixture def client(monkeypatch, db_session): def _get_db_override(): yield db_session app.dependency_overrides[get_db] = _get_db_override monkeypatch.setattr(auth_module, "_run_initial_sync", lambda: None) monkeypatch.setattr( google_oauth, "build_authorization_url", lambda: ("https://accounts.google.com/fake", "fixed-state") ) with TestClient(app) as test_client: yield test_client del app.dependency_overrides[get_db] class FakeCredentials: token = "fake-access-token" refresh_token = "fake-refresh-token" expiry = None def _start_oauth(client): response = client.get("/api/auth/google/start", follow_redirects=False) assert response.status_code in (302, 307) return "fixed-state" def test_callback_rejects_disallowed_email(client, monkeypatch): state = _start_oauth(client) monkeypatch.setattr(google_oauth, "exchange_code", lambda code, state: FakeCredentials()) monkeypatch.setattr(google_oauth, "fetch_userinfo", lambda token: {"email": "stranger@example.com"}) revoked = {} monkeypatch.setattr(google_oauth, "revoke_token", lambda token: revoked.setdefault("called", True)) response = client.get(f"/api/auth/google/callback?code=abc&state={state}", follow_redirects=False) assert response.status_code in (302, 307) assert "account_not_allowed" in response.headers["location"] assert revoked.get("called") is True status = client.get("/api/auth/status").json() assert status["authenticated"] is False assert status["connected"] is False def test_callback_accepts_allowed_email(client, monkeypatch): state = _start_oauth(client) monkeypatch.setattr(google_oauth, "exchange_code", lambda code, state: FakeCredentials()) monkeypatch.setattr(google_oauth, "fetch_userinfo", lambda token: {"email": "owner@example.com"}) response = client.get(f"/api/auth/google/callback?code=abc&state={state}", follow_redirects=False) assert response.status_code in (302, 307) assert "auth_error" not in response.headers["location"] status = client.get("/api/auth/status").json() assert status["authenticated"] is True assert status["connected"] is True assert status["email"] == "owner@example.com" def test_callback_rejects_invalid_state(client, monkeypatch): _start_oauth(client) response = client.get("/api/auth/google/callback?code=abc&state=wrong-state", follow_redirects=False) assert response.status_code in (302, 307) assert "invalid_state" in response.headers["location"]