"""The legacy /discord/* routes. These are not duplicate coverage of tests/accounts/test_oauth.py — they pin the two compatibility promises that outlive the refactor: * **/discord/link/callback is registered in Discord's developer portal.** If it stops answering, every Discord login breaks until an operator edits the portal. * The other /discord/* paths are what the deployed frontend calls, and frontend and backend deploy independently, so a newer backend must keep serving an older one. The flow itself lives in accounts/oauth.py and is tested there. """ import pytest from fastapi.testclient import TestClient from web import app as appmod from accounts import db, oauth V2 = "/thermograph/api/v2" PW = "supersecret123" @pytest.fixture(scope="module", autouse=True) def _tables(): db.Base.metadata.create_all(db.sync_engine) @pytest.fixture(autouse=True) def _configured(monkeypatch): monkeypatch.setitem(oauth.CREDENTIALS, "discord", ("app123", "secret456")) monkeypatch.setattr(oauth.httpx, "AsyncClient", _MockClient) class _Resp: def __init__(self, code, data): self.status_code = code; self._data = data def json(self): return self._data class _MockClient: def __init__(self, *a, **k): pass async def __aenter__(self): return self async def __aexit__(self, *a): return False async def post(self, url, **k): return _Resp(200, {"access_token": "tok"}) async def get(self, url, **k): return _Resp(200, {"id": "discord-999", "email": "legacy@example.com", "verified": True}) def _login(client, email): assert client.post(f"{V2}/auth/register", json={"email": email, "password": PW}).status_code in (201, 400) assert client.post(f"{V2}/auth/login", data={"username": email, "password": PW}).status_code == 204 def test_legacy_config_still_reports_enabled_state(monkeypatch): c = TestClient(appmod.app) monkeypatch.setitem(oauth.CREDENTIALS, "discord", ("", "")) assert c.get(f"{V2}/discord/config").json() == {"enabled": False} monkeypatch.setitem(oauth.CREDENTIALS, "discord", ("app123", "secret456")) assert c.get(f"{V2}/discord/config").json() == {"enabled": True} def test_legacy_link_start_still_requires_auth(): c = TestClient(appmod.app) assert c.get(f"{V2}/discord/link/start", follow_redirects=False).status_code == 401 assert c.post(f"{V2}/discord/unlink").status_code == 401 def test_legacy_start_redirects_to_discord(): c = TestClient(appmod.app) _login(c, "legacy-start@example.com") r = c.get(f"{V2}/discord/link/start", follow_redirects=False) assert r.status_code == 303 loc = r.headers["location"] assert loc.startswith("https://discord.com/api/oauth2/authorize") assert "client_id=app123" in loc and "scope=identify" in loc and "state=" in loc # The redirect_uri it asks Discord to call back on must be the registered one. assert "discord%2Flink%2Fcallback" in loc def test_the_registered_callback_url_still_completes_a_link(): c = TestClient(appmod.app) _login(c, "legacy-cb@example.com") uid = c.get(f"{V2}/users/me").json()["id"] state = oauth._sign_state(uid, "link", "discord") r = c.get(f"{V2}/discord/link/callback?code=abc&state={state}", follow_redirects=False) assert r.status_code == 303 and "discord=linked" in r.headers["location"] me = c.get(f"{V2}/users/me").json() assert me["discord_id"] == "discord-999" # the DM delivery address assert me["oauth_providers"] == ["discord"] # and the identity row # Legacy unlink clears both. assert c.post(f"{V2}/discord/unlink").status_code == 204 me = c.get(f"{V2}/users/me").json() assert me["discord_id"] is None and me["oauth_providers"] == [] def test_the_callback_still_emits_the_discord_query_param(): """The deployed frontend reads ?discord=, not ?oauth=. Dropping it would silently stop it showing any outcome at all.""" c = TestClient(appmod.app) r = c.get(f"{V2}/discord/link/callback?error=access_denied", follow_redirects=False) loc = r.headers["location"] assert "discord=cancelled" in loc and "oauth=cancelled" in loc def test_users_me_still_carries_the_deprecated_discord_only_field(monkeypatch): """Renamed to oauth_only, but the deployed frontend still reads the old name.""" monkeypatch.setattr(oauth.httpx, "AsyncClient", _MockClient) c = TestClient(appmod.app) r = c.get(f"{V2}/discord/link/callback?code=abc" f"&state={oauth._sign_state(None, 'login', 'discord')}", follow_redirects=False) assert "discord=created" in r.headers["location"] me = c.get(f"{V2}/users/me").json() assert me["oauth_only"] is True and me["discord_only"] is True def test_legacy_login_start_is_still_mounted(): c = TestClient(appmod.app) r = c.get(f"{V2}/discord/login/start", follow_redirects=False) assert r.status_code == 303 assert r.headers["location"].startswith("https://discord.com/api/oauth2/authorize")