Spaces:
Sleeping
Sleeping
| """ | |
| Visualization endpoint testleri. | |
| GET /api/v1/inspect/{id}/visualization/{annotated|parts|damages} | |
| Beklenen davranis: | |
| - completed inspection icin result.visualization_urls var ise -> 302 redirect | |
| - yoksa storage.get_presigned_url fallback'inden URL uretir veya 404 | |
| Bu test'te: | |
| 1. Sync inspection olusturup, sonra result icine visualization_urls enjekte ederiz | |
| 2. GET endpoint'i 302 doner ve Location header'i set eder | |
| 3. Olmayan tipler icin 404 doner (mocked get_presigned_url ile) | |
| """ | |
| from __future__ import annotations | |
| import httpx | |
| import pytest | |
| def _bearer(token: str) -> dict[str, str]: | |
| return {"Authorization": f"Bearer {token}"} | |
| async def _setup_completed_with_viz( | |
| async_client: httpx.AsyncClient, | |
| png_bytes: bytes, | |
| viz_url_map: dict | None = None, | |
| ) -> tuple[str, str]: | |
| """Sync inspection olustur ve result.visualization_urls'i guncelle. | |
| Returns: (inspection_id, access_token) | |
| """ | |
| reg = await async_client.post( | |
| "/auth/register", | |
| json={"email": "viz@test.example.com", "password": "strong-pass-1234"}, | |
| ) | |
| token = reg.json()["access_token"] | |
| files = {"file": ("a.png", png_bytes, "image/png")} | |
| r = await async_client.post( | |
| "/api/v1/inspect/sync", | |
| files=files, | |
| headers=_bearer(token), | |
| ) | |
| assert r.status_code == 200 | |
| inspection_id = r.json()["inspection_id"] | |
| if viz_url_map is not None: | |
| # Mevcut result'i al, visualization_urls ekle | |
| from main import get_db, update_inspection | |
| existing = get_db().get(inspection_id) | |
| result = dict(existing.get("result") or {}) | |
| result["visualization_urls"] = viz_url_map | |
| update_inspection(inspection_id, result=result) | |
| return inspection_id, token | |
| async def test_visualization_redirects_302_when_url_exists( | |
| async_client: httpx.AsyncClient, png_bytes: bytes, | |
| reset_user_store, reset_inspection_store | |
| ): | |
| viz_map = { | |
| "annotated": "https://cdn.test.local/inspections/x/annotated.png?sig=abc", | |
| "parts": "https://cdn.test.local/inspections/x/parts.png?sig=def", | |
| "damages": "https://cdn.test.local/inspections/x/damages.png?sig=ghi", | |
| } | |
| inspection_id, token = await _setup_completed_with_viz( | |
| async_client, png_bytes, viz_url_map=viz_map | |
| ) | |
| for viz_type, expected_url in viz_map.items(): | |
| r = await async_client.get( | |
| f"/api/v1/inspect/{inspection_id}/visualization/{viz_type}", | |
| headers=_bearer(token), | |
| follow_redirects=False, | |
| ) | |
| assert r.status_code == 302, f"{viz_type}: {r.status_code} {r.text}" | |
| assert r.headers.get("location") == expected_url | |
| async def test_visualization_falls_back_to_presigned_when_no_urls( | |
| async_client: httpx.AsyncClient, png_bytes: bytes, monkeypatch, | |
| reset_user_store, reset_inspection_store | |
| ): | |
| """visualization_urls yoksa, storage.get_presigned_url cagrilir.""" | |
| fake_signed = "https://cdn.test.local/fallback/annotated.png?token=xyz" | |
| def fake_get_presigned_url(key, expires_in=None): | |
| return fake_signed | |
| # main.py'deki import noktasini yamala | |
| monkeypatch.setattr("main.get_presigned_url", fake_get_presigned_url) | |
| inspection_id, token = await _setup_completed_with_viz( | |
| async_client, png_bytes, viz_url_map=None | |
| ) | |
| r = await async_client.get( | |
| f"/api/v1/inspect/{inspection_id}/visualization/annotated", | |
| headers=_bearer(token), | |
| follow_redirects=False, | |
| ) | |
| assert r.status_code == 302 | |
| assert r.headers.get("location") == fake_signed | |
| async def test_visualization_returns_404_when_no_url_and_presign_fails( | |
| async_client: httpx.AsyncClient, png_bytes: bytes, monkeypatch, | |
| reset_user_store, reset_inspection_store | |
| ): | |
| def fake_get_presigned_url(key, expires_in=None): | |
| raise RuntimeError("S3 unreachable") | |
| monkeypatch.setattr("main.get_presigned_url", fake_get_presigned_url) | |
| inspection_id, token = await _setup_completed_with_viz( | |
| async_client, png_bytes, viz_url_map=None | |
| ) | |
| r = await async_client.get( | |
| f"/api/v1/inspect/{inspection_id}/visualization/annotated", | |
| headers=_bearer(token), | |
| follow_redirects=False, | |
| ) | |
| assert r.status_code == 404 | |
| async def test_visualization_invalid_type_returns_422( | |
| async_client: httpx.AsyncClient, png_bytes: bytes, | |
| reset_user_store, reset_inspection_store | |
| ): | |
| inspection_id, token = await _setup_completed_with_viz( | |
| async_client, png_bytes, viz_url_map=None | |
| ) | |
| r = await async_client.get( | |
| f"/api/v1/inspect/{inspection_id}/visualization/invalid_type_xyz", | |
| headers=_bearer(token), | |
| ) | |
| # Literal pattern check -> FastAPI 422 | |
| assert r.status_code == 422 | |
| async def test_visualization_404_when_inspection_missing( | |
| async_client: httpx.AsyncClient, | |
| reset_user_store, reset_inspection_store | |
| ): | |
| reg = await async_client.post( | |
| "/auth/register", | |
| json={"email": "noinspec@test.example.com", "password": "strong-pass-1234"}, | |
| ) | |
| token = reg.json()["access_token"] | |
| r = await async_client.get( | |
| "/api/v1/inspect/00000000-0000-0000-0000-000000000000/visualization/annotated", | |
| headers=_bearer(token), | |
| follow_redirects=False, | |
| ) | |
| assert r.status_code == 404 | |