# -*- coding: utf-8 -*- """异步 HTTP 客户端测试:连接复用、有界重试、4xx 不重试、响应体上限。""" import pytest from aiohttp import web from core.http_client import AsyncHTTPClient, UpstreamHTTPError, UpstreamTooLarge @pytest.fixture() async def server(): """本地 aiohttp 测试服务器。""" hits = {'flaky': 0} async def ok(request): return web.json_response({'ok': True}) async def flaky(request): # 首次 500,之后 200 —— 验证 5xx 退避重试 hits['flaky'] += 1 if hits['flaky'] == 1: return web.Response(status=500, text='boom') return web.json_response({'retried': True}) async def notfound(request): return web.Response(status=404, text='nope') async def big(request): return web.Response(body=b'x' * (2 * 1024 * 1024)) app = web.Application() app.router.add_get('/ok', ok) app.router.add_get('/flaky', flaky) app.router.add_get('/notfound', notfound) app.router.add_get('/big', big) runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, '127.0.0.1', 0) await site.start() port = site._server.sockets[0].getsockname()[1] base = f'http://127.0.0.1:{port}' try: yield base, hits finally: await runner.cleanup() async def test_get_json(server): base, _ = server c = AsyncHTTPClient() try: assert await c.get_json(base + '/ok') == {'ok': True} finally: await c.close() async def test_retry_on_5xx(server): base, hits = server c = AsyncHTTPClient() try: data = await c.get_json(base + '/flaky') assert data == {'retried': True} assert hits['flaky'] == 2 # 第一次 500 + 重试成功 finally: await c.close() async def test_no_retry_on_4xx(server): base, _ = server c = AsyncHTTPClient() try: with pytest.raises(UpstreamHTTPError) as ei: await c.get_text(base + '/notfound') assert ei.value.status == 404 finally: await c.close() async def test_response_size_limit(server): base, _ = server c = AsyncHTTPClient(max_bytes=1024 * 1024) # 1MB 上限,服务端返回 2MB try: with pytest.raises(UpstreamTooLarge): await c.get_text(base + '/big') finally: await c.close() async def test_session_reuse(server): """同一客户端多次请求复用同一(按 loop 隔离的)session —— 连接池生效。""" base, _ = server c = AsyncHTTPClient() try: await c.get_json(base + '/ok') s1 = await c._session_for_loop() await c.get_json(base + '/ok') s2 = await c._session_for_loop() assert s1 is s2 and not s1.closed finally: await c.close() async def test_stream(server): base, _ = server c = AsyncHTTPClient() try: async with c.stream('GET', base + '/ok') as resp: assert resp.status == 200 body = await resp.read() assert b'ok' in body finally: await c.close()