fix(core): preserve data integrity and bound shared UI and response work
Module Package Release / publish-packages (push) Successful in 13s
Module Package Release / publish-packages (push) Successful in 13s
Release v0.1.46. Coordinated integrity review: GovOPlaN/govoplan-core#298.
This commit is contained in:
@@ -1,15 +1,85 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from fastapi import APIRouter, Response
|
||||
from fastapi import APIRouter, Request, Response
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from fastapi.testclient import TestClient
|
||||
from starlette.background import BackgroundTask
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
from govoplan_core.auth import get_api_principal
|
||||
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
|
||||
from govoplan_core.server.conditional_requests import conditional_json_get_middleware
|
||||
|
||||
|
||||
class ConditionalBufferTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_unknown_length_overflow_replays_exact_bytes_without_eager_drain(self) -> None:
|
||||
chunks = [b'{"value":"', b'', b'a' * 32, b'b' * 32, b'c' * 32, b'"}']
|
||||
consumed = []
|
||||
|
||||
async def body():
|
||||
for chunk in chunks:
|
||||
consumed.append(chunk)
|
||||
yield chunk
|
||||
|
||||
background = BackgroundTask(lambda: None)
|
||||
response = StreamingResponse(body(), media_type="application/json", background=background)
|
||||
|
||||
async def route(request):
|
||||
return response
|
||||
|
||||
request = Request({"type": "http", "method": "GET", "headers": [(b'if-none-match', b'*')]})
|
||||
with patch("govoplan_core.server.conditional_requests.MAX_CONDITIONAL_JSON_BYTES", 64, create=True):
|
||||
result = await conditional_json_get_middleware(request, route)
|
||||
self.assertIs(result, response)
|
||||
self.assertEqual(4, len(consumed))
|
||||
self.assertEqual(200, result.status_code)
|
||||
self.assertNotIn("etag", result.headers)
|
||||
self.assertIn("private", result.headers["cache-control"])
|
||||
self.assertIn("Authorization", result.headers["vary"])
|
||||
self.assertIs(background, result.background)
|
||||
self.assertEqual(b"".join(chunks), b"".join([chunk async for chunk in result.body_iterator]))
|
||||
self.assertEqual(chunks, consumed)
|
||||
|
||||
async def test_known_large_body_is_not_consumed(self) -> None:
|
||||
consumed = []
|
||||
|
||||
async def body():
|
||||
consumed.append(True)
|
||||
yield b"x" * 65
|
||||
|
||||
response = StreamingResponse(body(), media_type="application/json", headers={"Content-Length": "65"})
|
||||
|
||||
async def route(request):
|
||||
return response
|
||||
|
||||
request = Request({"type": "http", "method": "GET", "headers": []})
|
||||
with patch("govoplan_core.server.conditional_requests.MAX_CONDITIONAL_JSON_BYTES", 64, create=True):
|
||||
result = await conditional_json_get_middleware(request, route)
|
||||
self.assertIs(result, response)
|
||||
self.assertEqual([], consumed)
|
||||
self.assertEqual("65", result.headers["content-length"])
|
||||
self.assertEqual(b"x" * 65, b"".join([chunk async for chunk in result.body_iterator]))
|
||||
|
||||
async def test_matching_small_response_still_runs_current_route_authorization(self) -> None:
|
||||
calls = []
|
||||
|
||||
async def route(request):
|
||||
calls.append(True)
|
||||
if len(calls) > 1:
|
||||
return Response(status_code=403)
|
||||
return StreamingResponse(iter([b'{"ok":true}']), media_type="application/json")
|
||||
|
||||
request = Request({"type": "http", "method": "GET", "headers": []})
|
||||
first = await conditional_json_get_middleware(request, route)
|
||||
conditional = Request({"type": "http", "method": "GET", "headers": [(b'if-none-match', first.headers['etag'].encode())]})
|
||||
second = await conditional_json_get_middleware(conditional, route)
|
||||
self.assertEqual(403, second.status_code)
|
||||
self.assertEqual(2, len(calls))
|
||||
|
||||
|
||||
class ConditionalRequestTests(unittest.TestCase):
|
||||
|
||||
Reference in New Issue
Block a user