| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- """Integration test for dual-system workflow (System 2 <-> System 1).
- Tests the full closed loop:
- 1. Create project
- 2. Create simulation plan
- 3. Upload scan results CSV (simulating System 2 execution)
- 4. Import results to experience library
- 5. Query experience library (list/get/update/delete)
- 6. Query analytics (trend/pareto/sensitivity/overview/stats/similar)
- Uses FastAPI TestClient (no real server needed).
- All source is ASCII.
- """
- import io
- import json
- import sys
- import os
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
- from fastapi.testclient import TestClient
- from app.main import app
- client = TestClient(app)
- # Sample CSV content simulating scan_results.csv from System 2
- SAMPLE_CSV = """run_index,status,seconds,Airgap,RMSCurrent,MagnetThickness,tavg_nm,ripple_pct,efficiency_pct,total_losses_w,copper_loss_w,iron_loss_w
- 0,OK,120,0.8,20,5,2.800,3.50,86.0,50.0,30.0,15.0
- 1,OK,118,1.0,20,5,2.500,3.20,85.2,48.0,28.0,14.0
- 2,OK,121,1.2,20,5,2.300,2.90,84.8,45.0,26.0,13.0
- 3,OK,125,1.0,25,5,3.000,4.10,86.5,55.0,35.0,15.0
- 4,FAILED,0,1.5,20,5,0,0,0,0,0,0
- """
- passed = 0
- failed = 0
- 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("Dual-System Integration Test (System 2 <-> System 1)")
- print("=" * 60)
- # 1. Health check
- print("\n[1] Health Check")
- r = client.get("/api/health")
- test("health endpoint returns 200", r.status_code == 200, f"status={r.status_code}")
- test("health status is ok", r.json().get("status") == "ok")
- # 2. Create project
- print("\n[2] Create Project")
- r = client.post("/api/projects", json={
- "name": "Integration Test Project",
- "topology": "SSSR",
- "description": "Created by integration test",
- "boundary_conditions": {"outer_radius_mm": 100, "speed_rpm": 3000}
- })
- test("create project returns 201", r.status_code == 201, f"status={r.status_code}")
- project_id = r.json().get("id")
- test("project has id", project_id is not None)
- test("project name correct", r.json().get("name") == "Integration Test Project")
- # 3. Create plan
- print("\n[3] Create Simulation Plan")
- r = client.post("/api/plans", json={
- "project_id": project_id,
- "name": "Test Airgap Scan",
- "plan_id": "INT-TEST-001",
- "topology": "SSSR",
- "model_path": "models/test.mot",
- "plan_data": {
- "plan_id": "INT-TEST-001",
- "model_path": "models/test.mot",
- "variables": [
- {"name": "Airgap", "display_name": "Airgap", "unit": "mm", "values": [0.8, 1.0, 1.2]},
- {"name": "RMSCurrent", "display_name": "RMS Current", "unit": "A", "values": [20, 25]}
- ]
- },
- "estimated_points": 6
- })
- test("create plan returns 201", r.status_code == 201, f"status={r.status_code}, body={r.text}")
- plan_id = r.json().get("id")
- test("plan has id", plan_id is not None)
- # 4. Download plan (System 2 fetches plan)
- print("\n[4] Download Plan (System 2 -> System 1)")
- r = client.get(f"/api/plans/{plan_id}/download")
- test("download plan returns 200", r.status_code == 200, f"status={r.status_code}")
- plan_data = r.json()
- test("download has plan_data", "plan_data" in plan_data)
- test("plan_data has variables", "variables" in plan_data.get("plan_data", {}))
- # 5. Upload results CSV (System 2 uploads results)
- print("\n[5] Upload Results CSV (System 2 -> System 1)")
- csv_file = ("scan_results.csv", SAMPLE_CSV.encode("utf-8"), "text/csv")
- r = client.post(
- f"/api/plans/{plan_id}/upload-results",
- files={"file": csv_file}
- )
- test("upload results returns 201", r.status_code == 201, f"status={r.status_code}, body={r.text}")
- upload_result = r.json()
- test("upload reports count", upload_result.get("count", 0) > 0, f"result={upload_result}")
- test("upload reports 5 results", upload_result.get("count") == 5, f"count={upload_result.get('count')}")
- # 6. Query plan results
- print("\n[6] Query Plan Results")
- r = client.get(f"/api/plans/{plan_id}/results")
- test("get results returns 200", r.status_code == 200)
- results = r.json().get("items", [])
- test("results has 5 items", len(results) == 5, f"count={len(results)}")
- ok_results = [r for r in results if r.get("status") == "OK"]
- test("4 OK results", len(ok_results) == 4, f"ok_count={len(ok_results)}")
- # 7. Import results to experience library
- print("\n[7] Import Results to Experience Library")
- r = client.post(f"/api/experience/from-plan/{plan_id}", json={
- "tags": ["integration-test", "auto-imported"],
- "rating": 3,
- "auto_conclusion": True
- })
- test("import returns 200", r.status_code == 200, f"status={r.status_code}, body={r.text}")
- import_result = r.json()
- test("imported 4 cases", import_result.get("imported") == 4, f"imported={import_result.get('imported')}")
- # 8. Query experience library
- print("\n[8] Query Experience Library")
- r = client.get("/api/experience?limit=50")
- test("list experience returns 200", r.status_code == 200)
- cases = r.json().get("items", [])
- test("at least 4 cases", len(cases) >= 4, f"count={len(cases)}")
- # Find our imported cases
- test_cases = [c for c in cases if "integration-test" in c.get("tags", [])]
- test("found integration-test cases", len(test_cases) >= 4, f"count={len(test_cases)}")
- if test_cases:
- case_id = test_cases[0]["id"]
- # Get single case
- r = client.get(f"/api/experience/{case_id}")
- test("get single case returns 200", r.status_code == 200)
- test("case has params", bool(r.json().get("params")))
- test("case has metrics", bool(r.json().get("metrics")))
- test("case has auto-generated conclusion", bool(r.json().get("conclusion")))
- # Update case
- r = client.put(f"/api/experience/{case_id}", json={
- "conclusion": "Updated by integration test",
- "tags": ["integration-test", "updated"],
- "rating": 5
- })
- test("update case returns 200", r.status_code == 200)
- test("updated conclusion", r.json().get("conclusion") == "Updated by integration test")
- test("updated rating", r.json().get("rating") == 5)
- # 9. Analytics: experience stats
- print("\n[9] Analytics: Experience Stats")
- r = client.get("/api/analytics/experience/stats")
- test("experience stats returns 200", r.status_code == 200)
- stats = r.json()
- test("stats has total", "total" in stats)
- test("stats has topology_distribution", "topology_distribution" in stats)
- test("stats has metric_ranges", "metric_ranges" in stats)
- # 10. Analytics: similar case search
- print("\n[10] Analytics: Similar Case Search")
- r = client.post("/api/analytics/experience/similar?top_k=3&tolerance=0.5", json={
- "params": {"Airgap": 1.0, "RMSCurrent": 20}
- })
- test("similar search returns 200", r.status_code == 200, f"status={r.status_code}, body={r.text}")
- similar = r.json().get("items", [])
- test("found similar cases", len(similar) >= 1, f"count={len(similar)}")
- if similar:
- test("similar has similarity_score", "similarity_score" in similar[0])
- # 11. Analytics: plan trend
- print("\n[11] Analytics: Plan Trend")
- r = client.get(f"/api/analytics/plans/{plan_id}/trend?param_key=Airgap&metric_key=tavg_nm")
- test("trend returns 200", r.status_code == 200, f"status={r.status_code}, body={r.text}")
- trend = r.json()
- test("trend has points", "points" in trend and len(trend["points"]) >= 3)
- test("trend points sorted by x", all(trend["points"][i][0] <= trend["points"][i+1][0] for i in range(len(trend["points"])-1)))
- # 12. Analytics: Pareto frontier
- print("\n[12] Analytics: Pareto Frontier")
- r = client.get(f"/api/analytics/plans/{plan_id}/pareto?x_metric=total_losses_w&y_metric=efficiency_pct")
- test("pareto returns 200", r.status_code == 200, f"status={r.status_code}, body={r.text}")
- pareto = r.json()
- test("pareto has all_points", "all_points" in pareto)
- test("pareto has pareto_points", "pareto_points" in pareto)
- test("pareto total count = 4", pareto.get("total_count") == 4, f"count={pareto.get('total_count')}")
- # 13. Analytics: sensitivity
- print("\n[13] Analytics: Parameter Sensitivity")
- r = client.get(f"/api/analytics/plans/{plan_id}/sensitivity?metric_key=tavg_nm")
- test("sensitivity returns 200", r.status_code == 200, f"status={r.status_code}, body={r.text}")
- sens = r.json()
- test("sensitivity has items", "items" in sens)
- if sens.get("items"):
- test("sensitivity sorted by abs_correlation desc",
- all(sens["items"][i]["abs_correlation"] >= sens["items"][i+1]["abs_correlation"]
- for i in range(len(sens["items"])-1)))
- # 14. Analytics: project overview
- print("\n[14] Analytics: Project Overview")
- r = client.get(f"/api/analytics/projects/{project_id}/overview")
- test("project overview returns 200", r.status_code == 200, f"status={r.status_code}, body={r.text}")
- overview = r.json()
- test("overview has total_plans", overview.get("total_plans") >= 1)
- test("overview has total_results", overview.get("total_results") >= 5)
- test("overview has ok_results", overview.get("ok_results") >= 4)
- test("overview has best_efficiency_pct", "best_efficiency_pct" in overview)
- test("overview has best_torque_nm", "best_torque_nm" in overview)
- # 15. Metric definitions
- print("\n[15] Metric Definitions")
- r = client.get("/api/analytics/metrics")
- test("metrics returns 200", r.status_code == 200)
- metrics = r.json().get("metrics", [])
- test("at least 10 metrics", len(metrics) >= 10, f"count={len(metrics)}")
- test("metric has key/label/unit", all("key" in m and "label" in m and "unit" in m for m in metrics))
- # 16. Delete test cases (cleanup)
- print("\n[16] Cleanup: Delete Test Cases")
- r = client.get("/api/experience?limit=50")
- test_cases = [c for c in r.json().get("items", []) if "integration-test" in c.get("tags", [])]
- deleted = 0
- for c in test_cases:
- r = client.delete(f"/api/experience/{c['id']}")
- if r.status_code == 204:
- deleted += 1
- test(f"deleted {len(test_cases)} test cases", deleted == len(test_cases), f"deleted={deleted}")
- # Summary
- print("\n" + "=" * 60)
- print(f"TEST SUMMARY: {passed} passed, {failed} failed")
- print("=" * 60)
- if failed > 0:
- sys.exit(1)
- else:
- print("\nAll integration tests passed!")
- sys.exit(0)
|