| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- """Shared test data and mock builders for OIDC icon tests (#1333).
- Cross-imported from ``backend.tests.unit.*`` and ``backend.tests.integration.*``
- following the established pattern (see ``backend/tests/unit/services/conftest.py``
- which imports ``mock_ftp_server``).
- Two mock builders are provided because ``fetch_icon`` evolved from a single
- ``client.get(...)`` call into a streaming ``client.stream(...).aiter_bytes()``
- loop:
- * ``build_get_icon_mock`` — the pre-streaming pattern, kept for tests that
- exercise httpx.AsyncClient.get() directly (e.g. routes that use httpx
- outside the icon-fetcher).
- * ``build_streaming_icon_mock`` — the current ``fetch_icon`` pattern; tests
- that exercise the size-cap early-exit need this.
- Both produce ``(MockHttpxClient, call_recorder)`` tuples for patching.
- """
- import hashlib
- from types import SimpleNamespace
- from unittest.mock import AsyncMock
- # Tiny valid 1×1 transparent PNG (~70 bytes) — small enough to fit in one
- # 4 KB chunk during streaming tests; suitable for the happy-path everywhere.
- PNG_BYTES = bytes.fromhex(
- "89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4"
- "890000000d49444154789c63000100000005000100"
- "0d0a2db40000000049454e44ae426082"
- )
- PNG_ETAG = hashlib.sha256(PNG_BYTES).hexdigest()
- def build_get_icon_mock(
- *,
- body: bytes = PNG_BYTES,
- content_type: str | None = "image/png",
- status_code: int = 200,
- ):
- """Returns ``(MockHttpxClient, mock_get)`` for the pre-streaming pattern.
- ``mock_get`` is an ``AsyncMock`` so tests can assert call count and
- inspect kwargs (e.g. ``follow_redirects=False``).
- Passing ``content_type=None`` produces a response with no Content-Type
- header at all (distinct from ``""``) — used to exercise the missing-
- header branch.
- """
- headers: dict[str, str] = {"content-type": content_type} if content_type is not None else {}
- response = SimpleNamespace(status_code=status_code, headers=headers, content=body)
- mock_get = AsyncMock(return_value=response)
- class _MockHttpxClient:
- def __init__(self, *_args, **_kwargs):
- pass
- async def __aenter__(self):
- return SimpleNamespace(get=mock_get)
- async def __aexit__(self, *_exc):
- return False
- return _MockHttpxClient, mock_get
- def build_streaming_icon_mock(
- *,
- body: bytes = PNG_BYTES,
- content_type: str | None = "image/png",
- status_code: int = 200,
- chunk_size: int = 4096,
- ):
- """Returns ``(MockHttpxClient, stream_recorder)`` for the current
- ``client.stream("GET", url, follow_redirects=...)`` + ``aiter_bytes()`` path.
- ``stream_recorder`` is an ``AsyncMock`` that records every ``.stream()``
- call so tests can assert e.g. ``follow_redirects=False`` was passed.
- ``body`` is emitted in ``chunk_size``-byte chunks. Tests of the size-cap
- early-exit should pick a chunk_size that crosses the cap mid-stream
- (e.g. body = 2 MB, chunk_size = 4096 → cap fires after ~256 chunks
- without buffering the whole payload).
- """
- headers: dict[str, str] = {"content-type": content_type} if content_type is not None else {}
- stream_recorder = AsyncMock()
- async def _aiter_bytes():
- for i in range(0, len(body), chunk_size):
- yield body[i : i + chunk_size]
- response = SimpleNamespace(
- status_code=status_code,
- headers=headers,
- aiter_bytes=_aiter_bytes,
- )
- class _StreamCtx:
- def __init__(self, *args, **kwargs):
- # Record the .stream() call positional + keyword args so tests
- # can assert `follow_redirects=False` etc.
- stream_recorder(*args, **kwargs)
- async def __aenter__(self):
- return response
- async def __aexit__(self, *_exc):
- return False
- class _MockHttpxClient:
- def __init__(self, *_args, **_kwargs):
- pass
- async def __aenter__(self):
- return self
- async def __aexit__(self, *_exc):
- return False
- def stream(self, *args, **kwargs):
- return _StreamCtx(*args, **kwargs)
- return _MockHttpxClient, stream_recorder
|