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