import pytest import pandas as pd import pkgutil import importlib import responses import fakeredis from typing import Dict from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker from responses.matchers import json_params_matcher from fastapi.testclient import TestClient from src.main import app, provide_connection from src.repository.common import get_session from src.entity import Base # ---------------------------------------------------------------- # Load all classes from src.entity module = importlib.import_module('src.entity') # Loop over all modules in the 'src.entity' package package = importlib.import_module('src.entity') for _, module_name, _ in pkgutil.walk_packages(package.__path__, package.__name__ + '.'): module = importlib.import_module(module_name) @pytest.fixture def client(db_session): """ Create a test client for the FastAPI app. """ # Override the get_session dependency to use the test database session def override_get_session(): yield db_session # Override the dependency in the FastAPI app app.dependency_overrides[get_session] = override_get_session app.dependency_overrides[provide_connection] = override_get_session return TestClient(app) # ---------------------------------------------------------------- # Fixtures for Database # ---------------------------------------------------------------- @pytest.fixture def db_session(postgresql): """ Create a new database session for each test and tear it down after the test. """ # Create a new database connection host = postgresql.info.host port = postgresql.info.port user = postgresql.info.user dbname = postgresql.info.dbname dsn = f"postgresql+psycopg://{user}@{host}:{port}/{dbname}" engine = create_engine(dsn, echo=True) # Create schema and tables once with engine.begin() as conn: conn.execute(text("CREATE SCHEMA IF NOT EXISTS data")) Base.metadata.create_all(engine) connection = engine.connect() connection.begin() SessionLocal = sessionmaker(bind=connection) session = SessionLocal() try: yield session except Exception: session.rollback() # In case of an error, rollback the session raise finally: session.close() connection.close() @pytest.fixture() def with_materialized_views(db_session): """ Create materialized views for testing. """ # Create the ref_surface_m_view materialized view db_session.execute(text(""" CREATE MATERIALIZED VIEW IF NOT EXISTS data.ref_surface_m_view AS SELECT * FROM ( VALUES ('Carpet'), ('Clay'), ('Grass'), ('Hard') ) AS t(name); """)) # Create the ref_court_m_view materialized view db_session.execute(text(""" CREATE MATERIALIZED VIEW IF NOT EXISTS data.ref_court_m_view AS SELECT * FROM ( VALUES ('Indoor'), ('Outdoor') ) AS t(name); """)) # Create the ref_series_m_view materialized view db_session.execute(text(""" CREATE MATERIALIZED VIEW IF NOT EXISTS data.ref_series_m_view AS SELECT * FROM ( VALUES ('ATP250'), ('ATP500'), ('Grand Slam'), ('International'), ('International Gold'), ('Masters'), ('Masters 1000'), ('Masters Cup') ) AS t(name); """)) yield # Use of fixture to inject a fake Redis client into the tests @pytest.fixture def fake_redis(): # Create a fake Redis instance fake_redis_instance = fakeredis.FakeStrictRedis() yield fake_redis_instance # ----------------------------------------------------------------- # Fixtures for requests calls # ----------------------------------------------------------------- @pytest.fixture(autouse=True) def mock_responses(): with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: rsps.add( rsps.GET, "https://www.atptour.com/en/-/www/players/hero/F324/", json={ "LastName": "Federer", "FirstName": "Roger", "MidInitial": None, "BirthCity": "Basel, Switzerland", "Residence": None, "Coach": "Ivan Ljubicic, Severin Luthi", "Pronunciation": None, "BirthDate": "1981-08-08T00:00:00", "Age": None, "NatlId": "SUI", "Nationality": "Switzerland", "HeightIn": 73, "HeightFt": "6'1\"", "HeightCm": 185, "WeightLb": 187, "WeightKg": 85, "PlayHand": { "Id": "R", "Description": "Right-Handed" }, "BackHand": { "Id": "1", "Description": "One-Handed" }, "ProYear": 1998, "Active": { "Id": "I", "Description": "Inactive" }, "DblSpecialist": False, "SglRank": None, "SglHiRank": 1, "SglRankMove": 0, "SglRankTie": False, "DblRank": None, "DblHiRank": 24, "DblRankMove": 0, "DblRankTie": False, "ScRelativeUrlPlayerProfile": "/en/players/roger-federer/f324/overview", "ScRelativeUrlPlayerCountryFlag": "/en/~/media/images/flags/sui.svg", "GladiatorImageUrl": None, "SglCareerWon": 1251, "SglCareerLost": 275, "SglYtdWon": 0, "SglYtdLost": 0, "SglCareerTitles": 103, "SglYtdTitles": 0, "SglYtdPrizeFormatted": "$0", "CareerPrizeFormatted": "$130,594,339", "DblCareerWon": 131, "DblCareerLost": 93, "DblYtdWon": 0, "DblYtdLost": 0, "DblCareerTitles": 8, "DblYtdTitles": 0, "DblYtdPrizeFormatted": "$0", "IsCarbonTrackerEnabled": False, "SocialLinks": [ { "SocialId": "FB", "SocialLink": "https://www.facebook.com/Federer" }, { "SocialId": "IG", "SocialLink": "https://www.instagram.com/rogerfederer/" }, { "SocialId": "TW", "SocialLink": "https://twitter.com/rogerfederer" }, { "SocialId": "Web", "SocialLink": "http://www.rogerfederer.com" } ], "CacheTags": None, "TopCourtLink": "", "SglHiRankDate": "2004-02-02T00:00:00", "DblHiRankDate": "2003-06-09T00:00:00" }, status=200 ) rsps.add( rsps.GET, "https://www.atptour.com/en/-/www/site-search/federer/", json={ "Players": [ { "PlayerId": "F324", "LastName": "Federer", "FirstName": "Roger", "NatlId": "SUI", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=F324&w=150&h=200", "SubCategoryName": "roger-federer-f324" } ], "Tournaments": [] }, status=200 ) rsps.add( rsps.GET, "https://www.atptour.com/en/-/www/site-search/herbert/", json={ "Players": [ { "PlayerId": "BS65", "LastName": "Baddeley", "FirstName": "Herbert", "NatlId": None, "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=BS65&w=150&h=200", "SubCategoryName": "herbert-baddeley-bs65" }, { "PlayerId": "BO78", "LastName": "Behrens", "FirstName": "Herbert", "NatlId": "USA", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=BO78&w=150&h=200", "SubCategoryName": "herbert-behrens-bo78" }, { "PlayerId": "B705", "LastName": "Bende", "FirstName": "Herbert", "NatlId": "SVK", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=B705&w=150&h=200", "SubCategoryName": "herbert-bende-b705" }, { "PlayerId": "BP00", "LastName": "Bowman", "FirstName": "Herbert", "NatlId": "USA", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=BP00&w=150&h=200", "SubCategoryName": "herbert-bowman-bp00" }, { "PlayerId": "BM64", "LastName": "Browne", "FirstName": "Herbert", "NatlId": "USA", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=BM64&w=150&h=200", "SubCategoryName": "herbert-browne-bm64" }, { "PlayerId": "CL93", "LastName": "Chipp", "FirstName": "Herbert", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=CL93&w=150&h=200", "SubCategoryName": "herbert-chipp-cl93" }, { "PlayerId": "FA25", "LastName": "Fischer", "FirstName": "Herbert", "NatlId": "USA", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=FA25&w=150&h=200", "SubCategoryName": "herbert-fischer-fa25" }, { "PlayerId": "H893", "LastName": "Herbert", "FirstName": "Chris", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=H893&w=150&h=200", "SubCategoryName": "chris-herbert-h893" }, { "PlayerId": "H966", "LastName": "Herbert", "FirstName": "Justus", "NatlId": "GER", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=H966&w=150&h=200", "SubCategoryName": "justus-herbert-h966" }, { "PlayerId": "H996", "LastName": "Herbert", "FirstName": "Pierre-Hugues", "NatlId": "FRA", "Active": "A", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=H996&w=150&h=200", "SubCategoryName": "pierre-hugues-herbert-h996" }, { "PlayerId": "H430", "LastName": "Herbert", "FirstName": "William", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=H430&w=150&h=200", "SubCategoryName": "william-herbert-h430" }, { "PlayerId": "J385", "LastName": "Jerich", "FirstName": "Herbert", "NatlId": "AUT", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=J385&w=150&h=200", "SubCategoryName": "herbert-jerich-j385" }, { "PlayerId": "KG69", "LastName": "Kinzl", "FirstName": "Herbert", "NatlId": "AUT", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=KG69&w=150&h=200", "SubCategoryName": "herbert-kinzl-kg69" }, { "PlayerId": "LG94", "LastName": "Lawford", "FirstName": "Herbert", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=LG94&w=150&h=200", "SubCategoryName": "herbert-lawford-lg94" }, { "PlayerId": "L0E1", "LastName": "Loerke", "FirstName": "Herbert", "NatlId": "GER", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=L0E1&w=150&h=200", "SubCategoryName": None }, { "PlayerId": "M0M2", "LastName": "Mann", "FirstName": "Herbert", "NatlId": "AUT", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=M0M2&w=150&h=200", "SubCategoryName": None }, { "PlayerId": "R509", "LastName": "Rapp", "FirstName": "Herbert", "NatlId": "USA", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=R509&w=150&h=200", "SubCategoryName": "herbert-rapp-r509" }, { "PlayerId": "RF72", "LastName": "Roper-Barrett", "FirstName": "Herbert", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=RF72&w=150&h=200", "SubCategoryName": "herbert-roper-barrett-rf72" }, { "PlayerId": "S264", "LastName": "Sandberg", "FirstName": "Herbert", "NatlId": "GER", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=S264&w=150&h=200", "SubCategoryName": "herbert-sandberg-s264" }, { "PlayerId": "TF94", "LastName": "Taylor", "FirstName": "Herbert", "NatlId": None, "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=TF94&w=150&h=200", "SubCategoryName": "herbert-taylor-tf94" }, { "PlayerId": "TF28", "LastName": "Turner", "FirstName": "Herbert", "NatlId": "AUS", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=TF28&w=150&h=200", "SubCategoryName": "herbert-turner-tf28" }, { "PlayerId": "W487", "LastName": "Weirather", "FirstName": "Herbert", "NatlId": "AUT", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=W487&w=150&h=200", "SubCategoryName": "herbert-weirather-w487" }, { "PlayerId": "W858", "LastName": "Whitney", "FirstName": "Herbert", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=W858&w=150&h=200", "SubCategoryName": "herbert-whitney-w858" }, { "PlayerId": "WA15", "LastName": "Wilberforce", "FirstName": "Herbert W.W.", "NatlId": None, "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=WA15&w=150&h=200", "SubCategoryName": "herbert-ww-wilberforce-wa15" }, { "PlayerId": "W964", "LastName": "Wilson-Fox", "FirstName": "Herbert", "NatlId": "GBR", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=W964&w=150&h=200", "SubCategoryName": "herbert-wilson-fox-w964" }, { "PlayerId": "W200", "LastName": "Wiltschnig", "FirstName": "Herbert", "NatlId": "AUT", "Active": "I", "PlayerHeadshotUrl": "https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=W200&w=150&h=200", "SubCategoryName": "herbert-wiltschnig-w200" } ], "Tournaments": [] }, status=200 ) rsps.add( rsps.POST, "http://localhost:8191/v1", match=[json_params_matcher({ "cmd": "request.get", "url": "https://www.atptour.com/en/-/www/site-search/gasquet/", "maxTimeout": 60000, })], json={ "status": "ok", "message": "Challenge not detected!", "solution": { "url": "https://www.atptour.com/en/-/www/site-search/gasquet/", "status": 200, "cookies": [ { "version": 0, "name": "__cf_bm", "value": "WDSm4XHIA8j37G9XV8CUfLKlUVTBysgyT23FkRppgtM-1745136598-1.0.1.1-m4nLAUUG.jeVm2QXn_9moCv_FQJ0x7tdVvrInb5kdJkN_v.larvZupdSl7yBs8WK3q32axMEUdZF5iIMAzjYp3ymMmJOy1TTa6BPvPuxSJQ", "port": None, "port_specified": False, "domain": ".atptour.com", "domain_specified": True, "domain_initial_dot": True, "path": "/", "path_specified": True, "secure": True, "expires": 1745138398, "discard": True, "comment": None, "comment_url": None, "rfc2109": False, "_rest": { "HttpOnly": None } }, { "version": 0, "name": "__Secure-ENID", "value": "27.SE=GdzvtamVZTVdmqlnUQ4oTV7-4n9LEA4-QFnIHHTH3wQDVKkV20g6PCFev_Sr17kvtHzSyb7Zl8c41QMOsYZecJRykBL-Yq7GM9JCc0YkS8LyXG-qrlyTXP2-_ifZn-KeYjIIrfv_QmptCwCQlkSGdstAQD3eKOwVbeVeUpytdY-i_Kai8uSkNp4LmmE0PXAIq4_DYqCf-b5HqFUOtELS89CBnSn6kaju82Ilk-PARJzxLOam", "port": None, "port_specified": False, "domain": ".google.com", "domain_specified": True, "domain_initial_dot": True, "path": "/", "path_specified": True, "secure": True, "expires": 1779323296, "discard": True, "comment": None, "comment_url": None, "rfc2109": False, "_rest": { "HttpOnly": None } } ], "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36", "headers": {}, "response": "
{\"Players\":[{\"PlayerId\":\"G628\",\"LastName\":\"Gasquet\",\"FirstName\":\"Richard\",\"NatlId\":\"FRA\",\"Active\":\"A\",\"PlayerHeadshotUrl\":\"https://www.atptour.com/en/-/ajax/PlayerSearch/HeadshotPhoto?playerId=G628&w=150&h=200\",\"SubCategoryName\":\"richard-gasquet-g628\"}],\"Tournaments\":[]}"
},
"startTimestamp": 1745136597163,
"endTimestamp": 1745136600038,
"version": "3.4.0"
},
)
rsps.add(
rsps.POST,
"http://localhost:8191/v1",
match=[json_params_matcher({
"cmd": "request.get",
"url": "https://www.atptour.com/en/-/www/players/hero/G628/",
"maxTimeout": 60000,
})],
json={
"status": "ok",
"message": "Challenge not detected!",
"solution": {
"url": "https://www.atptour.com/en/-/www/players/hero/G628/",
"status": 200,
"cookies": [
{
"version": 0,
"name": "__cf_bm",
"value": "GY6qsK8u3tOIPnVIMAm9GRdevol.fIfKCIiSh2lm0N0-1745144497-1.0.1.1-ll6tTf2iuAjap4V5dMY5a86bSCnkc.X3wLm51ynROa9uMDBWJiMbuPVFoYpAEjB6_x1JT.H3O3R4KA0kjyEwgWjH9lx.uAeArf6vOpl_ebc",
"port": None,
"port_specified": True,
"domain": ".atptour.com",
"domain_specified": True,
"domain_initial_dot": True,
"path": "/",
"path_specified": True,
"secure": True,
"expires": 1745146297,
"discard": True,
"comment": None,
"comment_url": None,
"rfc2109": True,
"_rest": {
"HttpOnly": None
}
}
],
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"headers": {},
"response": "{\"LastName\":\"Gasquet\",\"FirstName\":\"Richard\",\"MidInitial\":null,\"BirthCity\":\"Beziers, France\",\"Residence\":null,\"Coach\":\"Julien Cassaigne\",\"Pronunciation\":\"gas-KAY\",\"BirthDate\":\"1986-06-18T00:00:00\",\"Age\":38,\"NatlId\":\"FRA\",\"Nationality\":\"France\",\"HeightIn\":72,\"HeightFt\":\"6'0\\\"\",\"HeightCm\":183,\"WeightLb\":174,\"WeightKg\":79,\"PlayHand\":{\"Id\":\"R\",\"Description\":\"Right-Handed\"},\"BackHand\":{\"Id\":\"1\",\"Description\":\"One-Handed\"},\"ProYear\":2002,\"Active\":{\"Id\":\"A\",\"Description\":\"Active\"},\"DblSpecialist\":false,\"SglRank\":142,\"SglHiRank\":7,\"SglRankMove\":22,\"SglRankTie\":false,\"DblRank\":null,\"DblHiRank\":45,\"DblRankMove\":0,\"DblRankTie\":false,\"ScRelativeUrlPlayerProfile\":\"/en/players/richard-gasquet/g628/overview\",\"ScRelativeUrlPlayerCountryFlag\":\"/en/~/media/images/flags/fra.svg\",\"GladiatorImageUrl\":null,\"SglCareerWon\":609,\"SglCareerLost\":407,\"SglYtdWon\":3,\"SglYtdLost\":4,\"SglCareerTitles\":16,\"SglYtdTitles\":0,\"SglYtdPrizeFormatted\":\"$107,399\",\"CareerPrizeFormatted\":\"$21,338,168\",\"DblCareerWon\":72,\"DblCareerLost\":63,\"DblYtdWon\":0,\"DblYtdLost\":1,\"DblCareerTitles\":2,\"DblYtdTitles\":0,\"DblYtdPrizeFormatted\":\"$1,699\",\"IsCarbonTrackerEnabled\":false,\"SocialLinks\":[{\"SocialId\":\"IG\",\"SocialLink\":\"https://www.instagram.com/richardgasquet34/\"},{\"SocialId\":\"TW\",\"SocialLink\":\"https://twitter.com/richardgasquet1\"}],\"CacheTags\":null,\"TopCourtLink\":\"\",\"SglHiRankDate\":\"2007-07-09T00:00:00\",\"DblHiRankDate\":\"2008-04-07T00:00:00\"}"
},
"startTimestamp": 1745144495599,
"endTimestamp": 1745144498574,
"version": "3.4.0"
}
)
yield rsps
# ----------------------------------------------------------------
# Fixtures for Match Data
# ----------------------------------------------------------------
@pytest.fixture
def simple_match():
return pd.DataFrame({
'series': ['ATP250',],
'surface': ['Clay',],
'court': ['Indoor',],
'round': ['Round Robin',],
'w_rank': [5],
'l_rank': [300],
'w_points': [2000],
'l_points': [40],
})
@pytest.fixture
def simple_match_pairwise_data(simple_match: pd.DataFrame):
return pd.DataFrame({
'Series': ['ATP250', 'ATP250'],
'Surface': ['Clay', 'Clay'],
'Court': ['Indoor', 'Indoor'],
'Round': ['Round Robin', 'Round Robin'],
'diffRanking': [-295, 295],
'diffPoints': [1960, -1960],
'target': [1, 0]
})
@pytest.fixture
def simple_match_empty():
return pd.DataFrame({
'Series': [],
'Surface': [],
'Court': [],
'Round': [],
'diffRanking': [],
'diffPoints': [],
'target': []
})
@pytest.fixture
def raw_match():
return {
"Comment": "Completed",
"Best of": 3,
"Loser": "Djokovic N.",
"Round": "The Final",
"Winner": "Federer R.",
"Court": "Outdoor",
"Surface": "Grass",
"Wsets": 3,
"Lsets": 0,
"Date": "2019-06-15",
"WRank": 1,
"WPts": 4000,
"LPts": 3000,
"Tournament": "Wimbledon",
"LRank": 2,
"Location": "London",
"Series": "Grand Slam",
"W1": 6,
"W2": 6,
"W3": 6,
"W4": None,
"W5": None,
"L1": 3,
"L2": 2,
"L3": 0,
"L4": None,
"L5": None,
"AvgW": 1.3,
"AvgL": 2.3,
"MaxW": 1.5,
"MaxL": 1.8,
"B365W": 1.2,
"B365L": 4.5,
}
@pytest.fixture
def raw_matches_batch(raw_match: Dict):
return [
raw_match,
{
"Comment": "Completed",
"Best of": 3,
"Loser": "Nadal R.",
"Round": "The Final",
"Winner": "Djokovic N.",
"Court": "Outdoor",
"Surface": "Hard",
"Wsets": 3,
"Lsets": 0,
"Date": "2022-01-21",
"WRank": 1,
"WPts": 4000,
"LPts": 3000,
"Tournament": "Australian Open",
"LRank": 2,
"Location": "Melbourne",
"Series": "Grand Slam",
"W1": 6,
"W2": 6,
"W3": 6,
"W4": None,
"W5": None,
"L1": 3,
"L2": 2,
"L3": 0,
"L4": None,
"L5": None,
"AvgW": 1.3,
"AvgL": 2.3,
"MaxW": 1.5,
"MaxL": 1.8,
"B365W": 1.2,
"B365L": 4.5,
}
]
# ----------------------------------------------------------------
# Fixtures for Player Data
# ----------------------------------------------------------------
@pytest.fixture
def super_joueur(db_session):
"""
Create a super player with caracteristics and add it to the database
"""
from src.entity.player import Player, Caracteristics
player = Player(
name="Joueur S.",
tennis_id='J001',
caracteristics=Caracteristics(
first_name="Super",
last_name="Joueur",
date_of_birth="1981-08-08",
nationality="France",
height_cm=185,
weight_kg=85,
play_hand="R",
back_hand=1,
pro_year=1998,
)
)
db_session.add(player)
db_session.commit()
return player
@pytest.fixture
def tout_nouveau_joueur(db_session):
"""
Create a new player without caracteristics nor tennis_id and add it to the database
"""
from src.entity.player import Player
player = Player(name="Joueur T.N.")
db_session.add(player)
db_session.commit()
return player
@pytest.fixture
def nouveau_joueur(db_session):
"""
Create a new player without caracteristics and add it to the database
"""
from src.entity.player import Player
player = Player(
name="Joueur N.",
tennis_id='J002',
)
db_session.add(player)
db_session.commit()
return player
@pytest.fixture
def wimbledon_final(db_session):
"""
Create a Wimbledon final match with odds and new players
"""
from src.entity.match import Match
from src.entity.odds import Odds
from src.entity.player import Player
match = Match(
date="2019-06-15",
comment="Completed",
winner=Player(name="Federer R."),
loser=Player(name="Djokovic N."),
tournament_name="Wimbledon",
tournament_series="Grand Slam",
tournament_surface="Grass",
tournament_court="Outdoor",
tournament_round="The Final",
tournament_location="London",
winner_rank=1,
winner_points=4000,
loser_rank=2,
loser_points=3000,
)
match.odds = [
Odds(
bookmaker="B365",
winner=1.2,
loser=4.5,
match=match
),
Odds(
bookmaker="PS",
winner=1.22,
loser=4.52,
match=match
),
Odds(
bookmaker="Max",
winner=1.24,
loser=4.55,
match=match
),
Odds(
bookmaker="Avg",
winner=1.21,
loser=4.7,
match=match
),
]
# Add the match to the database
db_session.add(match)
db_session.commit()
return match
@pytest.fixture
def wimbledon_final_raw(wimbledon_final):
"""
Create a Wimbledon final match with odds and new players
"""
raw_match = {
"Comment": wimbledon_final.comment,
"Loser": wimbledon_final.loser.name,
"Round": wimbledon_final.tournament_round,
"Winner": wimbledon_final.winner.name,
"Court": wimbledon_final.tournament_court,
"Surface": wimbledon_final.tournament_surface,
"Date": wimbledon_final.date.strftime("%Y-%m-%d"),
"Tournament": wimbledon_final.tournament_name,
"Location": wimbledon_final.tournament_location,
"Series": wimbledon_final.tournament_series,
"WRank": wimbledon_final.winner_rank,
"WPts": wimbledon_final.winner_points,
"LPts": wimbledon_final.loser_points,
"LRank": wimbledon_final.loser_rank,
"Best of": 3,
"Wsets": 3,
"Lsets": 0,
'W1': 6,
'W2': 6,
'W3': 6,
'W4': None,
'W5': None,
'L1': 3,
'L2': 2,
'L3': 0,
'L4': None,
'L5': None,
}
for i, odds in enumerate(wimbledon_final.odds):
raw_match[f'{odds.bookmaker}W'] = odds.winner
raw_match[f'{odds.bookmaker}L'] = odds.loser
return raw_match
@pytest.fixture
def roland_garros_final():
"""
Create a Roland Garros final match with odds and new players
"""
from src.entity.match import Match
from src.entity.odds import Odds
from src.entity.player import Player
match = Match(
date="2023-06-11",
comment="Completed",
winner=Player(name="Djokovic N."),
loser=Player(name="Ruud C."),
tournament_name="Roland Garros",
tournament_series="Grand Slam",
tournament_surface="Clay",
tournament_court="Outdoor",
tournament_round="The Final",
tournament_location="Paris",
winner_rank=3,
winner_points=5955,
loser_rank=4,
loser_points=4960,
)
match.odds = [
Odds(
bookmaker="B365",
winner=1.2,
loser=4.8,
match=match
),
Odds(
bookmaker="PS",
winner=1.22,
loser=4.92,
match=match
),
Odds(
bookmaker="Max",
winner=1.24,
loser=5.15,
match=match
),
Odds(
bookmaker="Avg",
winner=1.21,
loser=4.7,
match=match
),
]
return match