Skip to content

(fix) handle booking places for a past competition #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions competitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"name": "Fall Classic",
"date": "2020-10-22 13:30:00",
"numberOfPlaces": "13"
},
{
"name": "New",
"date": "2024-10-22 13:30:00",
"numberOfPlaces": "13"
}
]
}
60 changes: 38 additions & 22 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from datetime import datetime
from flask import Flask, render_template, request, redirect, flash, url_for

app = Flask(__name__)
Expand Down Expand Up @@ -29,9 +30,7 @@ def index():

def get_club_from_email(email):
try:
club = [
club for club in clubs if club["email"] == email
][0]
club = [club for club in clubs if club["email"] == email][0]
return club
except IndexError:
return None
Expand All @@ -41,33 +40,48 @@ def get_club_from_email(email):
def showSummary():
club = get_club_from_email(request.form["email"])
if club:
return render_template("welcome.html", club=club,
competitions=competitions)
return render_template(
"welcome.html", club=club, competitions=competitions
)
else:
flash("Sorry, that email wasn't found.")
return redirect(url_for("index"))


def validate_competition_date(competition):
competition_date = datetime.strptime(
competition["date"], "%Y-%m-%d %H:%M:%S"
)
if competition_date < datetime.now():
return "This competition is already over. You cannot book a place."


@app.route("/book/<competition>/<club>")
def book(competition, club):
foundClub = [c for c in clubs if c["name"] == club][0]
foundCompetition = [c for c in competitions if c["name"] == competition][0]
if foundClub and foundCompetition:
return render_template(
"booking.html", club=foundClub, competition=foundCompetition
)
else:
foundClub = get_club_from_name(club)
foundCompetition = get_competition_from_name(competition)
if not foundClub or not foundCompetition:
flash("Something went wrong-please try again")
return render_template(
"welcome.html", club=club, competitions=competitions
)
error_message = validate_competition_date(foundCompetition)
if error_message:
flash(error_message)
return render_template(
"welcome.html", club=foundClub, competitions=competitions
)
return render_template(
"booking.html", club=foundClub, competition=foundCompetition
)


def get_competition_from_name(name):
try:
competition = [
competition for competition in
competitions if competition["name"] == name
competition
for competition in competitions
if competition["name"] == name
][0]
return competition
except IndexError:
Expand All @@ -76,9 +90,7 @@ def get_competition_from_name(name):

def get_club_from_name(name):
try:
club = [
club for club in clubs if club["name"] == name
][0]
club = [club for club in clubs if club["name"] == name][0]
return club
except IndexError:
return None
Expand All @@ -88,16 +100,19 @@ def check_places(places, club):
if not places or int(places) < 1:
return "Places required must be a positive integer"
if int(places) > 12:
return ("Places required must be a positive integer "
"that does not exceed 12")
return (
"Places required must be a positive integer "
"that does not exceed 12"
)
if int(places) > int(club["points"]):
return "Places required exceed club's total points"


def take_places(places, club, competition):
try:
competition["numberOfPlaces"] = \
competition["numberOfPlaces"] = (
int(competition["numberOfPlaces"]) - places
)
club["points"] = int(club["points"]) - places
return True
except Exception:
Expand All @@ -119,8 +134,9 @@ def purchasePlaces():

if take_places(placesRequired, club, competition):
flash("Great-booking complete!")
return render_template("welcome.html", club=club,
competitions=competitions)
return render_template(
"welcome.html", club=club, competitions=competitions
)
else:
flash("Something went wrong-please try again")
return redirect(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
def test_book_valid_competition(client):
"""
Test booking a valid competition with a valid club
(future competition date).
"""
response = client.get("/book/Competition%201/Club%201")

# Ensure the correct template is rendered (booking.html)
assert response.status_code == 200
assert (
b"Competition 1" in response.data
) # Assuming the booking page has the word "Booking"


def test_book_past_competition(client):
"""
Test booking a competition with a past date.
"""
response = client.get("/book/Competition%202/Club%201")

# Ensure the user is shown a message that the competition is in the past
assert response.status_code == 200
assert (
b"This competition is already over. You cannot book a place."
in response.data
)


def test_book_invalid_competition(client):
"""
Test trying to book with an invalid competition name.
"""
response = client.get("/book/Invalid%20Competition/Club%201")

# Ensure the correct message is shown when competition is invalid
assert response.status_code == 200
assert b"Something went wrong-please try again" in response.data


def test_book_invalid_club(client):
"""
Test trying to book with an invalid club name.
"""
response = client.get("/book/Competition%201/Invalid%20Club")

# Ensure the correct message is shown when club is invalid
assert response.status_code == 200
assert b"Something went wrong-please try again" in response.data
189 changes: 189 additions & 0 deletions tests/unit_tests/test_booking-places-in-past-competitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
from unittest.mock import patch
from datetime import datetime

from server import validate_competition_date
from .utils import mock_competitions_list, mock_clubs_list

mock_competition_past = {
"name": "Competition 2",
"date": "2020-03-27 10:00:00",
"numberOfPlaces": "15",
}
mock_future_competition = {
"name": "Future Competition",
"date": "2024-03-27 10:00:00",
}


# Test for a valid future competition date
@patch("server.datetime")
def test_validate_competition_date_future(mock_datetime):
# Mock datetime.now() to return a date before the competition date
mock_datetime.now.return_value = datetime(2023, 3, 27)
mock_datetime.strptime.side_effect = datetime.strptime

# Call the function with a future competition
result = validate_competition_date(mock_future_competition)

# Assert that no error message is returned
assert result is None


# Test for a past competition date
@patch("server.datetime")
def test_validate_competition_date_past(mock_datetime):
# Mock datetime.now() to return a date after the competition date
mock_datetime.now.return_value = datetime(2023, 3, 27)
mock_datetime.strptime.side_effect = datetime.strptime

# Call the function with a past competition
result = validate_competition_date(mock_competition_past)

# Assert that the correct error message is returned
assert (
result == "This competition is already over. You cannot book a place."
)


# Test for valid club and valid competition (future date)
@patch("server.render_template")
@patch("server.validate_competition_date")
@patch("server.get_club_from_name")
@patch("server.get_competition_from_name")
def test_book_valid_competition(
mock_get_competition,
mock_get_club,
mock_validate_date,
mock_render_template,
client,
):
# Mock valid club and competition
mock_get_competition.return_value = mock_competitions_list[0]
mock_get_club.return_value = mock_clubs_list[0]
mock_validate_date.return_value = None # No validation error

# Simulate GET request to the route
response = client.get("/book/Competition%201/Club%201")

# Assert that the necessary functions are called with the correct parameters
mock_get_competition.assert_called_once_with("Competition 1")
mock_get_club.assert_called_once_with("Club 1")
mock_validate_date.assert_called_once_with(mock_competitions_list[0])
mock_render_template.assert_called_once_with(
"booking.html",
club=mock_clubs_list[0],
competition=mock_competitions_list[0],
)

# Check the response status code
assert response.status_code == 200


# Test for valid club and past competition (competition already over)
@patch("server.competitions", mock_competitions_list)
@patch("server.render_template")
@patch("server.flash")
@patch("server.get_club_from_name")
@patch("server.get_competition_from_name")
@patch("server.validate_competition_date")
def test_book_past_competition(
mock_validate_date,
mock_get_competition,
mock_get_club,
mock_flash,
mock_render_template,
client,
):
# Mock valid club but past competition
mock_get_competition.return_value = mock_competition_past
mock_get_club.return_value = mock_clubs_list[0]
mock_validate_date.return_value = (
"This competition is already over. You cannot book a place."
)

# Simulate GET request to the route
response = client.get("/book/Competition%202/Club%201")

# Assert that the necessary functions are called with the correct parameters
mock_get_competition.assert_called_once_with("Competition 2")
mock_get_club.assert_called_once_with("Club 1")
mock_validate_date.assert_called_once_with(mock_competition_past)
mock_flash.assert_called_once_with(
"This competition is already over. You cannot book a place."
)
mock_render_template.assert_called_once_with(
"welcome.html",
club=mock_clubs_list[0],
competitions=mock_competitions_list,
)

# Check the response status code
assert response.status_code == 200


# Test for missing club or competition
@patch("server.competitions", mock_competitions_list)
@patch("server.render_template")
@patch("server.flash")
@patch("server.get_club_from_name")
@patch("server.get_competition_from_name")
def test_book_missing_club_or_competition(
mock_get_competition,
mock_get_club,
mock_flash,
mock_render_template,
client,
):
# Mock a case where the competition is missing
mock_get_competition.return_value = None # Competition not found
mock_get_club.return_value = mock_clubs_list[0] # Club found

# Simulate GET request to the route
response = client.get("/book/Competition%201/Club%201")

# Assert that the necessary functions are called with the correct parameters
mock_get_competition.assert_called_once_with("Competition 1")
mock_get_club.assert_called_once_with("Club 1")
mock_flash.assert_called_once_with("Something went wrong-please try again")
mock_render_template.assert_called_once_with(
"welcome.html",
club=mock_clubs_list[0]["name"],
competitions=mock_competitions_list,
)

# Check the response status code
assert response.status_code == 200


# Test for invalid club
@patch("server.competitions", mock_competitions_list)
@patch("server.render_template")
@patch("server.flash")
@patch("server.get_club_from_name")
@patch("server.get_competition_from_name")
def test_book_invalid_club(
mock_get_competition,
mock_get_club,
mock_flash,
mock_render_template,
client,
):
# Mock a case where the club is invalid
mock_get_competition.return_value = mock_competitions_list[
0
] # Competition found
mock_get_club.return_value = None # Club not found

# Simulate GET request to the route
response = client.get("/book/Competition%201/Invalid%20Club")

# Assert that the necessary functions are called with the correct parameters
mock_get_competition.assert_called_once_with("Competition 1")
mock_get_club.assert_called_once_with("Invalid Club")
mock_flash.assert_called_once_with("Something went wrong-please try again")
mock_render_template.assert_called_once_with(
"welcome.html", club="Invalid Club", competitions=mock_competitions_list
)

# Check the response status code
assert response.status_code == 200