# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """Where a command's seconds actually go, recorded as it goes. ``DOCS-latency.md`` established the numbers that matter by reading three journal lines by hand, once, and its own rule is that an upstream latency claim gets measured before it gets designed around. These tests cover the seam that makes the measurement continuous instead of anecdotal. """ from __future__ import annotations from collections.abc import Callable import httpx import pytest from signal_bot.analytics import probe from signal_bot.http_client import ImageFetcher IMAGE = b"\x89PNG\r\n\x1a\nfake" MockHandler = Callable[[httpx.Request], httpx.Response] def _fetcher(handler: MockHandler) -> ImageFetcher: return ImageFetcher(httpx.AsyncClient(transport=httpx.MockTransport(handler))) def _ok(_request: httpx.Request) -> httpx.Response: return httpx.Response(200, content=IMAGE) async def test_a_fetch_records_its_host_and_status() -> None: """One row per outbound request, per invocation.""" fetcher = _fetcher(_ok) with probe.probing() as notes: await fetcher.fetch_image("https://cdn.example.test/cat.png") (call,) = notes.calls assert call.host == "cdn.example.test" assert call.status == 200 assert call.ms >= 0 async def test_only_the_host_is_recorded_never_the_path() -> None: """A URL carries identifiers; item_id is where those are governed. The poem URL carries a Wolne Lektury slug, which is exactly the kind of thing that must be a deliberate column rather than a side effect of timing a request. """ fetcher = _fetcher(_ok) with probe.probing() as notes: await fetcher.fetch_image("https://wolnelektury.test/lektura/some-slug.txt") (call,) = notes.calls assert call.host == "wolnelektury.test" assert "slug" not in repr(call) async def test_a_json_fetch_and_its_image_are_two_calls() -> None: """fetch_base64 is a lookup and a download, and both cost time.""" def handler(request: httpx.Request) -> httpx.Response: if "search" in str(request.url): return httpx.Response(200, json=[{"url": "https://cdn.example.test/cat.png"}]) return httpx.Response(200, content=IMAGE) fetcher = _fetcher(handler) with probe.probing() as notes: await fetcher.fetch_base64("https://api.example.test/search", [0, "url"]) assert [call.host for call in notes.calls] == [ "api.example.test", "cdn.example.test", ] async def test_a_bad_status_is_recorded_and_still_raises() -> None: """Upstream answering badly is data; it must not become silence.""" fetcher = _fetcher(lambda _request: httpx.Response(503)) with probe.probing() as notes, pytest.raises(httpx.HTTPStatusError): await fetcher.fetch_image("https://cdn.example.test/cat.png") (call,) = notes.calls assert call.status == 503 async def test_a_request_that_never_answered_is_recorded_as_zero() -> None: """DNS, connect and timeout failures never reach a status code. Losing them would make an upstream that is entirely down look merely unused, which is the opposite of what the health panel is for. """ refused = "connection refused" def refuse(request: httpx.Request) -> httpx.Response: raise httpx.ConnectError(refused, request=request) fetcher = _fetcher(refuse) with probe.probing() as notes, pytest.raises(httpx.ConnectError): await fetcher.fetch_image("https://cdn.example.test/cat.png") (call,) = notes.calls assert call.status == probe.NO_RESPONSE async def test_a_fetch_outside_a_probe_records_nothing() -> None: """The offline runs and most tests drive the fetcher with no dispatcher.""" fetcher = _fetcher(_ok) await fetcher.fetch_image("https://cdn.example.test/cat.png") assert probe.CURRENT.get() is None async def test_a_send_is_timed_even_when_it_fails() -> None: """A failed send still cost the seconds it took to fail.""" async def boom() -> None: async with probe.timed_send(): raise RuntimeError with probe.probing() as notes, pytest.raises(RuntimeError): await boom() assert notes.send_ms >= 0 async def test_two_sends_accumulate() -> None: """!puƂapka sends a picture and then its description.""" with probe.probing() as notes: async with probe.timed_send(): pass async with probe.timed_send(): pass assert notes.send_ms >= 0