Added a service that can get all current tournaments list

This commit is contained in:
Stanislas Jouffroy 2024-03-23 11:56:31 +01:00
parent e6023e0687
commit 3d0bd47079
26 changed files with 4305 additions and 204 deletions

View file

@ -3,7 +3,7 @@ import logging
import config
from gestion_sports_services import GestionSportsServices
from models import Action, BookingFilter, Club, Court, User
from models import Action, BookingFilter, Club, Court, Tournament, User
LOGGER = logging.getLogger(__name__)
@ -51,7 +51,18 @@ async def cancel_booking_id(club: Club, user: User, booking_id: int) -> None:
await service.cancel_booking_id(user, club, booking_id)
def main() -> tuple[Court, User] | None:
async def get_tournaments(club: Club, user: User) -> list[Tournament]:
"""
Cancel a booking that matches the booking id
:param club: the club in which the booking was made
:param user: the user who made the booking
"""
service = GestionSportsServices()
return await service.get_all_tournaments(user, club)
def main() -> tuple[Court, User] | list[Tournament] | None:
"""
Main function used to book a court
@ -80,3 +91,8 @@ def main() -> tuple[Court, User] | None:
club = config.get_club()
booking_filter = config.get_booking_filter()
asyncio.run(cancel_booking(club, user, booking_filter))
elif action == Action.TOURNAMENTS:
user = config.get_user()
club = config.get_club()
return asyncio.run(get_tournaments(club, user))

View file

@ -1,42 +0,0 @@
import logging
from aiohttp import ClientSession
from connectors import Connector
from models import BookingFilter, Club, User
LOGGER = logging.getLogger(__name__)
class BookingService:
def __init__(self, club: Club, connector: Connector):
LOGGER.info("Initializing booking service at for club", club.name)
self.club = club
self.connector = connector
self.session: ClientSession | None = None
async def __aenter__(self):
self.session = ClientSession()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.session.close()
async def book(self, user: User, booking_filter: BookingFilter) -> int | None:
"""
Book a court matching the booking filters for a user.
The steps to perform a booking are to go to the landing page, to log in, wait
and for the time when booking is open and then actually book the court
:param user: the user that wants to book a court
:param booking_filter: the booking criteria
:return: the court number if the booking is successful, None otherwise
"""
if self.connector is None:
LOGGER.error("No connection to Gestion Sports is available")
return None
if user is None or booking_filter is None:
LOGGER.error("Not enough information available to book a court")
return None
self.connector.book(user, booking_filter)

View file

@ -143,4 +143,4 @@ def get_action() -> Action:
Get the action to perform from an environment variable
:return: the action to perform
"""
return Action(os.environ.get("ACTION"))
return Action(os.environ.get("ACTION").lower())

View file

@ -49,6 +49,11 @@ class GestionSportsConnector:
self.club.booking_platform.urls.get(name).path,
)
def _get_url_parameter(self, name: str) -> str:
self._check_url_path_exists(name)
return self.club.booking_platform.urls.get(name).parameter
def _get_payload_template(self, name: str) -> Path:
"""
Get the path to the template file for the service with the given name
@ -178,6 +183,18 @@ class GestionSportsConnector:
"""
return self._get_payload_template("cancellation")
@property
def tournaments_sessions_url(self) -> str:
return self._get_url_path("tournament-sessions")
@property
def tournaments_sessions_template(self) -> Path:
return self._get_payload_template("tournament-sessions")
@property
def tournaments_list_url(self) -> str:
return self._get_url_path("tournaments-list")
@property
def available_sports(self) -> dict[str, Sport]:
"""
@ -209,7 +226,7 @@ class GestionSportsConnector:
payload = PayloadBuilder.build(self.login_template, user=user, club=self.club)
async with session.post(
self.login_url, data=payload, headers=POST_HEADERS
self.login_url, data=payload, headers=POST_HEADERS, allow_redirects=False
) as response:
resp_text = await response.text()
LOGGER.debug("Connexion request response:\n%s", resp_text)
@ -421,3 +438,23 @@ class GestionSportsConnector:
for booking in bookings:
if booking.matches(booking_filter):
return await self.cancel_booking_id(session, booking.id)
async def send_tournaments_sessions_request(
self, session: ClientSession
) -> ClientResponse:
payload = self.tournaments_sessions_template.read_text()
async with session.post(
self.tournaments_sessions_url, data=payload, headers=POST_HEADERS
) as response:
LOGGER.debug("tournament sessions: \n%s", await response.text())
return response
async def send_tournaments_request(
self, session: ClientSession, tournement_session_id: str
) -> ClientResponse:
final_url = self.tournaments_list_url + tournement_session_id
LOGGER.debug("Getting tournaments list at %s", final_url)
async with session.get(final_url) as response:
LOGGER.debug("tournaments: %s\n", await response.text())
return response

View file

@ -1,10 +1,12 @@
import json
import logging
import time
import pendulum
from aiohttp import ClientSession
from connectors import GestionSportsConnector
from models import BookingFilter, BookingOpening, Club, Court, User
from bs4 import BeautifulSoup
from gestion_sport_connector import GestionSportsConnector
from models import BookingFilter, BookingOpening, Club, Court, Tournament, User
from pendulum import DateTime
LOGGER = logging.getLogger(__name__)
@ -125,3 +127,62 @@ class GestionSportsServices:
booking_minute = opening_time.minute
return booking_date.at(booking_hour, booking_minute)
@staticmethod
async def get_all_tournaments(user: User, club: Club) -> list[Tournament]:
connector = GestionSportsConnector(club)
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
session_html = await connector.send_tournaments_sessions_request(session)
tournaments_id = GestionSportsServices.retrieve_tournament_session(
await session_html.text()
)
tournaments = await connector.send_tournaments_request(
session, tournaments_id
)
return GestionSportsServices.retrieve_tournaments(await tournaments.text())
@staticmethod
def retrieve_tournament_session(sessions: str) -> str:
session_object = json.loads(sessions).get("Inscription tournois:school-outline")
return list(session_object.keys())[0]
@staticmethod
def retrieve_tournaments(html: str) -> list[Tournament]:
soup = BeautifulSoup(html, "html.parser")
tournaments = []
cards = soup.find_all("div", {"class": "card-body"})
for card in cards:
title = card.find("h5")
price = title.find("span").get_text().strip()
name = title.get_text().strip().removesuffix(price).strip()
elements = card.find("div", {"class": "row"}).find_all("li")
date = elements[0].get_text().strip()
start_time, end_time = (
elements[2].get_text().strip().replace("h", ":").split(" - ")
)
start_datetime = pendulum.from_format(
f"{date} {start_time}", "DD/MM/YYYY HH:mm"
)
end_datetime = pendulum.from_format(
f"{date} {end_time}", "DD/MM/YYYY HH:mm"
)
gender = elements[1].get_text().strip()
places_left = (
card.find("span", {"class": "nb_place_libre"}).get_text().strip()
)
tournament = Tournament(
name=name,
price=price,
start_date=start_datetime,
end_date=end_datetime,
gender=gender,
places_left=places_left,
)
tournaments.append(tournament)
return tournaments

View file

@ -70,6 +70,7 @@ class Sport(BaseModel):
class Url(BaseModel):
name: str
path: str
parameter: Optional[str] = Field(default=None)
payload_template: Optional[str] = Field(default=None, alias="payloadTemplate")
@ -210,3 +211,13 @@ class Booking(BaseModel):
class Action(Enum):
BOOK = "book"
CANCEL = "cancel"
TOURNAMENTS = "tournaments"
class Tournament(BaseModel):
name: str
price: str
start_date: DateTime
end_date: DateTime
gender: str
places_left: str | int

View file

@ -1,12 +1,16 @@
{
"Connection": "keep-alive",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"DNT": "1",
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
"Connection": "keep-alive",
"DNT": "1",
"Origin": "https://toulousepadelclub.gestion-sports.com",
"Pragma": "no-cache",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin"
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0",
"X-Requested-With": "XMLHttpRequest"
}

View file

@ -0,0 +1 @@
ajax=loadSessionForSpecDay&date=all

View file

@ -17,3 +17,8 @@ platforms:
- name: cancellation
path: /membre/mesresas.html
payloadTemplate: gestion-sports/booking-cancellation-payload.txt
- name: tournament-sessions
path: /membre/index.php
payloadTemplate: gestion-sports/tournament-sessions-payload.txt
- name: tournaments-list
path: /membre/events/event.html?event=

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
{
"Inscription tournois:school-outline": {
"1174": {
"id": 1174,
"sport": "padel",
"clubName": "toulouse padel club",
"nom": "Tournoi",
"dateNextSession": "25\/03\/2024",
"dateDebut": "01\/08\/2022",
"dateFin": "01\/10\/2024",
"logo": "TCP_Ligue_Arcanthe2-01-min.png",
"nbSession": 14,
"icon": "school-outline",
"playerCanSeeThisEvent": null,
"type": "tournoi",
"isJp": false,
"isCiup": false,
"sqlDate": "2024-03-25 13:30:00"
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@ from pathlib import Path
import config
import pendulum
import pytest
from connectors import GestionSportsConnector
from gestion_sport_connector import GestionSportsConnector
from models import BookingFilter, Club, User
TEST_FOLDER = Path(__file__).parent.parent
@ -36,11 +36,17 @@ def booking_filter() -> BookingFilter:
@pytest.fixture
def booking_success_response() -> dict:
booking_success_file = RESPONSES_FOLDER / "booking_success.json"
booking_success_file = RESPONSES_FOLDER / "booking-success.json"
return json.loads(booking_success_file.read_text(encoding="utf-8"))
@pytest.fixture
def booking_failure_response() -> dict:
booking_failure_file = RESPONSES_FOLDER / "booking_failure.json"
return json.loads(booking_failure_file.read_text(encoding="utf-8"))
file = RESPONSES_FOLDER / "booking-failure.json"
return json.loads(file.read_text(encoding="utf-8"))
@pytest.fixture
def tournament_sessions_json() -> str:
file = RESPONSES_FOLDER / "tournament-sessions.json"
return file.read_text(encoding="utf-8")

View file

@ -1,36 +1,15 @@
import json
import os
from pathlib import Path
from unittest.mock import patch
import aiohttp
import pendulum
import pytest
from connectors import GestionSportsConnector
from models import BookingFilter, Club
from pendulum import DateTime
from aiohttp import ClientSession
from gestion_sport_connector import GestionSportsConnector
from yarl import URL
def retrieve_booking_datetime(
a_booking_filter: BookingFilter, a_club: Club
) -> DateTime:
"""
Utility to retrieve the booking datetime from the booking filter and the club
:param a_booking_filter: the booking filter that contains the date to book
:param a_club: the club which has the number of days before the date and the booking time
"""
booking_opening = a_club.booking_platform.booking_opening
opening_time = pendulum.parse(booking_opening.opening_time)
booking_hour = opening_time.hour
booking_minute = opening_time.minute
date_to_book = a_booking_filter.date
return date_to_book.subtract(days=booking_opening.days_before).at(
booking_hour, booking_minute
)
@patch.dict(
os.environ,
{"CLUB_ID": "tpc"},
@ -57,6 +36,10 @@ def test_urls(connector):
connector.booking_cancellation_url
== "https://toulousepadelclub.gestion-sports.com/membre/mesresas.html"
)
assert (
connector.tournaments_sessions_url
== "https://toulousepadelclub.gestion-sports.com/membre/index.php"
)
@patch.dict(
@ -76,11 +59,15 @@ def test_urls_payload_templates(connector):
connector.booking_cancel_template
== resources_folder / "booking-cancellation-payload.txt"
)
assert (
connector.tournaments_sessions_template
== resources_folder / "tournament-sessions-payload.txt"
)
@pytest.mark.asyncio
async def test_landing_page(connector):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
response = await connector.land(session)
assert response.status == 200
@ -93,7 +80,7 @@ async def test_landing_page(connector):
@pytest.mark.asyncio
async def test_login(connector, user):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
response = await connector.login(session, user)
@ -128,7 +115,7 @@ def test_get_booked_court(
@pytest.mark.asyncio
async def test_book_one_court(connector, user, booking_filter):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
@ -156,28 +143,9 @@ def test_build_booking_datetime(connector, booking_filter):
assert opening_datetime.minute == 0
@patch("pendulum.now")
def test_wait_until_booking_time(mock_now, connector, booking_filter, club):
booking_datetime = retrieve_booking_datetime(booking_filter, club)
seconds = [
booking_datetime.subtract(seconds=3),
booking_datetime.subtract(seconds=2),
booking_datetime.subtract(seconds=1),
booking_datetime,
booking_datetime.add(microseconds=1),
booking_datetime.add(microseconds=2),
]
mock_now.side_effect = seconds
connector.wait_until_booking_time(booking_filter)
assert pendulum.now() == booking_datetime.add(microseconds=1)
@pytest.mark.asyncio
async def test_get_hash(connector, user):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
@ -197,7 +165,7 @@ def test_get_hash_input():
@pytest.mark.asyncio
async def test_get_bookings(connector, user):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
@ -210,7 +178,7 @@ async def test_get_bookings(connector, user):
@pytest.mark.asyncio
async def test_get_ongoing_bookings(connector, user):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
@ -218,20 +186,15 @@ async def test_get_ongoing_bookings(connector, user):
print(bookings)
@pytest.mark.asyncio
async def test_has_user_ongoing_bookings(connector, user):
assert await connector.has_user_ongoing_booking(user)
@pytest.mark.asyncio
async def test_cancel_booking_id(connector, user):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
ongoing_bookings = await connector.get_ongoing_bookings(session)
booking_id = ongoing_bookings[0].id
response = await connector.cancel_booking_id(user, booking_id)
response = await connector.cancel_booking_id(session, 666)
assert len(await connector.get_ongoing_bookings(session)) == 0
@ -245,7 +208,33 @@ def test_find_court(connector):
@pytest.mark.asyncio
async def test_cancel_booking(connector, user, booking_filter):
async with aiohttp.ClientSession() as session:
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
await connector.cancel_booking(session, booking_filter)
@pytest.mark.asyncio
async def test_tournament_sessions(connector, user):
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
response = await connector.send_tournaments_sessions_request(session)
assert response.status == 200
all_sessions = json.loads(await response.text())
sessions = all_sessions.get("Inscription tournois:school-outline")
assert len(sessions) == 1
@pytest.mark.asyncio
async def test_send_tournaments_request(connector, user):
async with ClientSession() as session:
await connector.land(session)
await connector.login(session, user)
tournament_session_id = "1174"
response = await connector.send_tournaments_request(
session, tournament_session_id
)
assert "<span class='nb_place_libre'>Complet</span>" in await response.text()

View file

@ -18,3 +18,9 @@ async def test_user_has_available_slots(club, user):
@pytest.mark.asyncio
async def test_cancel_booking(club, user, booking_filter):
await GestionSportsServices.cancel_booking(user, club, booking_filter)
@pytest.mark.asyncio
async def test_get_all_tournaments(user, club):
tournaments = await GestionSportsServices.get_all_tournaments(user, club)
assert len(tournaments) == 14

View file

@ -3,7 +3,7 @@ from pathlib import Path
import pendulum
import pytest
from connectors import GestionSportsConnector
from gestion_sport_connector import GestionSportsConnector
from gestion_sports_services import GestionSportsServices
from models import (
BookingFilter,
@ -43,7 +43,7 @@ def court14() -> Court:
@pytest.fixture
def sport1(court11, court12, court13, court14) -> Sport:
def sport1(court11: Court, court12: Court, court13: Court, court14: Court) -> Sport:
return Sport(
name="Sport1",
id=8,
@ -75,7 +75,7 @@ def court24() -> Court:
@pytest.fixture
def sport2(court21, court22, court23, court24) -> Sport:
def sport2(court21: Court, court22: Court, court23: Court, court24: Court) -> Sport:
return Sport(
name="Sport 2",
id=10,
@ -130,9 +130,26 @@ def cancellation_url() -> Url:
)
@pytest.fixture
def tournament_sessions_url() -> Url:
return Url(
name="tournament-sessions",
path="/tournaments_sessions.php",
payloadTemplate="gestion-sports/tournament-sessions-payload.txt",
)
@pytest.fixture
def tournaments_list_url() -> Url:
return Url(
name="tournaments-list",
path="/tournaments_list.html?event=",
)
@pytest.fixture
def booking_opening() -> BookingOpening:
return BookingOpening(daysBefore=10, time="03:27")
return BookingOpening(daysBefore=7, time="00:00")
@pytest.fixture
@ -142,15 +159,17 @@ def total_bookings() -> TotalBookings:
@pytest.fixture
def booking_platform(
booking_opening,
total_bookings,
sport1,
sport2,
landing_url,
login_url,
booking_url,
user_bookings_url,
cancellation_url,
booking_opening: BookingOpening,
total_bookings: TotalBookings,
sport1: Sport,
sport2: Sport,
landing_url: str,
login_url: str,
booking_url: str,
user_bookings_url: str,
cancellation_url: str,
tournament_sessions_url: str,
tournaments_list_url: str,
) -> BookingPlatform:
return BookingPlatform(
id="gestion-sports",
@ -166,12 +185,14 @@ def booking_platform(
"booking": booking_url,
"user-bookings": user_bookings_url,
"cancellation": cancellation_url,
"tournament-sessions": tournament_sessions_url,
"tournaments-list": tournaments_list_url,
},
)
@pytest.fixture
def club(booking_platform) -> Club:
def club(booking_platform: BookingPlatform) -> Club:
return Club(
id="super_club",
name="Super Club",
@ -204,42 +225,42 @@ def booking_filter() -> BookingFilter:
@pytest.fixture
def landing_response() -> str:
landing_response_file = RESPONSES_FOLDER / "landing_response.html"
return landing_response_file.read_text(encoding="utf-8")
file = RESPONSES_FOLDER / "landing-response.html"
return file.read_text(encoding="utf-8")
@pytest.fixture
def login_success_response() -> dict:
login_success_file = RESPONSES_FOLDER / "login_success.json"
login_success_file = RESPONSES_FOLDER / "login-success.json"
return json.loads(login_success_file.read_text(encoding="utf-8"))
@pytest.fixture
def login_failure_response() -> dict:
login_failure_file = RESPONSES_FOLDER / "login_failure.json"
return json.loads(login_failure_file.read_text(encoding="utf-8"))
file = RESPONSES_FOLDER / "login-failure.json"
return json.loads(file.read_text(encoding="utf-8"))
@pytest.fixture
def booking_success_response() -> dict:
booking_success_file = RESPONSES_FOLDER / "booking_success.json"
return json.loads(booking_success_file.read_text(encoding="utf-8"))
file = RESPONSES_FOLDER / "booking-success.json"
return json.loads(file.read_text(encoding="utf-8"))
@pytest.fixture
def booking_failure_response() -> dict:
booking_failure_file = RESPONSES_FOLDER / "booking_failure.json"
return json.loads(booking_failure_file.read_text(encoding="utf-8"))
file = RESPONSES_FOLDER / "booking-failure.json"
return json.loads(file.read_text(encoding="utf-8"))
@pytest.fixture
def booked_courts_response(
court11,
court12,
court13,
court14,
booking_success_response,
booking_failure_response,
court11: int,
court12: int,
court13: int,
court14: int,
booking_success_response: dict,
booking_failure_response: dict,
) -> list[tuple[int, dict]]:
court1_resp = court11.id, booking_failure_response
court2_resp = court12.id, booking_failure_response
@ -250,10 +271,10 @@ def booked_courts_response(
@pytest.fixture
def booking_success_from_start(
landing_response,
login_success_response,
booking_success_response,
booking_failure_response,
landing_response: str,
login_success_response: str,
booking_success_response: str,
booking_failure_response: str,
):
return [
landing_response,
@ -267,10 +288,10 @@ def booking_success_from_start(
@pytest.fixture
def booking_failure_from_start(
landing_response,
login_success_response,
booking_success_response,
booking_failure_response,
landing_response: str,
login_success_response: str,
booking_success_response: str,
booking_failure_response: str,
):
return [
landing_response,
@ -284,22 +305,22 @@ def booking_failure_from_start(
@pytest.fixture
def user_bookings_get_response() -> str:
user_bookings_file = RESPONSES_FOLDER / "user_bookings_get.html"
return user_bookings_file.read_text(encoding="utf-8")
file = RESPONSES_FOLDER / "user-bookings-get.html"
return file.read_text(encoding="utf-8")
@pytest.fixture
def user_bookings_list() -> list:
user_bookings_file = RESPONSES_FOLDER / "user_bookings_post.json"
return json.loads(user_bookings_file.read_text(encoding="utf-8"))
def user_bookings_list() -> str:
file = RESPONSES_FOLDER / "user-bookings-post.json"
return json.loads(file.read_text(encoding="utf-8"))
@pytest.fixture
def user_has_ongoing_bookings_from_start(
landing_response,
login_success_response,
user_bookings_get_response,
user_bookings_list,
landing_response: str,
login_success_response: str,
user_bookings_get_response: str,
user_bookings_list: str,
) -> list:
return [
landing_response,
@ -316,10 +337,10 @@ def user_bookings_empty_list() -> list:
@pytest.fixture
def user_has_no_ongoing_bookings_from_start(
landing_response,
login_success_response,
user_bookings_get_response,
user_bookings_empty_list,
landing_response: str,
login_success_response: str,
user_bookings_get_response: str,
user_bookings_empty_list: str,
) -> list:
return [
landing_response,
@ -331,16 +352,16 @@ def user_has_no_ongoing_bookings_from_start(
@pytest.fixture
def cancellation_response() -> list:
cancellation_response_file = RESPONSES_FOLDER / "cancellation_response.json"
return json.loads(cancellation_response_file.read_text(encoding="utf-8"))
file = RESPONSES_FOLDER / "cancellation-response.json"
return json.loads(file.read_text(encoding="utf-8"))
@pytest.fixture
def cancellation_by_id_from_start(
landing_response,
login_success_response,
user_bookings_get_response,
cancellation_response,
landing_response: str,
login_success_response: str,
user_bookings_get_response: str,
cancellation_response: str,
):
return [
landing_response,
@ -352,11 +373,11 @@ def cancellation_by_id_from_start(
@pytest.fixture
def cancellation_success_from_start(
landing_response,
login_success_response,
user_bookings_get_response,
user_bookings_list,
cancellation_response,
landing_response: str,
login_success_response: str,
user_bookings_get_response: str,
user_bookings_list: str,
cancellation_response: str,
):
return [
landing_response,
@ -377,3 +398,30 @@ def cancellation_success_booking_filter() -> BookingFilter:
@pytest.fixture
def service() -> GestionSportsServices:
return GestionSportsServices()
@pytest.fixture
def tournament_sessions_json() -> str:
file = RESPONSES_FOLDER / "tournament-sessions.json"
return file.read_text(encoding="utf-8")
@pytest.fixture
def tournaments_html() -> str:
file = RESPONSES_FOLDER / "tournaments.html"
return file.read_text(encoding="utf-8")
@pytest.fixture
def full_tournaments_responses(
landing_response: str,
login_success_response: str,
tournament_sessions_json: str,
tournaments_html: str,
) -> list[str]:
return [
landing_response,
login_success_response,
tournament_sessions_json,
tournaments_html,
]

View file

@ -1,31 +1,24 @@
import json
from pathlib import Path
from unittest.mock import patch
import pytest
from aiohttp import ClientSession
from connectors import GestionSportsConnector
from gestion_sport_connector import GestionSportsConnector
from tests.unit_tests import responses
def test_urls(connector, club):
base_url = club.booking_platform.url
relative_urls = club.booking_platform.urls
base_url = "https://ptf1.com"
relative_landing_url = relative_urls.get("landing-page").path
assert connector.landing_url == f"{base_url}/{relative_landing_url}"
relative_login_url = relative_urls.get("login").path
assert connector.login_url == f"{base_url}/{relative_login_url}"
relative_booking_url = relative_urls.get("booking").path
assert connector.booking_url == f"{base_url}/{relative_booking_url}"
relative_user_bookings_url = relative_urls.get("user-bookings").path
assert connector.user_bookings_url == f"{base_url}/{relative_user_bookings_url}"
relative_cancel_url = relative_urls.get("cancellation").path
assert connector.booking_cancellation_url == f"{base_url}/{relative_cancel_url}"
assert connector.landing_url == f"{base_url}/landing.html"
assert connector.login_url == f"{base_url}/login.html"
assert connector.booking_url == f"{base_url}/booking.html"
assert connector.user_bookings_url == f"{base_url}/user_bookings.html"
assert connector.booking_cancellation_url == f"{base_url}/cancel.html"
assert connector.tournaments_sessions_url == f"{base_url}/tournaments_sessions.php"
assert connector.tournaments_list_url == f"{base_url}/tournaments_list.html?event="
@patch("config.get_resources_folder")
@ -34,19 +27,27 @@ def test_urls_payload_templates(mock_resources, club):
mock_resources.return_value = path_to_resources
connector = GestionSportsConnector(club)
relative_urls = club.booking_platform.urls
login_payload = relative_urls.get("login").payload_template
assert connector.login_template == path_to_resources / login_payload
booking_payload = relative_urls.get("booking").payload_template
assert connector.booking_template == path_to_resources / booking_payload
user_bookings_payload = relative_urls.get("user-bookings").payload_template
assert connector.user_bookings_template == path_to_resources / user_bookings_payload
cancel_payload = relative_urls.get("cancellation").payload_template
assert connector.booking_cancel_template == path_to_resources / cancel_payload
assert (
connector.login_template
== path_to_resources / "gestion-sports/login-payload.txt"
)
assert (
connector.booking_template
== path_to_resources / "gestion-sports/booking-payload.txt"
)
assert (
connector.user_bookings_template
== path_to_resources / "gestion-sports/user-bookings-payload.txt"
)
assert (
connector.booking_cancel_template
== path_to_resources / "gestion-sports/booking-cancellation-payload.txt"
)
assert (
connector.tournaments_sessions_template
== path_to_resources / "gestion-sports/tournament-sessions-payload.txt"
)
@pytest.mark.asyncio
@ -143,3 +144,35 @@ async def test_cancel_booking_success(
)
assert await response.json() == cancellation_success_from_start[4]
@pytest.mark.asyncio
async def test_tournament_sessions(
aioresponses, connector, user, tournament_sessions_json
):
responses.set_tournaments_sessions_response(
aioresponses, connector, tournament_sessions_json
)
async with ClientSession() as session:
response = await connector.send_tournaments_sessions_request(session)
assert response.status == 200
all_sessions = json.loads(await response.text())
sessions = all_sessions.get("Inscription tournois:school-outline")
assert len(sessions) == 1
@pytest.mark.asyncio
async def test_send_tournaments_request(
aioresponses, connector, user, tournaments_html
):
tournament_session_id = "255"
responses.set_tournaments_list_response(
aioresponses, connector, tournament_session_id, tournaments_html
)
async with ClientSession() as session:
response = await connector.send_tournaments_request(
session, tournament_session_id
)
assert "<span class='nb_place_libre'>Complet</span>" in await response.text()

View file

@ -1,5 +1,9 @@
from unittest.mock import patch
import pendulum
import pytest
from gestion_sports_services import GestionSportsServices
from models import BookingFilter, BookingOpening
from tests.unit_tests import responses
@ -108,3 +112,65 @@ async def test_cancel_booking_id(
)
await gs_services.cancel_booking_id(user, club, 65464)
@patch("pendulum.now")
def test_wait_until_booking_time(mock_now, club, user):
booking_filter = BookingFilter(
sport_name="Sport1", date=pendulum.parse("2024-03-21T13:30:00+01:00")
)
booking_datetime = pendulum.parse("2024-03-14T00:00:00+01:00")
seconds = [
booking_datetime.subtract(seconds=3),
booking_datetime.subtract(seconds=2),
booking_datetime.subtract(seconds=1),
booking_datetime,
booking_datetime.add(microseconds=1),
booking_datetime.add(microseconds=2),
]
mock_now.side_effect = seconds
booking_opening = club.booking_platform.booking_opening
GestionSportsServices.wait_until_booking_time(booking_filter, booking_opening)
assert pendulum.now() == booking_datetime.add(microseconds=1)
def test_build_booking_time():
booking_filter = BookingFilter(
sport_name="Sport1", date=pendulum.parse("2024-03-21T13:30:00+01:00")
)
booking_opening = BookingOpening(daysBefore=7, time="00:00")
booking_time = GestionSportsServices.build_booking_datetime(
booking_filter, booking_opening
)
assert booking_time == pendulum.parse("2024-03-13T23:00:00Z")
def test_retrieve_tournament_id(tournament_sessions_json):
session_id = GestionSportsServices.retrieve_tournament_session(
tournament_sessions_json
)
assert session_id == "1174"
def test_retrieve_tournaments(tournaments_html):
tournaments = GestionSportsServices.retrieve_tournaments(tournaments_html)
assert len(tournaments) == 14
@pytest.mark.asyncio
async def test_get_all_tournaments(
aioresponses, user, connector, club, full_tournaments_responses
):
responses.set_full_tournaments_requests(
aioresponses, connector, full_tournaments_responses, 1174
)
tournaments = await GestionSportsServices.get_all_tournaments(user, club)
assert len(tournaments) == 14