111 lines
3.6 KiB
Python
Executable File
111 lines
3.6 KiB
Python
Executable File
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()
|