feat(cloud): harden multi-tenant runtime resources

This commit is contained in:
Junyan Qin
2026-07-29 11:32:26 +08:00
parent 32abbb636f
commit ae85ac2b16
211 changed files with 14963 additions and 1968 deletions
+88
View File
@@ -6,8 +6,13 @@ Tests session management, reuse, and cleanup.
from __future__ import annotations
import threading
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
import aiohttp
import httpx
from aiohttp import web
from langbot.pkg.utils import httpclient
@@ -88,6 +93,89 @@ class TestCloseAll:
assert len(httpclient._sessions) == 0
class TestReadLimited:
async def test_rejects_oversized_content_length_before_reading(self):
content = SimpleNamespace(iter_chunked=None)
response = SimpleNamespace(headers={'Content-Length': '11'}, content=content)
with pytest.raises(httpclient.RemoteResponseTooLargeError):
await httpclient.read_limited(response, max_bytes=10)
async def test_rejects_chunked_body_that_crosses_limit(self):
class Content:
async def iter_chunked(self, _chunk_size):
yield b'12345'
yield b'678901'
response = SimpleNamespace(headers={}, content=Content())
with pytest.raises(httpclient.RemoteResponseTooLargeError):
await httpclient.read_limited(response, max_bytes=10)
async def test_returns_body_within_limit(self):
class Content:
async def iter_chunked(self, _chunk_size):
yield b'12345'
yield b'67890'
response = SimpleNamespace(headers={}, content=Content())
assert await httpclient.read_limited(response, max_bytes=10) == b'1234567890'
async def test_json_reader_uses_same_limit(self):
class Content:
async def iter_chunked(self, _chunk_size):
yield b'{"ok":true}'
response = SimpleNamespace(
headers={},
content=Content(),
)
assert await httpclient.read_json_limited(response, max_bytes=16) == {'ok': True}
async def test_response_json_parse_runs_off_event_loop(self):
event_loop_thread = threading.get_ident()
response = SimpleNamespace(json=lambda: threading.get_ident())
assert await httpclient.parse_json_response(response) != event_loop_thread
async def test_response_json_parse_supports_async_test_doubles(self):
response = SimpleNamespace(json=AsyncMock(return_value={'ok': True}))
assert await httpclient.parse_json_response(response) == {'ok': True}
async def test_response_text_runs_off_loop_and_caps_diagnostics(self):
event_loop_thread = threading.get_ident()
class Response:
@property
def text(self):
return f'{threading.get_ident()}:abcdef'
value = await httpclient.response_text(Response(), max_chars=4)
assert not value.startswith(str(event_loop_thread))
assert value.endswith('[truncated]')
async def test_httpx_hook_rejects_before_automatic_buffer_grows(self):
class Source(httpx.AsyncByteStream):
async def __aiter__(self):
yield b'123'
yield b'45'
async def aclose(self):
return None
transport = httpx.MockTransport(lambda _request: httpx.Response(200, stream=Source()))
async with httpx.AsyncClient(
transport=transport,
event_hooks=httpclient.httpx_response_limit_hooks(max_bytes=4),
) as client:
with pytest.raises(httpclient.RemoteResponseTooLargeError, match='4-byte'):
await client.get('https://example.invalid')
async def test_close_all_handles_already_closed(self):
"""close_all handles already closed sessions gracefully."""
session = httpclient.get_session()