Skip to content

Commit 24d07cf

Browse files
test(automation): add unit tests for critical automation modules (#3162)
## Summary - Add 104 unit tests for previously untested automation modules - Increase test coverage for critical automation scripts from 0% to meaningful levels ### Coverage Improvements | Module | Before | After | |--------|--------|-------| | `sync_to_postgres.py` | 0% | **60%** | | `backfill_review_metadata.py` | 0% | **50%** | | `migrate_metadata_format.py` | 0% | **87%** | | `plot_generator.py` | 0% | **33%** | ### New Test Files - `tests/unit/automation/scripts/test_sync_to_postgres.py` (39 tests) - `tests/unit/automation/generators/test_plot_generator.py` (22 tests) - `tests/unit/automation/scripts/test_backfill_review_metadata.py` (22 tests) - `tests/unit/automation/scripts/test_migrate_metadata_format.py` (21 tests) ## Test plan - [x] All 104 new tests pass locally - [x] Ruff lint check passes - [x] Ruff format check passes - [ ] CI pipeline passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 2881afa commit 24d07cf

File tree

4 files changed

+971
-0
lines changed

4 files changed

+971
-0
lines changed

tests/unit/api/test_routers.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,49 @@ def test_libraries_with_db(self, db_client, mock_lib) -> None:
201201
assert len(data["libraries"]) == 1
202202
assert data["libraries"][0]["id"] == "matplotlib"
203203

204+
def test_libraries_cache_hit(self, db_client) -> None:
205+
"""Libraries should return cached data when available."""
206+
client, _ = db_client
207+
208+
cached_data = {"libraries": [{"id": "cached_lib", "name": "Cached"}]}
209+
210+
with patch("api.routers.libraries.get_cache", return_value=cached_data):
211+
response = client.get("/libraries")
212+
assert response.status_code == 200
213+
data = response.json()
214+
assert data["libraries"][0]["id"] == "cached_lib"
215+
216+
def test_library_images_with_db(self, db_client, mock_spec) -> None:
217+
"""Library images should return images from DB."""
218+
client, _ = db_client
219+
220+
mock_spec_repo = MagicMock()
221+
mock_spec_repo.get_all = AsyncMock(return_value=[mock_spec])
222+
223+
with (
224+
patch("api.routers.libraries.get_cache", return_value=None),
225+
patch("api.routers.libraries.set_cache"),
226+
patch("api.routers.libraries.SpecRepository", return_value=mock_spec_repo),
227+
):
228+
response = client.get("/libraries/matplotlib/images")
229+
assert response.status_code == 200
230+
data = response.json()
231+
assert data["library"] == "matplotlib"
232+
assert len(data["images"]) == 1
233+
assert data["images"][0]["spec_id"] == "scatter-basic"
234+
235+
def test_library_images_cache_hit(self, db_client) -> None:
236+
"""Library images should return cached data when available."""
237+
client, _ = db_client
238+
239+
cached_data = {"library": "matplotlib", "images": [{"spec_id": "cached"}]}
240+
241+
with patch("api.routers.libraries.get_cache", return_value=cached_data):
242+
response = client.get("/libraries/matplotlib/images")
243+
assert response.status_code == 200
244+
data = response.json()
245+
assert data["images"][0]["spec_id"] == "cached"
246+
204247

205248
class TestSpecsRouter:
206249
"""Tests for specs router."""
@@ -305,6 +348,53 @@ def test_download_impl_not_found(self, client: TestClient, mock_spec) -> None:
305348
response = client.get("/download/scatter-basic/seaborn")
306349
assert response.status_code == 404
307350

351+
def test_download_success(self, client: TestClient, mock_spec) -> None:
352+
"""Download should return image when spec and impl found."""
353+
354+
mock_spec_repo = MagicMock()
355+
mock_spec_repo.get_by_id = AsyncMock(return_value=mock_spec)
356+
357+
# Mock httpx response
358+
mock_response = MagicMock()
359+
mock_response.content = b"fake image content"
360+
mock_response.raise_for_status = MagicMock()
361+
362+
mock_httpx_client = AsyncMock()
363+
mock_httpx_client.get = AsyncMock(return_value=mock_response)
364+
mock_httpx_client.__aenter__ = AsyncMock(return_value=mock_httpx_client)
365+
mock_httpx_client.__aexit__ = AsyncMock(return_value=None)
366+
367+
with (
368+
patch(DB_CONFIG_PATCH, return_value=True),
369+
patch("api.routers.download.SpecRepository", return_value=mock_spec_repo),
370+
patch("api.routers.download.httpx.AsyncClient", return_value=mock_httpx_client),
371+
):
372+
response = client.get("/download/scatter-basic/matplotlib")
373+
assert response.status_code == 200
374+
assert response.headers["content-type"] == "image/png"
375+
assert "attachment" in response.headers["content-disposition"]
376+
assert response.content == b"fake image content"
377+
378+
def test_download_gcs_error(self, client: TestClient, mock_spec) -> None:
379+
"""Download should return 502 when GCS fetch fails."""
380+
import httpx
381+
382+
mock_spec_repo = MagicMock()
383+
mock_spec_repo.get_by_id = AsyncMock(return_value=mock_spec)
384+
385+
mock_httpx_client = AsyncMock()
386+
mock_httpx_client.get = AsyncMock(side_effect=httpx.HTTPError("GCS error"))
387+
mock_httpx_client.__aenter__ = AsyncMock(return_value=mock_httpx_client)
388+
mock_httpx_client.__aexit__ = AsyncMock(return_value=None)
389+
390+
with (
391+
patch(DB_CONFIG_PATCH, return_value=True),
392+
patch("api.routers.download.SpecRepository", return_value=mock_spec_repo),
393+
patch("api.routers.download.httpx.AsyncClient", return_value=mock_httpx_client),
394+
):
395+
response = client.get("/download/scatter-basic/matplotlib")
396+
assert response.status_code == 502
397+
308398

309399
class TestSeoRouter:
310400
"""Tests for SEO router."""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for automation.generators module."""

0 commit comments

Comments
 (0)