import math from typing import Any, Dict, List import pytest from fastapi.testclient import TestClient from services.reporting import generate_report def test_generate_report_empty(): report = generate_report([]) assert report == { "count": 0, "mean": 0.0, "median": 0.0, "min": 0.0, "max": 0.0, "std_dev": 0.0, "variance": 0.0, "percentile_10": 0.0, "percentile_90": 0.0, "percentile_5": 0.0, "percentile_95": 0.0, "value_at_risk_95": 0.0, "expected_shortfall_95": 0.0, } def test_generate_report_with_values(): values: List[Dict[str, float]] = [ {"iteration": 1, "result": 10.0}, {"iteration": 2, "result": 20.0}, {"iteration": 3, "result": 30.0}, ] report = generate_report(values) assert report["count"] == 3 assert math.isclose(float(report["mean"]), 20.0) assert math.isclose(float(report["median"]), 20.0) assert math.isclose(float(report["min"]), 10.0) assert math.isclose(float(report["max"]), 30.0) assert math.isclose(float(report["std_dev"]), 8.1649658, rel_tol=1e-6) assert math.isclose(float(report["variance"]), 66.6666666, rel_tol=1e-6) assert math.isclose(float(report["percentile_10"]), 12.0) assert math.isclose(float(report["percentile_90"]), 28.0) assert math.isclose(float(report["percentile_5"]), 11.0) assert math.isclose(float(report["percentile_95"]), 29.0) assert math.isclose(float(report["value_at_risk_95"]), 11.0) assert math.isclose(float(report["expected_shortfall_95"]), 10.0) def test_generate_report_single_value(): report = generate_report( [ {"iteration": 1, "result": 42.0}, ] ) assert report["count"] == 1 assert report["std_dev"] == 0.0 assert report["variance"] == 0.0 assert report["percentile_10"] == 42.0 assert report["expected_shortfall_95"] == 42.0 def test_generate_report_ignores_invalid_entries(): raw_values: List[Any] = [ {"iteration": 1, "result": 10.0}, "not-a-mapping", {"iteration": 2}, {"iteration": 3, "result": None}, {"iteration": 4, "result": 20}, ] report = generate_report(raw_values) assert report["count"] == 2 assert math.isclose(float(report["mean"]), 15.0) assert math.isclose(float(report["min"]), 10.0) assert math.isclose(float(report["max"]), 20.0) @pytest.fixture def client(api_client: TestClient) -> TestClient: return api_client def test_reporting_endpoint_invalid_input(client: TestClient): resp = client.post("/api/reporting/summary", json={}) assert resp.status_code == 400 assert resp.json()["detail"] == "Invalid input format" def test_reporting_endpoint_success(client: TestClient): input_data: List[Dict[str, float]] = [ {"iteration": 1, "result": 10.0}, {"iteration": 2, "result": 20.0}, {"iteration": 3, "result": 30.0}, ] resp = client.post("/api/reporting/summary", json=input_data) assert resp.status_code == 200 data: Dict[str, Any] = resp.json() assert data["count"] == 3 assert math.isclose(float(data["mean"]), 20.0) assert math.isclose(float(data["variance"]), 66.6666666, rel_tol=1e-6) assert math.isclose(float(data["value_at_risk_95"]), 11.0) assert math.isclose(float(data["expected_shortfall_95"]), 10.0) validation_error_cases: List[tuple[List[Any], str]] = [ (["not-a-dict"], "Entry at index 0 must be an object"), ([{"iteration": 1}], "Entry at index 0 must include numeric 'result'"), ( [{"iteration": 1, "result": "bad"}], "Entry at index 0 must include numeric 'result'", ), ] @pytest.mark.parametrize("payload,expected_detail", validation_error_cases) def test_reporting_endpoint_validation_errors( client: TestClient, payload: List[Any], expected_detail: str ): resp = client.post("/api/reporting/summary", json=payload) assert resp.status_code == 400 assert resp.json()["detail"] == expected_detail