Commit b57de374 authored by Your Name's avatar Your Name

Update extraction controller targeting

parent 2bcbdd45
This diff is collapsed.
This diff is collapsed.
from pathlib import Path
from unittest.mock import patch
import random
from mbetterclient.core.games_thread import GamesThread
from mbetterclient.core.message_bus import Message, MessageBus, MessageType
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.database.models import (
BetDetailModel,
GameConfigModel,
MatchModel,
MatchOutcomeModel,
PersistentRedistributionAdjustmentModel,
ResultOptionModel,
ExtractionAssociationModel,
)
def _build_games_thread(tmp_path: Path):
db_path = tmp_path / "accounting_test.db"
db_manager = DatabaseManager(str(db_path))
assert db_manager.initialize()
message_bus = MessageBus()
games_thread = GamesThread("test_games_thread", message_bus, db_manager)
return games_thread, db_manager
def _seed_match_and_bets(db_manager: DatabaseManager):
session = db_manager.get_session()
try:
cap_config = session.query(GameConfigModel).filter_by(config_key='extraction_redistribution_cap').first()
if cap_config is None:
session.add(GameConfigModel(
config_key='extraction_redistribution_cap',
config_value='70.0',
value_type='float',
description='Test cap'
))
else:
cap_config.config_value = '70.0'
cap_config.value_type = 'float'
if session.query(ResultOptionModel).filter_by(result_name='WIN1').first() is None:
session.add(ResultOptionModel(result_name='WIN1', description='Win 1', is_active=True, sort_order=1))
for outcome_name in ('WIN1', 'KO1'):
exists = session.query(ExtractionAssociationModel).filter_by(
outcome_name=outcome_name,
extraction_result='WIN1'
).first()
if exists is None:
session.add(ExtractionAssociationModel(outcome_name=outcome_name, extraction_result='WIN1'))
match = MatchModel(
match_number=1,
fighter1_township='A',
fighter2_township='B',
venue_kampala_township='Kampala',
filename='fixture.txt',
file_sha1sum='fixture-sha1',
fixture_id='fixture-1',
active_status=True,
status='bet'
)
session.add(match)
session.flush()
session.add_all([
MatchOutcomeModel(match_id=match.id, column_name='UNDER', float_value=2.0),
MatchOutcomeModel(match_id=match.id, column_name='OVER', float_value=2.5),
MatchOutcomeModel(match_id=match.id, column_name='WIN1', float_value=3.0),
MatchOutcomeModel(match_id=match.id, column_name='KO1', float_value=4.0),
])
session.add_all([
BetDetailModel(bet_id='bet-under', match_id=match.id, match_number=1, outcome='UNDER', amount=10.0, result='pending'),
BetDetailModel(bet_id='bet-win1', match_id=match.id, match_number=1, outcome='WIN1', amount=20.0, result='pending'),
BetDetailModel(bet_id='bet-ko1', match_id=match.id, match_number=1, outcome='KO1', amount=15.0, result='pending'),
])
session.commit()
return match.id, match.fixture_id
finally:
session.close()
def test_match_start_uses_settled_winnings_for_adjustment(tmp_path):
games_thread, db_manager = _build_games_thread(tmp_path)
match_id, fixture_id = _seed_match_and_bets(db_manager)
message = Message(
type=MessageType.MATCH_START,
sender='test',
recipient='test_games_thread',
data={'fixture_id': fixture_id, 'match_id': match_id}
)
with patch('mbetterclient.core.games_thread.extract_match', return_value=('UNDER', 'WIN1', 0.0, 'WIN1, KO1')):
with patch.object(games_thread, '_send_play_video_match', return_value=None):
games_thread._handle_match_start(message)
session = db_manager.get_session()
try:
global_record = session.query(PersistentRedistributionAdjustmentModel).filter_by(date='1970-01-01').one()
winning_bets = session.query(BetDetailModel).filter_by(match_id=match_id, result='win').all()
actual_redistributed = sum(float(bet.win_amount or 0.0) for bet in winning_bets)
assert actual_redistributed == 140.0
assert global_record.total_redistributed == actual_redistributed
assert global_record.total_payin == 45.0
assert global_record.accumulated_shortfall == (45.0 * 0.70) - actual_redistributed
finally:
session.close()
db_manager.close()
def _seed_simulation_match(session, fixture_id: str, match_number: int, under_amount: float, over_amount: float, win1_amount: float, ko1_amount: float):
match = MatchModel(
match_number=match_number,
fighter1_township=f'F{match_number}A',
fighter2_township=f'F{match_number}B',
venue_kampala_township='Kampala',
filename=f'fixture-{match_number}.txt',
file_sha1sum=f'fixture-sha1-{match_number}',
fixture_id=fixture_id,
active_status=True,
status='bet'
)
session.add(match)
session.flush()
session.add_all([
MatchOutcomeModel(match_id=match.id, column_name='UNDER', float_value=2.0),
MatchOutcomeModel(match_id=match.id, column_name='OVER', float_value=2.0),
MatchOutcomeModel(match_id=match.id, column_name='WIN1', float_value=2.0),
MatchOutcomeModel(match_id=match.id, column_name='KO1', float_value=2.0),
])
bet_rows = []
for index, (outcome, amount) in enumerate((
('UNDER', under_amount),
('OVER', over_amount),
('WIN1', win1_amount),
('KO1', ko1_amount),
), start=1):
if amount <= 0:
continue
bet_rows.append(BetDetailModel(
bet_id=f'bet-{match_number}-{index}',
match_id=match.id,
match_number=match_number,
outcome=outcome,
amount=amount,
result='pending'
))
session.add_all(bet_rows)
return match.id
def test_match_start_simulation_tracks_persisted_settled_totals(tmp_path):
games_thread, db_manager = _build_games_thread(tmp_path)
fixture_id = 'fixture-sim'
rng = random.Random(7)
rounds = 120
total_payin = 0.0
total_redistributed = 0.0
session = db_manager.get_session()
try:
session.query(ExtractionAssociationModel).delete()
session.query(ResultOptionModel).delete()
session.add(ResultOptionModel(result_name='WIN1', description='Win 1', is_active=True, sort_order=1))
session.add(ExtractionAssociationModel(outcome_name='WIN1', extraction_result='WIN1'))
session.add(ExtractionAssociationModel(outcome_name='KO1', extraction_result='WIN1'))
session.commit()
finally:
session.close()
try:
for match_number in range(1, rounds + 1):
under_amount = rng.uniform(20.0, 60.0)
over_amount = rng.uniform(20.0, 60.0)
win1_amount = rng.uniform(20.0, 60.0)
ko1_amount = rng.uniform(20.0, 60.0)
session = db_manager.get_session()
try:
match_id = _seed_simulation_match(
session,
fixture_id,
match_number,
under_amount,
over_amount,
win1_amount,
ko1_amount,
)
session.commit()
finally:
session.close()
message = Message(
type=MessageType.MATCH_START,
sender='test',
recipient='test_games_thread',
data={'fixture_id': fixture_id, 'match_id': match_id}
)
with patch.object(games_thread, '_send_play_video_match', return_value=None):
games_thread._handle_match_start(message)
session = db_manager.get_session()
try:
winning_bets = session.query(BetDetailModel).filter_by(match_id=match_id, result='win').all()
match_payin = sum(float(bet.amount or 0.0) for bet in session.query(BetDetailModel).filter_by(match_id=match_id).all())
match_redistributed = sum(float(bet.win_amount or 0.0) for bet in winning_bets)
total_payin += match_payin
total_redistributed += match_redistributed
finally:
session.close()
session = db_manager.get_session()
try:
global_record = session.query(PersistentRedistributionAdjustmentModel).filter_by(date='1970-01-01').one()
assert abs(global_record.total_payin - total_payin) < 1e-6
assert abs(global_record.total_redistributed - total_redistributed) < 1e-6
assert abs(
global_record.accumulated_shortfall - ((total_payin * 0.70) - total_redistributed)
) < 1e-6
finally:
session.close()
finally:
db_manager.close()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment