|
| 1 | +import socket |
| 2 | +import threading |
| 3 | + |
| 4 | +import pytest |
| 5 | +import responses |
| 6 | +from requests.adapters import HTTPAdapter |
| 7 | + |
| 8 | +from galaxy.util import requests as galaxy_requests |
| 9 | +from galaxy.util.requests import DEFAULT_RETRIES |
| 10 | +from galaxy.util.user_agent import get_default_headers |
| 11 | + |
| 12 | +EXPECTED_USER_AGENT = get_default_headers()["user-agent"] |
| 13 | + |
| 14 | + |
| 15 | +# --- User-Agent header injection --- |
| 16 | + |
| 17 | + |
| 18 | +@pytest.mark.parametrize("method", ("delete", "get", "head", "options", "patch", "post", "put")) |
| 19 | +@responses.activate |
| 20 | +def test_user_agent_and_caller_headers_are_set(method: str): |
| 21 | + """All methods inject the Galaxy user-agent and preserve caller-supplied headers.""" |
| 22 | + responses.add(getattr(responses, method.upper()), "https://example.com/", status=200) |
| 23 | + getattr(galaxy_requests, method)("https://example.com/", headers={"X-Custom": "value"}) |
| 24 | + req_headers = responses.calls[0].request.headers |
| 25 | + assert req_headers["user-agent"] == EXPECTED_USER_AGENT |
| 26 | + assert req_headers["X-Custom"] == "value" |
| 27 | + |
| 28 | + |
| 29 | +# --- Session factory --- |
| 30 | + |
| 31 | + |
| 32 | +@pytest.mark.parametrize("method", (galaxy_requests.Session, galaxy_requests.session)) |
| 33 | +def test_Session_has_user_agent_header(method): |
| 34 | + s: galaxy_requests.Session = method() |
| 35 | + assert s.headers["user-agent"] == EXPECTED_USER_AGENT |
| 36 | + |
| 37 | + |
| 38 | +def test_Session_has_no_retry_adapter(): |
| 39 | + s = galaxy_requests.Session() |
| 40 | + for url in ("https://example.com/", "http://example.com/"): |
| 41 | + adapter = s.get_adapter(url) |
| 42 | + assert isinstance(adapter, HTTPAdapter) |
| 43 | + assert adapter.max_retries.total == 0 # requests default: no retries |
| 44 | + |
| 45 | + |
| 46 | +# --- RetrySession --- |
| 47 | + |
| 48 | + |
| 49 | +def test_RetrySession_has_user_agent_header(): |
| 50 | + s = galaxy_requests.RetrySession() |
| 51 | + assert s.headers["user-agent"] == EXPECTED_USER_AGENT |
| 52 | + |
| 53 | + |
| 54 | +def test_retry_session_has_retry_adapter(): |
| 55 | + s = galaxy_requests.RetrySession() |
| 56 | + for url in ("https://example.com/", "http://example.com/"): |
| 57 | + adapter = s.get_adapter(url) |
| 58 | + assert isinstance(adapter, HTTPAdapter) |
| 59 | + assert adapter.max_retries.total == DEFAULT_RETRIES |
| 60 | + |
| 61 | + |
| 62 | +# --- Retry behaviour --- |
| 63 | + |
| 64 | + |
| 65 | +@pytest.fixture() |
| 66 | +def connection_reset_server(): |
| 67 | + """A TCP server that accepts connections and immediately closes them.""" |
| 68 | + attempts = 0 |
| 69 | + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 70 | + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 71 | + sock.bind(("127.0.0.1", 0)) |
| 72 | + port = sock.getsockname()[1] |
| 73 | + sock.listen(10) |
| 74 | + stop = threading.Event() |
| 75 | + sock.settimeout(0.1) |
| 76 | + |
| 77 | + def serve(): |
| 78 | + nonlocal attempts |
| 79 | + while not stop.is_set(): |
| 80 | + try: |
| 81 | + conn, _ = sock.accept() |
| 82 | + except socket.timeout: # noqa: UP041 # Python <=3.9 support, replace with TimeoutError in Python 3.10+ |
| 83 | + continue |
| 84 | + except OSError: |
| 85 | + break |
| 86 | + attempts += 1 |
| 87 | + conn.close() |
| 88 | + |
| 89 | + thread = threading.Thread(target=serve, daemon=True) |
| 90 | + thread.start() |
| 91 | + yield f"http://127.0.0.1:{port}", lambda: attempts |
| 92 | + stop.set() |
| 93 | + sock.close() |
| 94 | + thread.join() |
| 95 | + |
| 96 | + |
| 97 | +@pytest.mark.parametrize("method", ("delete", "get", "head", "options", "put")) |
| 98 | +def test_RetrySession_retries_idempotent_methods(method: str, connection_reset_server): |
| 99 | + """RetrySession retries idempotent methods on connection failure.""" |
| 100 | + url, get_attempts = connection_reset_server |
| 101 | + with galaxy_requests.RetrySession() as s: |
| 102 | + with pytest.raises(galaxy_requests.exceptions.ConnectionError): |
| 103 | + getattr(s, method)(url) |
| 104 | + assert get_attempts() == 1 + DEFAULT_RETRIES |
0 commit comments