DocDoeAI / tests /test_google_auth.py
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
6515ef9
Raw
History Blame Contribute Delete
5.26 kB
from __future__ import annotations
import os
from urllib.parse import parse_qs, urlparse
def _set_google_env() -> None:
os.environ["GOOGLE_CLIENT_ID"] = "test-google-client"
os.environ["GOOGLE_CLIENT_SECRET"] = "test-google-secret"
os.environ["FRONTEND_BASE_URL"] = "http://127.0.0.1:3000"
os.environ["GOOGLE_OAUTH_REDIRECT_URI"] = "http://testserver/auth/google/callback"
from app.core.config import get_settings
get_settings.cache_clear()
def _clear_google_env() -> None:
for key in (
"GOOGLE_CLIENT_ID",
"GOOGLE_CLIENT_SECRET",
"FRONTEND_BASE_URL",
"GOOGLE_OAUTH_REDIRECT_URI",
):
os.environ.pop(key, None)
from app.core.config import get_settings
get_settings.cache_clear()
def _fragment_params(location: str) -> dict[str, str]:
fragment = urlparse(location).fragment
parsed = parse_qs(fragment)
return {key: values[0] for key, values in parsed.items()}
def test_google_start_redirects_to_frontend_error_without_oauth_credentials(auth_client):
os.environ["GOOGLE_CLIENT_ID"] = ""
os.environ["GOOGLE_CLIENT_SECRET"] = ""
from app.core.config import get_settings
get_settings.cache_clear()
try:
response = auth_client.get("/auth/google/start", follow_redirects=False)
finally:
_clear_google_env()
assert response.status_code == 302
params = _fragment_params(response.headers["location"])
assert "not ready" in params["error"].lower()
def test_google_callback_creates_user_and_returns_frontend_session(auth_client, monkeypatch):
_set_google_env()
from app.routes import auth as auth_routes
monkeypatch.setattr(
auth_routes,
"_post_google_token",
lambda payload: {"access_token": "google-access-token"},
)
monkeypatch.setattr(
auth_routes,
"_fetch_google_profile",
lambda access_token: {
"email": "google.student@example.com",
"email_verified": True,
"name": "Google Student",
},
)
try:
nonce = "test-google-state-nonce"
state = auth_routes._create_google_state(
next_path="/dashboard", invite_code=None, nonce=nonce
)
response = auth_client.get(
f"/auth/google/callback?code=test-code&state={state}",
cookies={auth_routes.GOOGLE_STATE_COOKIE: nonce},
follow_redirects=False,
)
assert response.status_code == 302
location = response.headers["location"]
assert location.startswith("http://127.0.0.1:3000/auth/google/callback#")
params = _fragment_params(location)
assert params["access_token"]
assert params["token_type"] == "bearer"
assert params["next"] == "/dashboard"
assert "google.student@example.com" in params["user"]
finally:
_clear_google_env()
def test_google_callback_respects_beta_invite_gate(auth_client, monkeypatch):
_set_google_env()
os.environ["BETA_ACCESS_ENABLED"] = "true"
os.environ["BETA_INVITE_CODE"] = "DOCDOE-BETA-2026"
from app.core.config import get_settings
from app.routes import auth as auth_routes
get_settings.cache_clear()
monkeypatch.setattr(
auth_routes,
"_post_google_token",
lambda payload: {"access_token": "google-access-token"},
)
monkeypatch.setattr(
auth_routes,
"_fetch_google_profile",
lambda access_token: {
"email": "invite-needed@example.com",
"email_verified": True,
"name": "Invite Needed",
},
)
try:
nonce = "test-google-state-nonce"
state = auth_routes._create_google_state(
next_path="/onboarding", invite_code=None, nonce=nonce
)
response = auth_client.get(
f"/auth/google/callback?code=test-code&state={state}",
cookies={auth_routes.GOOGLE_STATE_COOKIE: nonce},
follow_redirects=False,
)
assert response.status_code == 302
params = _fragment_params(response.headers["location"])
assert "invite" in params["error"].lower()
assert params["next"] == "/onboarding"
finally:
os.environ["BETA_ACCESS_ENABLED"] = "false"
os.environ.pop("BETA_INVITE_CODE", None)
_clear_google_env()
def test_google_callback_rejects_state_from_another_browser(auth_client, monkeypatch):
_set_google_env()
from app.routes import auth as auth_routes
monkeypatch.setattr(
auth_routes,
"_post_google_token",
lambda payload: (_ for _ in ()).throw(AssertionError("token exchange must not run")),
)
try:
state = auth_routes._create_google_state(
next_path="/dashboard", invite_code=None, nonce="browser-a"
)
response = auth_client.get(
f"/auth/google/callback?code=test-code&state={state}",
cookies={auth_routes.GOOGLE_STATE_COOKIE: "browser-b"},
follow_redirects=False,
)
assert response.status_code == 302
params = _fragment_params(response.headers["location"])
assert "expired" in params["error"].lower()
finally:
_clear_google_env()