fix(jlcpcb): skip re-download when cache file already complete (#199, #204)

bhoot found that download_cdfer 416-loops when a complete cdfer.sqlite3 already
exists in the cache (e.g. a prior run downloaded it but died before
conversion/cleanup): resuming a complete file sends Range: bytes=<size>- which
the server answers with HTTP 416, and raise_for_status treated that as a
retryable error, spinning until max_retries then failing.

Fix: HEAD once for Content-Length up front and short-circuit when the local file
already matches the full size; and in the loop, treat a 416 as "complete" when
the size matches (else drop the stale partial and restart fresh). Remove the now
-unused _head_last_modified helper (HEAD is done inline). Add a no-network test.

Verified live: happy path still downloads+converts 616k parts (~50s); a
pre-existing complete file now short-circuits in ~0.4s instead of looping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
mixelpixx
2026-05-30 09:29:22 -04:00
parent 030d008843
commit a18e729076
2 changed files with 67 additions and 13 deletions

View File

@@ -266,3 +266,39 @@ def test_result_flags_stale_catalog_and_leaves_fresh_alone(tmp_path, monkeypatch
assert fresh.get("stale") is not True
assert "warning" not in fresh
assert fresh["catalog_age_days"] <= 1
def test_download_cdfer_skips_when_file_already_complete(tmp_path, monkeypatch):
"""A complete cache file must short-circuit, not re-download (no HTTP 416 loop).
Regression for the force/resume bug: resuming a complete file sends
Range: bytes=<size>- which the server answers with 416; the old code retried
that until failure. With a HEAD size check we return immediately and never
call requests.get.
"""
import requests
cache_dir = tmp_path / "cache"
cache_dir.mkdir()
dest = cache_dir / "cdfer.sqlite3"
payload = b"x" * 4096
dest.write_bytes(payload)
class _Head:
ok = True
headers = {
"Content-Length": str(len(payload)),
"Last-Modified": "Wed, 01 Apr 2026 00:00:00 GMT",
}
monkeypatch.setattr(requests, "head", lambda *a, **k: _Head())
def _no_get(*a, **k):
raise AssertionError("requests.get must not run when the file is already complete")
monkeypatch.setattr(requests, "get", _no_get)
path, last_mod = jlcpcb_downloader.download_cdfer(cache_dir)
assert path == dest
assert path.stat().st_size == len(payload) # untouched
assert last_mod.startswith("Wed, 01 Apr 2026")