"""ラクマ 会话层测试:并发限流下的重试与错误映射 全部用 httpx.MockTransport 拦截,不触达真实站点。 """ from __future__ import annotations import httpx import pytest from app.core import rakuma_site as site from app.core.config import Settings from app.core.errors import ItemNotFoundError, UpstreamRequestError from app.services.rakuma_session import RakumaSession TARGET = "https://fril.jp/s?query=switch" GOOD_PAGE = '
21件中 1 - 21件
' def make_settings(**overrides) -> Settings: base = { "http_max_attempts": 3, "request_timeout_seconds": 5.0, "max_site_concurrency": 4, } base.update(overrides) return Settings(**base) async def build_session(handler, *, settings=None) -> RakumaSession: """构建 RakumaSession 并把 client 换成 MockTransport 版本""" session = RakumaSession(settings or make_settings()) await session.start() await session._client.aclose() session._client = httpx.AsyncClient( headers=site.default_headers(), transport=httpx.MockTransport(handler), follow_redirects=True, ) return session async def test_fetch_returns_page_without_warmup(): """ラクマ 无 Akamai 限速,不应像乐天那样先打一次首页预热""" seen: list[str] = [] def handler(request: httpx.Request) -> httpx.Response: seen.append(str(request.url)) return httpx.Response(200, text=GOOD_PAGE) session = await build_session(handler) try: assert await session.fetch_html(TARGET) == GOOD_PAGE finally: await session.close() assert seen == [TARGET] async def test_404_fails_fast_without_retrying(): """商品下架或 ID 不存在时重试没有意义""" attempts = {"n": 0} def handler(request: httpx.Request) -> httpx.Response: attempts["n"] += 1 return httpx.Response(404, text="not found") session = await build_session(handler) try: with pytest.raises(ItemNotFoundError): await session.fetch_html("https://item.fril.jp/deadbeef") finally: await session.close() assert attempts["n"] == 1 async def test_retry_recovers_when_a_later_attempt_succeeds(): attempts = {"n": 0} def handler(request: httpx.Request) -> httpx.Response: attempts["n"] += 1 if attempts["n"] == 1: return httpx.Response(503, text="unavailable") return httpx.Response(200, text=GOOD_PAGE) session = await build_session(handler) try: assert await session.fetch_html(TARGET) == GOOD_PAGE finally: await session.close() assert attempts["n"] == 2 async def test_persistent_5xx_surfaces_as_request_error(): def handler(request: httpx.Request) -> httpx.Response: return httpx.Response(503, text="unavailable") session = await build_session(handler) try: with pytest.raises(UpstreamRequestError): await session.fetch_html(TARGET) finally: await session.close() async def test_status_reports_readiness(): def handler(request: httpx.Request) -> httpx.Response: return httpx.Response(200, text=GOOD_PAGE) session = RakumaSession(make_settings()) assert session.status()["ready"] is False session = await build_session(handler) try: assert session.status()["ready"] is True finally: await session.close() assert session.status()["ready"] is False