import asyncio import sys import unittest from pathlib import Path from unittest.mock import MagicMock, patch from fastapi import HTTPException AI_ENGINE_ROOT = Path(__file__).resolve().parents[1] if str(AI_ENGINE_ROOT) not in sys.path: sys.path.insert(0, str(AI_ENGINE_ROOT)) import main as ai_main def _run(coro): return asyncio.run(coro) class MainApiFunctionTests(unittest.TestCase): def test_analyze_match_v20plus_returns_payload(self): orchestrator = MagicMock() orchestrator.analyze_match.return_value = {"match_info": {"match_id": "m1"}} with patch("main.get_single_match_orchestrator", return_value=orchestrator): result = _run(ai_main.analyze_match_v20plus("m1")) self.assertEqual(result["match_info"]["match_id"], "m1") def test_analyze_match_v20plus_raises_404(self): orchestrator = MagicMock() orchestrator.analyze_match.return_value = None with patch("main.get_single_match_orchestrator", return_value=orchestrator): with self.assertRaises(HTTPException) as ctx: _run(ai_main.analyze_match_v20plus("missing")) self.assertEqual(ctx.exception.status_code, 404) def test_analyze_match_htms_v20plus_returns_payload(self): orchestrator = MagicMock() orchestrator.analyze_match_htms.return_value = { "status": "ok", "engine_used": "v20plus_top_htms", } with patch("main.get_single_match_orchestrator", return_value=orchestrator): result = _run(ai_main.analyze_match_htms_v20plus("m1")) self.assertEqual(result["status"], "ok") self.assertEqual(result["engine_used"], "v20plus_top_htms") def test_analyze_match_htft_timeout_validation(self): with self.assertRaises(HTTPException) as ctx: _run(ai_main.analyze_match_htft_v20plus("m1", timeout_sec=2)) self.assertEqual(ctx.exception.status_code, 400) def test_generate_coupon_v20plus_forwards_payload(self): orchestrator = MagicMock() orchestrator.build_coupon.return_value = {"bets": []} request = ai_main.CouponRequest( match_ids=["m1", "m2"], strategy="SAFE", max_matches=3, min_confidence=70, ) with patch("main.get_single_match_orchestrator", return_value=orchestrator): result = _run(ai_main.generate_coupon_v20plus(request)) self.assertEqual(result, {"bets": []}) orchestrator.build_coupon.assert_called_once_with( match_ids=["m1", "m2"], strategy="SAFE", max_matches=3, min_confidence=70.0, ) def test_reversal_watchlist_validation(self): with self.assertRaises(HTTPException) as ctx: _run(ai_main.get_reversal_watchlist_v20plus(count=0)) self.assertEqual(ctx.exception.status_code, 400) def test_reversal_watchlist_forwards_payload(self): orchestrator = MagicMock() orchestrator.get_reversal_watchlist.return_value = {"watchlist": []} with patch("main.get_single_match_orchestrator", return_value=orchestrator): result = _run( ai_main.get_reversal_watchlist_v20plus( count=12, horizon_hours=48, min_score=50.5, top_leagues_only=True, ), ) self.assertEqual(result, {"watchlist": []}) orchestrator.get_reversal_watchlist.assert_called_once_with( count=12, horizon_hours=48, min_score=50.5, top_leagues_only=True, ) if __name__ == "__main__": unittest.main()