""" End-to-end CORS wiring for the one browser-facing REST surface. The frontend's Archive page calls GET /calls/search with Authorization + Content-Type headers, which forces the browser to send a CORS preflight first. Before #110 that OPTIONS got a bare 405 with no Access-Control-* headers and the fetch failed with "TypeError: Failed to fetch". These tests drive the real app through TestClient so a regression in the middleware wiring (not just the helper) is caught. TestClient is NOT used as a context manager on purpose: that would run the lifespan (mqtt_handler.connect(), the sweeper loops, dynsec bootstrap), none of which is needed here -- CORSMiddleware answers a preflight before routing or dependencies run. """ from fastapi.testclient import TestClient from app.config import settings from app.main import app client = TestClient(app) ALLOWED_ORIGIN = "https://drb.cusano.net" DISALLOWED_ORIGIN = "https://evil.example.com" def test_default_allowed_origin_matches_the_deployed_frontend(): # The frontend is served on the bare domain (infra Caddyfile.j2), so the # default must allow exactly that origin without any env override. assert ALLOWED_ORIGIN in settings.cors_origins def test_preflight_for_calls_search_is_allowed(): resp = client.options( "/calls/search", headers={ "Origin": ALLOWED_ORIGIN, "Access-Control-Request-Method": "GET", "Access-Control-Request-Headers": "authorization,content-type", }, ) assert resp.status_code == 200 assert resp.headers.get("access-control-allow-origin") == ALLOWED_ORIGIN allow_methods = resp.headers.get("access-control-allow-methods", "").upper() assert "GET" in allow_methods # Bearer auth, not cookies -- credentials must never be advertised. assert "access-control-allow-credentials" not in resp.headers def test_preflight_from_disallowed_origin_gets_no_allow_origin(): resp = client.options( "/calls/search", headers={ "Origin": DISALLOWED_ORIGIN, "Access-Control-Request-Method": "GET", }, ) assert resp.headers.get("access-control-allow-origin") is None def test_simple_get_from_allowed_origin_is_annotated(): # Even a non-preflight GET must carry Access-Control-Allow-Origin or the # browser hides the response body from the page. resp = client.get("/health", headers={"Origin": ALLOWED_ORIGIN}) assert resp.status_code == 200 assert resp.headers.get("access-control-allow-origin") == ALLOWED_ORIGIN