Download eval/test_api.py from RV302001/restockiq: direct link, hf CLI and curl.
- Browser
- Download file 4.87 kB
-
https://huggingface.co/spaces/RV302001/restockiq/resolve/main/eval/test_api.py
- Command line
-
hf download hf://spaces/RV302001/restockiq/eval/test_api.py
-
curl -L -o test_api.py https://huggingface.co/spaces/RV302001/restockiq/resolve/main/eval/test_api.py
4.87 kB
| """API contract tests — run against the real precomputed artifacts. | |
| Covers every route: response shape, parameter validation, 404s, cross-endpoint | |
| consistency (the recommendation endpoint must agree with the raw forecast it is | |
| derived from), and action-list invariants. | |
| """ | |
| import sys | |
| from pathlib import Path | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| from app.main import app | |
| KNOWN = ("CA_1", "FOODS_3_090") | |
| def client(): | |
| # context manager triggers the startup event that loads the parquet artifacts | |
| with TestClient(app) as c: | |
| yield c | |
| class TestHealth: | |
| def test_health(self, client): | |
| r = client.get("/health") | |
| assert r.status_code == 200 | |
| assert r.json() == {"status": "ok"} | |
| class TestSkus: | |
| def test_shape(self, client): | |
| r = client.get("/api/skus").json() | |
| assert len(r["stores"]) == 10 | |
| assert len(r["items"]) == 3049 | |
| assert KNOWN[0] in r["stores"] and KNOWN[1] in r["items"] | |
| class TestForecast: | |
| def test_arrays_aligned(self, client): | |
| r = client.get(f"/api/forecast/{KNOWN[0]}/{KNOWN[1]}") | |
| assert r.status_code == 200 | |
| b = r.json() | |
| n = len(b["dates"]) | |
| assert n == 28 | |
| assert len(b["p10"]) == len(b["p50"]) == len(b["p90"]) == len(b["actual"]) == n | |
| assert len(b["history_dates"]) == len(b["history_actual"]) == 56 | |
| def test_quantiles_ordered(self, client): | |
| b = client.get(f"/api/forecast/{KNOWN[0]}/{KNOWN[1]}").json() | |
| for lo, mid, hi in zip(b["p10"], b["p50"], b["p90"]): | |
| assert lo <= mid <= hi | |
| def test_unknown_series_404(self, client): | |
| assert client.get("/api/forecast/XX_9/NOPE").status_code == 404 | |
| class TestRecommendation: | |
| def test_defaults(self, client): | |
| b = client.get(f"/api/recommendation/{KNOWN[0]}/{KNOWN[1]}").json() | |
| assert b["service_level"] == 0.95 | |
| assert b["lead_time_days"] == 7 | |
| assert b["suggested_order_qty"] >= 0 | |
| def test_inventory_reduces_order(self, client): | |
| base = f"/api/recommendation/{KNOWN[0]}/{KNOWN[1]}" | |
| q0 = client.get(base, params={"current_inventory": 0}).json() | |
| q100 = client.get(base, params={"current_inventory": 100}).json() | |
| assert q100["suggested_order_qty"] <= q0["suggested_order_qty"] | |
| # reorder point is independent of what's on the shelf | |
| assert q100["reorder_point"] == q0["reorder_point"] | |
| def test_higher_service_level_bigger_cushion(self, client): | |
| base = f"/api/recommendation/{KNOWN[0]}/{KNOWN[1]}" | |
| lo = client.get(base, params={"service_level": 0.90}).json() | |
| hi = client.get(base, params={"service_level": 0.99}).json() | |
| assert hi["safety_stock"] > lo["safety_stock"] | |
| def test_agrees_with_forecast(self, client): | |
| """Recommendation must be derivable from the served forecast (no drift | |
| between endpoints).""" | |
| fc = client.get(f"/api/forecast/{KNOWN[0]}/{KNOWN[1]}").json() | |
| rec = client.get(f"/api/recommendation/{KNOWN[0]}/{KNOWN[1]}").json() | |
| expected_avg = sum(fc["p50"][:7]) / 7 | |
| assert rec["avg_daily_demand"] == pytest.approx(expected_avg, rel=1e-3) | |
| def test_validation_422(self, client, bad): | |
| r = client.get(f"/api/recommendation/{KNOWN[0]}/{KNOWN[1]}", params=bad) | |
| assert r.status_code == 422 | |
| class TestActionList: | |
| def test_counts_sum_to_total(self, client): | |
| b = client.get(f"/api/action_list/{KNOWN[0]}").json() | |
| assert b["total_items"] == 3049 | |
| assert sum(b["counts"].values()) == b["total_items"] | |
| def test_sorted_by_order_size(self, client): | |
| items = client.get(f"/api/action_list/{KNOWN[0]}?limit=50").json()["items"] | |
| qtys = [i["suggested_order_qty"] for i in items] | |
| assert qtys == sorted(qtys, reverse=True) | |
| def test_zero_inventory_means_everything_ordered(self, client): | |
| b = client.get(f"/api/action_list/{KNOWN[0]}?current_inventory=0").json() | |
| assert b["counts"].get("order_now", 0) + b["counts"].get("order_soon", 0) >= 3000 | |
| def test_cached_response_identical(self, client): | |
| url = f"/api/action_list/{KNOWN[0]}?current_inventory=42" | |
| assert client.get(url).json() == client.get(url).json() | |
| class TestBacktest: | |
| def test_summary_shape(self, client): | |
| b = client.get("/api/backtest_summary").json() | |
| for policy in ("naive", "recommended"): | |
| p = b["policies"][policy] | |
| assert 0 <= p["stockout_rate_pct"] <= 100 | |
| assert p["avg_holding_units"] >= 0 | |
| assert len(b["top_skus"]) == 20 | |