sathvik77 commited on
Commit
5d9f00f
·
1 Parent(s): dc5236d

fix(backend): use asyncio.to_thread for stats IO, guard malformed JSON, isolate test side-effects

Browse files
Files changed (2) hide show
  1. main.py +14 -6
  2. tests/test_main.py +1 -0
main.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import json
2
  import threading
3
  import uuid
@@ -21,8 +22,11 @@ _stats_lock = threading.Lock()
21
  def _read_stats() -> dict:
22
  if not STATS_FILE.exists():
23
  return {"total_visits": 0, "total_downloads": 0}
24
- with open(STATS_FILE, "r") as f:
25
- return json.load(f)
 
 
 
26
 
27
 
28
  def _write_stats(stats: dict) -> None:
@@ -70,15 +74,19 @@ async def health():
70
  return {"status": "ok"}
71
 
72
 
73
- @app.get("/stats")
74
- async def get_stats():
75
  with _stats_lock:
76
  return _read_stats()
77
 
78
 
 
 
 
 
 
79
  @app.post("/stats/visit", status_code=204)
80
  async def record_visit():
81
- _increment_stat("total_visits")
82
 
83
 
84
  @app.get("/status/{job_id}", response_model=JobStatus)
@@ -135,7 +143,7 @@ async def download(job_id: str):
135
  if not output_file.exists():
136
  raise HTTPException(status_code=404, detail="Output file not found")
137
 
138
- _increment_stat("total_downloads")
139
 
140
  return FileResponse(
141
  path=output_file,
 
1
+ import asyncio
2
  import json
3
  import threading
4
  import uuid
 
22
  def _read_stats() -> dict:
23
  if not STATS_FILE.exists():
24
  return {"total_visits": 0, "total_downloads": 0}
25
+ try:
26
+ with open(STATS_FILE, "r") as f:
27
+ return json.load(f)
28
+ except (json.JSONDecodeError, OSError):
29
+ return {"total_visits": 0, "total_downloads": 0}
30
 
31
 
32
  def _write_stats(stats: dict) -> None:
 
74
  return {"status": "ok"}
75
 
76
 
77
+ def _read_stats_locked() -> dict:
 
78
  with _stats_lock:
79
  return _read_stats()
80
 
81
 
82
+ @app.get("/stats")
83
+ async def get_stats():
84
+ return await asyncio.to_thread(_read_stats_locked)
85
+
86
+
87
  @app.post("/stats/visit", status_code=204)
88
  async def record_visit():
89
+ await asyncio.to_thread(_increment_stat, "total_visits")
90
 
91
 
92
  @app.get("/status/{job_id}", response_model=JobStatus)
 
143
  if not output_file.exists():
144
  raise HTTPException(status_code=404, detail="Output file not found")
145
 
146
+ await asyncio.to_thread(_increment_stat, "total_downloads")
147
 
148
  return FileResponse(
149
  path=output_file,
tests/test_main.py CHANGED
@@ -97,6 +97,7 @@ async def test_download_done_job(client, tmp_path, monkeypatch):
97
  fake_status = JobStatus(job_id=job_id, state=JobState.done, progress=100)
98
  monkeypatch.setitem(processor._jobs, job_id, fake_status)
99
  monkeypatch.setattr(main, "OUTPUT_DIR", tmp_path)
 
100
 
101
  response = await client.get(f"/download/{job_id}")
102
  assert response.status_code == 200
 
97
  fake_status = JobStatus(job_id=job_id, state=JobState.done, progress=100)
98
  monkeypatch.setitem(processor._jobs, job_id, fake_status)
99
  monkeypatch.setattr(main, "OUTPUT_DIR", tmp_path)
100
+ monkeypatch.setattr(main, "STATS_FILE", tmp_path / "stats.json")
101
 
102
  response = await client.get(f"/download/{job_id}")
103
  assert response.status_code == 200