"""How a test gets a signed-in client, and the credential it signs in with. Phase 11 task 03 put every route except `PUBLIC_PATHS` behind a session, so a TestClient that used to `GET /api/config` now gets a 401 unless it signs in first. This module is the one place that does it, for the same reason `tests/card_contract.py` is the one place the card paths are written down: if signing in is a sequence twelve test files have to remember, one of them will remember it wrong and assert the 401. `sign_in` also copies the CSRF token onto the client's default headers, which is what keeps the twelve existing `client.post(`/`ups_client.post(` assertions in `tests/test_api.py` unedited: they get the session cookie *and* the write header for free, so the assertions still assert the UPS behaviour they were written for instead of the new gate. The password itself lives in `auth_contract.py` (with the cheap PBKDF2 cost the suite hashes it at) and is re-exported here, so `from auth_client import ADMIN_PASSWORD, sign_in` works and there is still exactly one file that says what the testing password is. """ from __future__ import annotations from typing import Any from fastapi.testclient import TestClient from auth_contract import ADMIN_PASSWORD, FAST_PBKDF2 # re-exported: one constant, two doors from dashboards import auth __all__ = ["ADMIN_PASSWORD", "FAST_PBKDF2", "sign_in"] def sign_in(client: TestClient, password: str = ADMIN_PASSWORD, **payload: Any) -> dict[str, Any]: """Log the client in and make its writes carry the CSRF token. Returns the login body (`{ok, username, expires_at, csrf, next}`) so a test can assert on it without repeating the request. The assertion that the sign-in *worked* is part of the helper: a test that silently started from a 401 would otherwise fail three lines later on something that looks like an unrelated bug. Pass extra keyword arguments to reach the parts of the endpoint the pages do not use (a `next`, or a body the login form would never send). """ response = client.post("/api/auth/login", json={"password": password, **payload}) assert response.status_code == 200, f"sign-in failed: {response.status_code} {response.text}" body: dict[str, Any] = response.json() # On the client, not per request: the ~200 existing HTTP assertions in test_api.py post to # the one write endpoint, and a token the helper did not attach would 403 every one of them. client.headers[auth.CSRF_HEADER] = str(body.get("csrf", "")) return body