Skip to content

Commit 84288a1

Browse files
committed
(fix) handle booking places for a past competition
1 parent 0962a9e commit 84288a1

File tree

4 files changed

+280
-22
lines changed

4 files changed

+280
-22
lines changed

competitions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
"name": "Fall Classic",
1010
"date": "2020-10-22 13:30:00",
1111
"numberOfPlaces": "13"
12+
},
13+
{
14+
"name": "New",
15+
"date": "2024-10-22 13:30:00",
16+
"numberOfPlaces": "13"
1217
}
1318
]
1419
}

server.py

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from datetime import datetime
23
from flask import Flask, render_template, request, redirect, flash, url_for
34

45
app = Flask(__name__)
@@ -29,9 +30,7 @@ def index():
2930

3031
def get_club_from_email(email):
3132
try:
32-
club = [
33-
club for club in clubs if club["email"] == email
34-
][0]
33+
club = [club for club in clubs if club["email"] == email][0]
3534
return club
3635
except IndexError:
3736
return None
@@ -41,33 +40,48 @@ def get_club_from_email(email):
4140
def showSummary():
4241
club = get_club_from_email(request.form["email"])
4342
if club:
44-
return render_template("welcome.html", club=club,
45-
competitions=competitions)
43+
return render_template(
44+
"welcome.html", club=club, competitions=competitions
45+
)
4646
else:
4747
flash("Sorry, that email wasn't found.")
4848
return redirect(url_for("index"))
4949

5050

51+
def validate_competition_date(competition):
52+
competition_date = datetime.strptime(
53+
competition["date"], "%Y-%m-%d %H:%M:%S"
54+
)
55+
if competition_date < datetime.now():
56+
return "This competition is already over. You cannot book a place."
57+
58+
5159
@app.route("/book/<competition>/<club>")
5260
def book(competition, club):
53-
foundClub = [c for c in clubs if c["name"] == club][0]
54-
foundCompetition = [c for c in competitions if c["name"] == competition][0]
55-
if foundClub and foundCompetition:
56-
return render_template(
57-
"booking.html", club=foundClub, competition=foundCompetition
58-
)
59-
else:
61+
foundClub = get_club_from_name(club)
62+
foundCompetition = get_competition_from_name(competition)
63+
if not foundClub or not foundCompetition:
6064
flash("Something went wrong-please try again")
6165
return render_template(
6266
"welcome.html", club=club, competitions=competitions
6367
)
68+
error_message = validate_competition_date(foundCompetition)
69+
if error_message:
70+
flash(error_message)
71+
return render_template(
72+
"welcome.html", club=foundClub, competitions=competitions
73+
)
74+
return render_template(
75+
"booking.html", club=foundClub, competition=foundCompetition
76+
)
6477

6578

6679
def get_competition_from_name(name):
6780
try:
6881
competition = [
69-
competition for competition in
70-
competitions if competition["name"] == name
82+
competition
83+
for competition in competitions
84+
if competition["name"] == name
7185
][0]
7286
return competition
7387
except IndexError:
@@ -76,9 +90,7 @@ def get_competition_from_name(name):
7690

7791
def get_club_from_name(name):
7892
try:
79-
club = [
80-
club for club in clubs if club["name"] == name
81-
][0]
93+
club = [club for club in clubs if club["name"] == name][0]
8294
return club
8395
except IndexError:
8496
return None
@@ -88,16 +100,19 @@ def check_places(places, club):
88100
if not places or int(places) < 1:
89101
return "Places required must be a positive integer"
90102
if int(places) > 12:
91-
return ("Places required must be a positive integer "
92-
"that does not exceed 12")
103+
return (
104+
"Places required must be a positive integer "
105+
"that does not exceed 12"
106+
)
93107
if int(places) > int(club["points"]):
94108
return "Places required exceed club's total points"
95109

96110

97111
def take_places(places, club, competition):
98112
try:
99-
competition["numberOfPlaces"] = \
113+
competition["numberOfPlaces"] = (
100114
int(competition["numberOfPlaces"]) - places
115+
)
101116
club["points"] = int(club["points"]) - places
102117
return True
103118
except Exception:
@@ -119,8 +134,9 @@ def purchasePlaces():
119134

120135
if take_places(placesRequired, club, competition):
121136
flash("Great-booking complete!")
122-
return render_template("welcome.html", club=club,
123-
competitions=competitions)
137+
return render_template(
138+
"welcome.html", club=club, competitions=competitions
139+
)
124140
else:
125141
flash("Something went wrong-please try again")
126142
return redirect(
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
def test_book_valid_competition(client):
2+
"""
3+
Test booking a valid competition with a valid club
4+
(future competition date).
5+
"""
6+
response = client.get("/book/Competition%201/Club%201")
7+
8+
# Ensure the correct template is rendered (booking.html)
9+
assert response.status_code == 200
10+
assert (
11+
b"Competition 1" in response.data
12+
) # Assuming the booking page has the word "Booking"
13+
14+
15+
def test_book_past_competition(client):
16+
"""
17+
Test booking a competition with a past date.
18+
"""
19+
response = client.get("/book/Competition%202/Club%201")
20+
21+
# Ensure the user is shown a message that the competition is in the past
22+
assert response.status_code == 200
23+
assert (
24+
b"This competition is already over. You cannot book a place."
25+
in response.data
26+
)
27+
28+
29+
def test_book_invalid_competition(client):
30+
"""
31+
Test trying to book with an invalid competition name.
32+
"""
33+
response = client.get("/book/Invalid%20Competition/Club%201")
34+
35+
# Ensure the correct message is shown when competition is invalid
36+
assert response.status_code == 200
37+
assert b"Something went wrong-please try again" in response.data
38+
39+
40+
def test_book_invalid_club(client):
41+
"""
42+
Test trying to book with an invalid club name.
43+
"""
44+
response = client.get("/book/Competition%201/Invalid%20Club")
45+
46+
# Ensure the correct message is shown when club is invalid
47+
assert response.status_code == 200
48+
assert b"Something went wrong-please try again" in response.data
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
from unittest.mock import patch
2+
from datetime import datetime
3+
4+
from server import validate_competition_date
5+
from .utils import mock_competitions_list, mock_clubs_list
6+
7+
mock_competition_past = {
8+
"name": "Competition 2",
9+
"date": "2020-03-27 10:00:00",
10+
"numberOfPlaces": "15",
11+
}
12+
mock_future_competition = {
13+
"name": "Future Competition",
14+
"date": "2024-03-27 10:00:00",
15+
}
16+
17+
18+
# Test for a valid future competition date
19+
@patch("server.datetime")
20+
def test_validate_competition_date_future(mock_datetime):
21+
# Mock datetime.now() to return a date before the competition date
22+
mock_datetime.now.return_value = datetime(2023, 3, 27)
23+
mock_datetime.strptime.side_effect = datetime.strptime
24+
25+
# Call the function with a future competition
26+
result = validate_competition_date(mock_future_competition)
27+
28+
# Assert that no error message is returned
29+
assert result is None
30+
31+
32+
# Test for a past competition date
33+
@patch("server.datetime")
34+
def test_validate_competition_date_past(mock_datetime):
35+
# Mock datetime.now() to return a date after the competition date
36+
mock_datetime.now.return_value = datetime(2023, 3, 27)
37+
mock_datetime.strptime.side_effect = datetime.strptime
38+
39+
# Call the function with a past competition
40+
result = validate_competition_date(mock_competition_past)
41+
42+
# Assert that the correct error message is returned
43+
assert (
44+
result == "This competition is already over. You cannot book a place."
45+
)
46+
47+
48+
# Test for valid club and valid competition (future date)
49+
@patch("server.render_template")
50+
@patch("server.validate_competition_date")
51+
@patch("server.get_club_from_name")
52+
@patch("server.get_competition_from_name")
53+
def test_book_valid_competition(
54+
mock_get_competition,
55+
mock_get_club,
56+
mock_validate_date,
57+
mock_render_template,
58+
client,
59+
):
60+
# Mock valid club and competition
61+
mock_get_competition.return_value = mock_competitions_list[0]
62+
mock_get_club.return_value = mock_clubs_list[0]
63+
mock_validate_date.return_value = None # No validation error
64+
65+
# Simulate GET request to the route
66+
response = client.get("/book/Competition%201/Club%201")
67+
68+
# Assert that the necessary functions are called with the correct parameters
69+
mock_get_competition.assert_called_once_with("Competition 1")
70+
mock_get_club.assert_called_once_with("Club 1")
71+
mock_validate_date.assert_called_once_with(mock_competitions_list[0])
72+
mock_render_template.assert_called_once_with(
73+
"booking.html",
74+
club=mock_clubs_list[0],
75+
competition=mock_competitions_list[0],
76+
)
77+
78+
# Check the response status code
79+
assert response.status_code == 200
80+
81+
82+
# Test for valid club and past competition (competition already over)
83+
@patch("server.competitions", mock_competitions_list)
84+
@patch("server.render_template")
85+
@patch("server.flash")
86+
@patch("server.get_club_from_name")
87+
@patch("server.get_competition_from_name")
88+
@patch("server.validate_competition_date")
89+
def test_book_past_competition(
90+
mock_validate_date,
91+
mock_get_competition,
92+
mock_get_club,
93+
mock_flash,
94+
mock_render_template,
95+
client,
96+
):
97+
# Mock valid club but past competition
98+
mock_get_competition.return_value = mock_competition_past
99+
mock_get_club.return_value = mock_clubs_list[0]
100+
mock_validate_date.return_value = (
101+
"This competition is already over. You cannot book a place."
102+
)
103+
104+
# Simulate GET request to the route
105+
response = client.get("/book/Competition%202/Club%201")
106+
107+
# Assert that the necessary functions are called with the correct parameters
108+
mock_get_competition.assert_called_once_with("Competition 2")
109+
mock_get_club.assert_called_once_with("Club 1")
110+
mock_validate_date.assert_called_once_with(mock_competition_past)
111+
mock_flash.assert_called_once_with(
112+
"This competition is already over. You cannot book a place."
113+
)
114+
mock_render_template.assert_called_once_with(
115+
"welcome.html",
116+
club=mock_clubs_list[0],
117+
competitions=mock_competitions_list,
118+
)
119+
120+
# Check the response status code
121+
assert response.status_code == 200
122+
123+
124+
# Test for missing club or competition
125+
@patch("server.competitions", mock_competitions_list)
126+
@patch("server.render_template")
127+
@patch("server.flash")
128+
@patch("server.get_club_from_name")
129+
@patch("server.get_competition_from_name")
130+
def test_book_missing_club_or_competition(
131+
mock_get_competition,
132+
mock_get_club,
133+
mock_flash,
134+
mock_render_template,
135+
client,
136+
):
137+
# Mock a case where the competition is missing
138+
mock_get_competition.return_value = None # Competition not found
139+
mock_get_club.return_value = mock_clubs_list[0] # Club found
140+
141+
# Simulate GET request to the route
142+
response = client.get("/book/Competition%201/Club%201")
143+
144+
# Assert that the necessary functions are called with the correct parameters
145+
mock_get_competition.assert_called_once_with("Competition 1")
146+
mock_get_club.assert_called_once_with("Club 1")
147+
mock_flash.assert_called_once_with("Something went wrong-please try again")
148+
mock_render_template.assert_called_once_with(
149+
"welcome.html",
150+
club=mock_clubs_list[0]["name"],
151+
competitions=mock_competitions_list,
152+
)
153+
154+
# Check the response status code
155+
assert response.status_code == 200
156+
157+
158+
# Test for invalid club
159+
@patch("server.competitions", mock_competitions_list)
160+
@patch("server.render_template")
161+
@patch("server.flash")
162+
@patch("server.get_club_from_name")
163+
@patch("server.get_competition_from_name")
164+
def test_book_invalid_club(
165+
mock_get_competition,
166+
mock_get_club,
167+
mock_flash,
168+
mock_render_template,
169+
client,
170+
):
171+
# Mock a case where the club is invalid
172+
mock_get_competition.return_value = mock_competitions_list[
173+
0
174+
] # Competition found
175+
mock_get_club.return_value = None # Club not found
176+
177+
# Simulate GET request to the route
178+
response = client.get("/book/Competition%201/Invalid%20Club")
179+
180+
# Assert that the necessary functions are called with the correct parameters
181+
mock_get_competition.assert_called_once_with("Competition 1")
182+
mock_get_club.assert_called_once_with("Invalid Club")
183+
mock_flash.assert_called_once_with("Something went wrong-please try again")
184+
mock_render_template.assert_called_once_with(
185+
"welcome.html", club="Invalid Club", competitions=mock_competitions_list
186+
)
187+
188+
# Check the response status code
189+
assert response.status_code == 200

0 commit comments

Comments
 (0)