| """ |
| End-to-end test script for NutriLoop AI API. |
| Tests /health, /predict, /cold-start, and news adjuster. |
| """ |
| import httpx |
| import sys |
| from pathlib import Path |
|
|
| |
| project_root = Path(__file__).resolve().parent.parent |
| if str(project_root) not in sys.path: |
| sys.path.insert(0, str(project_root)) |
|
|
|
|
| BASE_URL = "http://localhost:7860" |
|
|
|
|
| def test_health(): |
| """Test GET /health endpoint.""" |
| print("\n[TEST] GET /health") |
| response = httpx.get(f"{BASE_URL}/health", timeout=10.0) |
| data = response.json() |
| print(f" Status: {response.status_code}") |
| print(f" Response: {data}") |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}" |
| assert data["status"] == "ok", f"Expected status='ok', got {data['status']}" |
| print(" PASS") |
|
|
|
|
| def test_predict_global(): |
| """Test POST /predict with known restaurant_id and item.""" |
| print("\n[TEST] POST /predict") |
| |
| response = httpx.post( |
| f"{BASE_URL}/predict", |
| json={ |
| "restaurant_id": "test_restaurant_001", |
| "item_name": "biriyani", |
| "city": "Kochi", |
| "days": 7, |
| }, |
| timeout=10.0, |
| ) |
| data = response.json() |
| print(f" Status: {response.status_code}") |
| print(f" Response: {data}") |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}" |
| assert "predictions" in data, "Missing 'predictions' in response" |
| assert len(data["predictions"]) == 7, f"Expected 7 predictions, got {len(data['predictions'])}" |
| assert data["source"] in ["global_model", "cold_start"], f"Invalid source: {data['source']}" |
| print(" PASS") |
|
|
|
|
| def test_cold_start(): |
| """Test POST /cold-start endpoint with Kochi coordinates.""" |
| print("\n[TEST] POST /cold-start") |
| response = httpx.post( |
| f"{BASE_URL}/cold-start", |
| json={ |
| "latitude": 9.9312, |
| "longitude": 76.2673, |
| "cuisine_type": "indian", |
| "avg_daily_quantity": 25.0, |
| "item_name": "puttu", |
| "days": 7, |
| }, |
| timeout=10.0, |
| ) |
| data = response.json() |
| print(f" Status: {response.status_code}") |
| print(f" Response: {data}") |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}" |
| assert data["source"] == "cold_start", f"Expected source='cold_start', got {data['source']}" |
| assert len(data["predictions"]) == 7, f"Expected 7 predictions, got {len(data['predictions'])}" |
| assert data["news_multiplier"] > 0, "Invalid news_multiplier" |
| print(" PASS") |
|
|
|
|
| def test_news_adjuster(): |
| """Test news adjuster directly.""" |
| print("\n[TEST] News adjuster (Kochi)") |
| from app.news_adjuster import get_news_multiplier |
| multiplier = get_news_multiplier("Kochi") |
| print(f" Multiplier: {multiplier}") |
| assert 0.5 <= multiplier <= 2.0, f"Multiplier out of range: {multiplier}" |
| print(" PASS") |
|
|
|
|
| def main(): |
| """Run all tests.""" |
| print("=" * 50) |
| print("NutriLoop AI - End-to-End API Tests") |
| print("=" * 50) |
|
|
| |
| try: |
| httpx.get(f"{BASE_URL}/health", timeout=5.0) |
| except httpx.ConnectError: |
| print(f"\nERROR: Cannot connect to {BASE_URL}") |
| print("Make sure the server is running:") |
| print(" uv run uvicorn app.main:app --reload --port 7860") |
| sys.exit(1) |
|
|
| passed = 0 |
| failed = 0 |
|
|
| tests = [ |
| ("Health", test_health), |
| ("Predict", test_predict_global), |
| ("Cold-Start", test_cold_start), |
| ("News Adjuster", test_news_adjuster), |
| ] |
|
|
| for name, test_fn in tests: |
| try: |
| test_fn() |
| passed += 1 |
| except Exception as e: |
| print(f" FAIL: {e}") |
| failed += 1 |
|
|
| print("\n" + "=" * 50) |
| print(f"Results: {passed} passed, {failed} failed") |
| print("=" * 50) |
|
|
| if failed > 0: |
| sys.exit(1) |
|
|
|
|
| if __name__ == "__main__": |
| main() |