"""'/' root redirect and the '/login' landing page.""" from __future__ import annotations from uuid import uuid4 from fastapi.testclient import TestClient from ragcore.application.auth import SessionService, SignedTokenCodec from ragcore.application.auth.ports import AuthorizationChallenge from ragcore.domain.auth import OidcClaims from ragcore.domain.control import Role from ragcore.main import create_app from ragcore.web.auth.dependencies import get_session_codec, get_session_service SECRET = b"0" * 32 class FakeOidcProvider: def __init__(self, claims: OidcClaims) -> None: self.claims = claims async def build_authorization_challenge(self, *, redirect_uri: str) -> AuthorizationChallenge: return AuthorizationChallenge( redirect_url="https://idp.example.test/authorize?mock=1", state="fixed-state-for-tests", nonce="fixed-nonce-for-tests", code_verifier="fixed-verifier-for-tests", ) async def complete_login( self, *, code: str, code_verifier: str, nonce: str, redirect_uri: str ) -> OidcClaims: return self.claims def _signed_in_client() -> TestClient: provider = FakeOidcProvider( OidcClaims(subject="alice", email="alice@example.test", groups=("ragcore-editors",)) ) service = SessionService( provider=provider, group_roles={"ragcore-editors": Role.EDITOR}, workspace_id=uuid4() ) codec = SignedTokenCodec(secret=SECRET) app = create_app() app.dependency_overrides[get_session_service] = lambda: service app.dependency_overrides[get_session_codec] = lambda: codec # SessionAuthenticationMiddleware reads the codec straight off app.state # (it runs outside FastAPI's dependency-injection system), so the # dependency override above -- which only affects the /auth/* routes -- # is not enough to make it recognize the cookie those routes just set. app.state.auth_session_codec = codec client = TestClient(app, follow_redirects=False) client.get("/auth/login") client.get("/auth/callback", params={"code": "auth-code", "state": "fixed-state-for-tests"}) return client def test_root_redirects_unauthenticated_visitors_to_login() -> None: client = TestClient(create_app(), follow_redirects=False) response = client.get("/") assert response.status_code == 302 assert response.headers["location"] == "/login" def test_root_redirects_authenticated_visitors_to_dashboard() -> None: client = _signed_in_client() response = client.get("/") assert response.status_code == 302 assert response.headers["location"] == "/admin/dashboard" def test_login_page_shows_not_configured_when_oidc_is_absent() -> None: client = TestClient(create_app(), follow_redirects=False) response = client.get("/login") assert response.status_code == 200 assert "No identity provider is configured" in response.text assert "Sign in with SSO" not in response.text assert "OIDC secured" not in response.text assert "Setup required" in response.text def test_login_page_offers_sso_when_oidc_is_configured() -> None: app = create_app() app.state.session_service = object() client = TestClient(app, follow_redirects=False) response = client.get("/login") assert response.status_code == 200 assert "Sign in with SSO" in response.text assert 'href="/auth/login"' in response.text def test_login_page_introduces_the_real_ragcore_value_flow() -> None: app = create_app() app.state.session_service = object() response = TestClient(app, follow_redirects=False).get("/login") assert response.status_code == 200 assert 'data-auth-stage="ragcore"' in response.text assert "Private knowledge. Verifiable answers." in response.text assert 'data-rag-flow="sources-retrieval-answers"' in response.text assert "Dense + sparse" in response.text assert "Cited answers" in response.text assert 'aria-describedby="sign-in-description"' in response.text assert "https://" not in response.text def test_login_page_declares_a_loadable_local_favicon() -> None: client = TestClient(create_app(), follow_redirects=False) response = client.get("/login") favicon = client.get("/static/favicon.svg") assert response.status_code == 200 assert '' in response.text assert favicon.status_code == 200 assert favicon.headers["content-type"].startswith("image/svg+xml") def test_login_page_redirects_an_already_authenticated_visitor() -> None: client = _signed_in_client() response = client.get("/login") assert response.status_code == 302 assert response.headers["location"] == "/admin/dashboard"