# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """The attachment store: listing, fetching and deleting the container's copy. Split out of test_transport_rest.py when !add_photo added the fetch half and that file crossed the 250-line cap. These four endpoints are one subject: the copy signal-cli keeps of every inbound attachment, which lives outside the album's 0700 and which !add_photo is the first thing to delete deliberately. """ from __future__ import annotations from collections.abc import Callable from typing import TYPE_CHECKING import httpx import pytest from signal_bot.transport_rest import RestTransport, TransportError if TYPE_CHECKING: from signal_bot.config import Config # The callable httpx.MockTransport expects -- not a bot command handler. MockHandler = Callable[[httpx.Request], httpx.Response] def _transport(config: Config, handler: MockHandler) -> RestTransport: """A transport whose every HTTP call is answered by ``handler``.""" return RestTransport(config, httpx.AsyncClient(transport=httpx.MockTransport(handler))) async def test_list_attachments(config: Config) -> None: transport = _transport(config, lambda r: httpx.Response(200, json=["a", "b"])) assert await transport.list_attachments() == ["a", "b"] async def test_list_attachments_tolerates_non_list(config: Config) -> None: transport = _transport(config, lambda r: httpx.Response(200, json={"oops": 1})) assert await transport.list_attachments() == [] async def test_delete_attachment(config: Config) -> None: seen: list[str] = [] def handler(request: httpx.Request) -> httpx.Response: seen.append(str(request.url)) return httpx.Response(204) await _transport(config, handler).delete_attachment("abc") assert seen[0].endswith("/v1/attachments/abc") async def test_delete_attachment_raises_on_failure(config: Config) -> None: transport = _transport(config, lambda r: httpx.Response(500)) with pytest.raises(TransportError, match="500"): await transport.delete_attachment("abc") async def test_fetch_attachment_returns_the_bytes(config: Config) -> None: """An envelope names an attachment but never carries it; this fetches it.""" seen: list[str] = [] def handler(request: httpx.Request) -> httpx.Response: seen.append(str(request.url)) return httpx.Response(200, content=b"\xff\xd8\xffphoto") assert await _transport(config, handler).fetch_attachment("abc") == b"\xff\xd8\xffphoto" assert seen[0].endswith("/v1/attachments/abc") async def test_fetch_attachment_raises_when_the_container_has_not_got_it( config: Config, ) -> None: transport = _transport(config, lambda r: httpx.Response(404)) with pytest.raises(TransportError, match="404"): await transport.fetch_attachment("gone")