fix: reserve and persist background jobs
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import threading
|
||||
|
||||
from dashboard.jobs import JobRegistry
|
||||
|
||||
|
||||
def test_job_registry_atomically_reserves_only_one_job_per_kind(tmp_path):
|
||||
registry = JobRegistry(tmp_path / "jobs.json")
|
||||
barrier = threading.Barrier(10)
|
||||
results = []
|
||||
|
||||
def reserve():
|
||||
barrier.wait()
|
||||
results.append(registry.reserve("refresh", details={"full": False}))
|
||||
|
||||
threads = [threading.Thread(target=reserve) for _ in range(10)]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
reserved = [job for job in results if job is not None]
|
||||
assert len(reserved) == 1
|
||||
assert reserved[0]["id"]
|
||||
assert reserved[0]["status"] == "queued"
|
||||
assert registry.active("refresh")["id"] == reserved[0]["id"]
|
||||
|
||||
|
||||
def test_job_registry_tracks_completion_and_result(tmp_path):
|
||||
registry = JobRegistry(tmp_path / "jobs.json")
|
||||
job = registry.reserve("history")
|
||||
|
||||
result = registry.run(job["id"], lambda: {"records": 42})
|
||||
|
||||
assert result == {"records": 42}
|
||||
saved = registry.get(job["id"])
|
||||
assert saved["status"] == "complete"
|
||||
assert saved["result"] == {"records": 42}
|
||||
assert saved["started_at"]
|
||||
assert saved["finished_at"]
|
||||
assert registry.active("history") is None
|
||||
|
||||
|
||||
def test_job_registry_marks_abandoned_active_jobs_interrupted_on_restart(tmp_path):
|
||||
path = tmp_path / "jobs.json"
|
||||
first = JobRegistry(path)
|
||||
job = first.reserve("refresh")
|
||||
|
||||
restarted = JobRegistry(path)
|
||||
|
||||
recovered = restarted.get(job["id"])
|
||||
assert recovered["status"] == "interrupted"
|
||||
assert recovered["finished_at"]
|
||||
assert restarted.active("refresh") is None
|
||||
@@ -117,3 +117,34 @@ def test_expired_onchain_timestamp_triggers_real_refresh(server, monkeypatch, tm
|
||||
assert saved["puell_multiple"]["source"] == "lookintobitcoin"
|
||||
assert saved["sopr"]["source"] == "checkonchain"
|
||||
assert saved["_onchain_timestamp"] != old.isoformat()
|
||||
|
||||
|
||||
def test_refresh_job_is_reserved_before_thread_start(server, monkeypatch, tmp_path):
|
||||
from dashboard.jobs import JobRegistry
|
||||
|
||||
registry = JobRegistry(tmp_path / "jobs.json")
|
||||
monkeypatch.setattr(server, "_jobs", registry, raising=False)
|
||||
monkeypatch.setattr(server, "_scraper_running", False)
|
||||
|
||||
started = server.api_refresh(full=False)
|
||||
duplicate = server.api_refresh(full=False)
|
||||
|
||||
assert started["job_id"]
|
||||
assert started["status"] == "queued"
|
||||
assert registry.get(started["job_id"])["status"] == "queued"
|
||||
assert duplicate.status_code == 409
|
||||
|
||||
|
||||
def test_history_collection_has_job_id_and_job_scoped_progress(server, monkeypatch, tmp_path):
|
||||
from dashboard.jobs import JobRegistry
|
||||
|
||||
registry = JobRegistry(tmp_path / "jobs.json")
|
||||
monkeypatch.setattr(server, "_jobs", registry, raising=False)
|
||||
|
||||
started = server.api_backtest_collect()
|
||||
job = registry.get(started["job_id"])
|
||||
|
||||
assert started["status"] == "queued"
|
||||
assert job["kind"] == "history"
|
||||
assert job["progress"] == {"status": "starting", "current": "", "step": 0, "total": 0}
|
||||
assert server.api_job_status(started["job_id"])["id"] == started["job_id"]
|
||||
|
||||
Reference in New Issue
Block a user