"""Slow Docker calls must fail with a message, not a bare 500. A volume or image prune that deletes a lot of data easily runs past docker-py's 60s read timeout. requests' ``ReadTimeout`` is not a ``DockerException``, so it slipped past ``safe_call`` and the UI only showed "Request failed with status code 500". Prunes now use a client with a long timeout, and any transport timeout that still happens is turned into a ``DockerError`` (-> 502 + detail). """ from __future__ import annotations import pytest import requests def test_safe_call_wraps_read_timeout(): from docker_client import DockerError, safe_call def slow(): raise requests.exceptions.ReadTimeout("Read timed out. (read timeout=60)") with pytest.raises(DockerError) as info: safe_call(slow) assert info.value.error == "docker_timeout" def test_safe_call_wraps_connection_error(): from docker_client import DockerError, safe_call def gone(): raise requests.exceptions.ConnectionError("socket went away") with pytest.raises(DockerError) as info: safe_call(gone) assert info.value.error == "docker_unavailable" def test_volume_prune_timeout_is_a_502_with_detail(as_admin, monkeypatch): from services import volume_service class Api: def containers(self, all=False): raise requests.exceptions.ReadTimeout("Read timed out.") class Client: api = Api() monkeypatch.setattr(volume_service, "get_long_client", lambda: Client()) r = as_admin.post("/api/volumes/prune") assert r.status_code == 502 assert r.json()["error"] == "docker_timeout" # The image, volume and network prunes go item by item and have their own # tests (test_image_prune.py, test_volume_prune.py, test_network_prune.py), # including that they use the long-timeout client.