feat: download media in bounded domain batches
This commit is contained in:
+50
-1
@@ -1,7 +1,8 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from rf4_research.media_cli import media_queue_plan
|
||||
from rf4_research import media_cli
|
||||
from rf4_research.media_cli import _download_batch, media_queue_plan
|
||||
|
||||
|
||||
def test_queue_plan_prioritizes_entity_gaps_and_respects_domain_cooldown(tmp_path: Path) -> None:
|
||||
@@ -26,6 +27,7 @@ def test_queue_plan_prioritizes_entity_gaps_and_respects_domain_cooldown(tmp_pat
|
||||
plan = media_queue_plan(root, state, now=2000)
|
||||
|
||||
assert plan["network_requests"] == 0
|
||||
assert plan["batch_limit_per_domain"] == 40
|
||||
assert plan["queued_total"] == 4
|
||||
assert plan["domains"]["rf4map.ru"]["ready"] is False
|
||||
assert plan["domains"]["rf4map.ru"]["retry_in_seconds"] == 800
|
||||
@@ -33,3 +35,50 @@ def test_queue_plan_prioritizes_entity_gaps_and_respects_domain_cooldown(tmp_pat
|
||||
assert plan["domains"]["rf4map.ru"]["next_asset"]["entity_type"] == "waterbody"
|
||||
assert plan["domains"]["rf4-posts.com"]["ready"] is True
|
||||
assert plan["exact_catalog_gaps_known"] is False
|
||||
|
||||
|
||||
def test_batch_reserves_once_per_domain_and_caps_each_window(tmp_path: Path, monkeypatch) -> None:
|
||||
root = tmp_path / "media"
|
||||
root.mkdir()
|
||||
assets = [
|
||||
{"status": "queued", "asset_url": f"https://rf4map.ru/fish-{index}.png", "source_page": "https://rf4map.ru/fishes", "entity_type": "fish", "label": f"Рыба {index}"}
|
||||
for index in range(45)
|
||||
] + [
|
||||
{"status": "queued", "asset_url": f"https://rf4-posts.com/fish-{index}.png", "source_page": "https://rf4-posts.com/fishes", "entity_type": "fish", "label": f"Рыба {index}"}
|
||||
for index in range(2)
|
||||
]
|
||||
(root / "manifest.json").write_text(json.dumps({"version": 1, "assets": assets}), encoding="utf-8")
|
||||
state = tmp_path / "state.json"
|
||||
|
||||
monkeypatch.setattr(media_cli, "_attempt_asset", lambda *_: ("stored", None))
|
||||
report = _download_batch(root, state, limit=40)
|
||||
|
||||
assert report["attempted_total"] == 42
|
||||
assert report["domains"]["rf4map.ru"]["attempted"] == 40
|
||||
assert report["domains"]["rf4-posts.com"]["attempted"] == 2
|
||||
assert set(json.loads(state.read_text())) == {"rf4map.ru", "rf4-posts.com"}
|
||||
|
||||
cooling = _download_batch(root, state, limit=40)
|
||||
assert cooling["attempted_total"] == 0
|
||||
assert all("cooldown is active" in item["skipped"] for item in cooling["domains"].values())
|
||||
|
||||
|
||||
def test_batch_stops_domain_after_three_consecutive_invalid_assets(tmp_path: Path, monkeypatch) -> None:
|
||||
root = tmp_path / "media"
|
||||
root.mkdir()
|
||||
assets = [
|
||||
{"status": "queued", "asset_url": f"https://rf4map.ru/bad-{index}.png", "source_page": "https://rf4map.ru/fishes", "entity_type": "fish", "label": f"Рыба {index}"}
|
||||
for index in range(10)
|
||||
]
|
||||
(root / "manifest.json").write_text(json.dumps({"version": 1, "assets": assets}), encoding="utf-8")
|
||||
|
||||
def invalid(_root, _path, _manifest, item):
|
||||
item["status"] = "invalid"
|
||||
return "invalid", ValueError("not an image")
|
||||
|
||||
monkeypatch.setattr(media_cli, "_attempt_asset", invalid)
|
||||
report = _download_batch(root, tmp_path / "state.json", limit=10)
|
||||
|
||||
assert report["attempted_total"] == 3
|
||||
assert report["failed_total"] == 3
|
||||
assert "3 consecutive failures" in report["domains"]["rf4map.ru"]["stopped_reason"]
|
||||
|
||||
Reference in New Issue
Block a user