VirtForemanBackend / tests /test_api_smoke.py
Dirbol's picture
chore: pre-release — full project lifecycle (CRUD, history, docs, tests)
1d5d841
Raw
History Blame Contribute Delete
3.79 kB
"""Smoke tests for the FastAPI surface area.
Run:
pytest tests/ -v
External services stays behind affordances:
* `pytest -m "not supabase and not nvidia"` runs offline smoke only.
"""
from __future__ import annotations
import io
import pytest
pytestmark = pytest.mark.asyncio
# ---- /health & / ----
def test_health(fastapi_client):
r = fastapi_client.get("/health")
assert r.status_code == 200, r.text
body = r.json()
assert body["status"] == "ok"
assert body["service"] == "virtual-foreman-backend"
def test_root_lists_endpoints(fastapi_client):
r = fastapi_client.get("/")
assert r.status_code == 200
body = r.json()
assert "POST /projects" in body["endpoints"]
assert "POST /work/infer" in body["endpoints"]
assert "POST /work/blueprint" in body["endpoints"]
assert "POST /documents/render" in body["endpoints"]
assert "GET /inference/history" in body["endpoints"]
def test_request_id_header_echoed(fastapi_client):
r = fastapi_client.get("/health", headers={"X-Request-ID": "rid-abc-123"})
assert r.headers.get("X-Request-ID") == "rid-abc-123"
def test_request_id_autogenerated_when_missing(fastapi_client):
r = fastapi_client.get("/health")
rid = r.headers.get("X-Request-ID")
assert rid is not None and len(rid) > 0
# ---- /documents/templates ----
def test_documents_templates(fastapi_client):
r = fastapi_client.get("/documents/templates")
assert r.status_code == 200
body = r.json()
assert "supported" in body
assert isinstance(body["supported"], list)
assert "act_hidden_works" in body["supported"]
assert "general_work_log" in body["supported"]
# ---- /work/infer ----
@pytest.mark.asyncio
async def test_work_infer_no_payload_returns_422(fastapi_client):
r = fastapi_client.post("/work/infer", json={"wrong": "shape"})
assert r.status_code == 422
@pytest.mark.asyncio
async def test_work_infer_short_text_returns_422(fastapi_client):
r = fastapi_client.post(
"/work/infer",
json={
"raw_text": "ab", # too short
"date_from": "2026-06-22",
"date_to": "2026-06-22",
},
)
assert r.status_code == 422
# ---- /work/blueprint (offline-friendly path) ----
@pytest.mark.asyncio
async def test_blueprint_reject_invalid_mime(fastapi_client):
fake = (b"not really a video", "test.gif", "image/gif")
r = fastapi_client.post(
"/work/blueprint",
files={"file": (fake[1], io.BytesIO(fake[0]), fake[2])},
)
assert r.status_code == 415
assert "image/png" in r.json()["detail"]
@pytest.mark.asyncio
async def test_blueprint_reject_empty(fastapi_client):
r = fastapi_client.post(
"/work/blueprint",
files={"file": ("empty.png", io.BytesIO(b""), "image/png")},
)
# FastAPI's UploadFile may read empty bytes — endpoint catches and
# returns 400 "empty upload".
assert r.status_code in (400, 422)
detail = (r.json().get("detail") or "").lower()
assert "empty" in detail or "required" in detail
# ---- /projects validation ----
@pytest.mark.asyncio
async def test_projects_create_missing_fields_returns_422(fastapi_client):
r = fastapi_client.post("/projects", json={"contract_no": "12-2026/BN"})
# contract_no isn't enough — many required fields missing.
assert r.status_code == 422
@pytest.mark.asyncio
async def test_projects_get_unknown_id_returns_404_or_503(fastapi_client):
"""Off the offline path this either 404s (real Supabase), 503 (Supabase
unreachable), 500 (broken client) — never 200 because the row isn't
real.
"""
r = fastapi_client.get(
"/projects/00000000-0000-0000-0000-000000000000"
)
assert r.status_code in (404, 503)