"""P2-M5 acceptance test - verify all backend API endpoints. Run while backend is running on http://127.0.0.1:8000. All source is ASCII. """ import json import urllib.request BASE = "http://127.0.0.1:8000" passed = 0 failed = 0 def api_get(path): with urllib.request.urlopen(f"{BASE}{path}", timeout=10) as r: return json.loads(r.read()) def test(name, condition, detail=""): global passed, failed if condition: passed += 1 print(f" [PASS] {name}") else: failed += 1 print(f" [FAIL] {name} {detail}") print("=" * 60) print("P2-M5 Acceptance Test - Backend API Verification") print("=" * 60) # 1. Health print("\n[1] Health & Core") r = api_get("/api/health") test("health status ok", r.get("status") == "ok") # 2. Projects print("\n[2] Projects API") r = api_get("/api/projects?limit=5") test("projects list returns total", "total" in r, str(r)[:100]) test("projects list has items", "items" in r) # 3. Plans print("\n[3] Plans API") r = api_get("/api/plans?limit=5") test("plans list returns total", "total" in r) test("plans list has items", "items" in r) # 4. Experience CRUD print("\n[4] Experience Library API") r = api_get("/api/experience?limit=5") test("experience list returns total", "total" in r) test("experience list has items", "items" in r) # Create a test case print(" -- Create test case --") test_data = { "topology": "SSSR", "source_plan_id": "ACCEPTANCE-TEST", "params": {"Airgap": 1.0, "RMSCurrent": 20}, "metrics": {"tavg_nm": 2.5, "efficiency_pct": 85.0, "ripple_pct": 3.0}, "conclusion": "Acceptance test case", "tags": ["acceptance-test"], "rating": 3 } req = urllib.request.Request( f"{BASE}/api/experience", data=json.dumps(test_data).encode(), headers={"Content-Type": "application/json"}, method="POST" ) with urllib.request.urlopen(req, timeout=10) as r: created = json.loads(r.read()) test("create experience returns 201", created.get("id") is not None) case_id = created.get("id") # Get single case r = api_get(f"/api/experience/{case_id}") test("get single case", r.get("id") == case_id) test("case has params", bool(r.get("params"))) test("case has metrics", bool(r.get("metrics"))) # Update case print(" -- Update test case --") update_data = {"conclusion": "Updated by acceptance test", "rating": 5, "tags": ["acceptance-test", "updated"]} req = urllib.request.Request( f"{BASE}/api/experience/{case_id}", data=json.dumps(update_data).encode(), headers={"Content-Type": "application/json"}, method="PUT" ) with urllib.request.urlopen(req, timeout=10) as r: updated = json.loads(r.read()) test("update case returns updated", updated.get("conclusion") == "Updated by acceptance test") test("update rating", updated.get("rating") == 5) # Delete test case print(" -- Delete test case --") req = urllib.request.Request( f"{BASE}/api/experience/{case_id}", method="DELETE" ) with urllib.request.urlopen(req, timeout=10) as r: test("delete case returns 204", r.status == 204) # 5. Analytics print("\n[5] Analytics API") r = api_get("/api/analytics/metrics") test("metrics list", len(r.get("metrics", [])) >= 10) r = api_get("/api/analytics/experience/stats") test("experience stats has total", "total" in r) test("experience stats has topology_distribution", "topology_distribution" in r) test("experience stats has metric_ranges", "metric_ranges" in r) # Similar search print(" -- Similar search --") req = urllib.request.Request( f"{BASE}/api/analytics/experience/similar?top_k=3&tolerance=0.5", data=json.dumps({"params": {"Airgap": 1.0, "RMSCurrent": 20}}).encode(), headers={"Content-Type": "application/json"}, method="POST" ) with urllib.request.urlopen(req, timeout=10) as r: similar = json.loads(r.read()) test("similar search returns items", "items" in similar) # 6. Scan parameters / Generation print("\n[6] Generation / Rule Engine API") r = api_get("/api/scan-parameters") test("scan parameters list", len(r.get("parameters", [])) >= 5, str(r)[:100]) # 7. API docs print("\n[7] API Documentation") try: with urllib.request.urlopen(f"{BASE}/docs", timeout=5) as r: test("Swagger docs accessible", r.status == 200) except Exception as e: test("Swagger docs accessible", False, str(e)) try: with urllib.request.urlopen(f"{BASE}/openapi.json", timeout=5) as r: spec = json.loads(r.read()) test("OpenAPI spec has paths", len(spec.get("paths", {})) >= 10) test("OpenAPI spec paths count", len(spec.get("paths", {})) >= 15, f"count={len(spec.get('paths',{}))}") except Exception as e: test("OpenAPI spec accessible", False, str(e)) # Summary print("\n" + "=" * 60) print(f"ACCEPTANCE TEST SUMMARY: {passed} passed, {failed} failed") print("=" * 60) if failed > 0: print("\nSome tests failed. Please review.") exit(1) else: print("\nAll backend API acceptance tests passed!") exit(0)