from __future__ import annotations import unittest from fastapi import APIRouter, Response from fastapi.responses import PlainTextResponse from fastapi.testclient import TestClient from govoplan_core.core.registry import PlatformRegistry from govoplan_core.server.fastapi import create_govoplan_app from govoplan_core.server.platform import create_platform_router class ConditionalRequestTests(unittest.TestCase): def _client(self) -> TestClient: router = APIRouter() @router.get("/json") def json_payload(value: str = "alpha") -> dict[str, str]: return {"value": value} @router.get("/cookie") def cookie_payload(response: Response) -> dict[str, bool]: response.set_cookie("govoplan-test", "changed") return {"ok": True} @router.get("/text") def text_payload() -> PlainTextResponse: return PlainTextResponse("plain") app = create_govoplan_app( title="conditional request test", version="test", registry=PlatformRegistry(), api_router=router, ) return TestClient(app) def test_json_get_receives_private_etag_and_matching_request_returns_304(self) -> None: with self._client() as client: first = client.get("/json", headers={"X-Request-ID": "request-1"}) self.assertEqual(200, first.status_code, first.text) self.assertEqual({"value": "alpha"}, first.json()) etag = first.headers.get("etag") self.assertIsNotNone(etag) self.assertTrue(etag.startswith('W/"sha256-'), etag) self.assertIn("private", first.headers.get("cache-control", "")) self.assertIn("no-cache", first.headers.get("cache-control", "")) self.assertIn("authorization", first.headers.get("vary", "").lower()) self.assertEqual("request-1", first.headers["X-Correlation-ID"]) second = client.get("/json", headers={"If-None-Match": etag or "", "X-Request-ID": "request-2"}) self.assertEqual(304, second.status_code, second.text) self.assertEqual(b"", second.content) self.assertEqual(etag, second.headers.get("etag")) self.assertEqual("request-2", second.headers["X-Correlation-ID"]) def test_changed_json_body_does_not_match_previous_etag(self) -> None: with self._client() as client: first = client.get("/json?value=alpha") etag = first.headers.get("etag") self.assertIsNotNone(etag) changed = client.get("/json?value=beta", headers={"If-None-Match": etag or ""}) self.assertEqual(200, changed.status_code, changed.text) self.assertEqual({"value": "beta"}, changed.json()) self.assertNotEqual(etag, changed.headers.get("etag")) def test_conditional_middleware_skips_cookie_and_non_json_responses(self) -> None: with self._client() as client: cookie = client.get("/cookie") self.assertEqual(200, cookie.status_code, cookie.text) self.assertNotIn("etag", cookie.headers) text = client.get("/text") self.assertEqual(200, text.status_code, text.text) self.assertNotIn("etag", text.headers) def test_platform_endpoints_return_304_for_matching_if_none_match(self) -> None: api_router = APIRouter(prefix="/api/v1") api_router.include_router(create_platform_router()) app = create_govoplan_app( title="conditional platform endpoint test", version="test", registry=PlatformRegistry(), api_router=api_router, ) with TestClient(app) as client: for path in ("/api/v1/platform/status", "/api/v1/platform/modules"): first = client.get(path) self.assertEqual(200, first.status_code, first.text) etag = first.headers.get("etag") self.assertIsNotNone(etag, path) second = client.get(path, headers={"If-None-Match": etag or ""}) self.assertEqual(304, second.status_code, second.text) self.assertEqual(b"", second.content) self.assertEqual(etag, second.headers.get("etag")) if __name__ == "__main__": unittest.main()