"""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)