104 lines
4.3 KiB
Python
104 lines
4.3 KiB
Python
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()
|