Public Access
DEVX-156: fix: add tenacity retry to install_tools._download for transient network failures
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
import urllib.error
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -68,6 +69,67 @@ class TestDownload:
|
||||
mock_urlopen.assert_called_once()
|
||||
assert dest.read_bytes() == b"data"
|
||||
|
||||
def test_download_retries_on_transient_error(self, tmp_path: Path) -> None:
|
||||
"""Download retries on URLError then succeeds."""
|
||||
dest = tmp_path / "file.bin"
|
||||
call_count = [0]
|
||||
|
||||
class _FakeResponse:
|
||||
def __init__(self) -> None:
|
||||
self._sent = False
|
||||
|
||||
def __enter__(self) -> _FakeResponse:
|
||||
return self
|
||||
|
||||
def __exit__(self, *args: object) -> None:
|
||||
pass
|
||||
|
||||
def read(self, n: int = -1) -> bytes:
|
||||
if self._sent:
|
||||
return b""
|
||||
self._sent = True
|
||||
return b"data"
|
||||
|
||||
def _flaky_urlopen(url: str, timeout: int = 60):
|
||||
call_count[0] += 1
|
||||
if call_count[0] < 2:
|
||||
raise urllib.error.URLError("Remote end closed connection")
|
||||
return _FakeResponse()
|
||||
|
||||
with patch("urllib.request.urlopen", side_effect=_flaky_urlopen):
|
||||
install_tools._download("https://example.com/file", dest, _sleep=lambda _: None)
|
||||
assert call_count[0] == 2
|
||||
assert dest.read_bytes() == b"data"
|
||||
|
||||
def test_download_fails_after_max_retries(self, tmp_path: Path) -> None:
|
||||
"""Download raises after MAX_DOWNLOAD_RETRIES attempts."""
|
||||
dest = tmp_path / "file.bin"
|
||||
call_count = [0]
|
||||
|
||||
def _always_fail(url: str, timeout: int = 60):
|
||||
call_count[0] += 1
|
||||
raise urllib.error.URLError("Remote end closed connection")
|
||||
|
||||
with patch("urllib.request.urlopen", side_effect=_always_fail):
|
||||
with pytest.raises(urllib.error.URLError):
|
||||
install_tools._download("https://example.com/file", dest, _sleep=lambda _: None)
|
||||
assert call_count[0] == install_tools.MAX_DOWNLOAD_RETRIES
|
||||
assert not dest.exists()
|
||||
|
||||
def test_download_no_retry_on_non_transient_error(self, tmp_path: Path) -> None:
|
||||
"""Download does not retry on non-network errors (e.g. ValueError)."""
|
||||
dest = tmp_path / "file.bin"
|
||||
call_count = [0]
|
||||
|
||||
def _fail_with_value_error(url: str, timeout: int = 60):
|
||||
call_count[0] += 1
|
||||
raise ValueError("not a network error")
|
||||
|
||||
with patch("urllib.request.urlopen", side_effect=_fail_with_value_error):
|
||||
with pytest.raises(ValueError):
|
||||
install_tools._download("https://example.com/file", dest, _sleep=lambda _: None)
|
||||
assert call_count[0] == 1
|
||||
|
||||
|
||||
class TestDownloadBinary:
|
||||
def test_download(self, tmp_path: Path) -> None:
|
||||
|
||||
Reference in New Issue
Block a user