Skip to content

Commit 9304c72

Browse files
authored
Merge pull request #1 from br-imen/bug/entering-a-unknown-email-crashes-the-app
(fix) handle unknown email in login
2 parents 81e09c8 + 20736bd commit 9304c72

18 files changed

+301
-34
lines changed

.coverage

52 KB
Binary file not shown.

.coveragerc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[run]
2+
omit =
3+
lib/
4+
bin/
5+
*/__init__.py
6+
tests/*

.flake8

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[flake8]
2+
max-line-length = 80
3+
exclude =
4+
bin/activate.py,
5+
lib,
6+
packages,
7+
migrations,
8+
build,
9+
dist,
10+
*.pyc,
11+
__pycache__,
12+
bin/activate_this.py

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ bin
22
include
33
lib
44
.Python
5-
tests/
65
.envrc
7-
__pycache__
6+
__pycache__
7+
pyvenv.cfg
8+
.DS_Store

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[tool.black]
2+
line-length = 80

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
3+
testpaths = tests

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ itsdangerous==1.1.0
44
Jinja2==2.11.2
55
MarkupSafe==1.1.1
66
Werkzeug==1.0.1
7+
pytest
8+
black
9+
flake8

server.py

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,84 @@
11
import json
2-
from flask import Flask,render_template,request,redirect,flash,url_for
2+
from flask import Flask, render_template, request, redirect, flash, url_for
33

44

55
def loadClubs():
6-
with open('clubs.json') as c:
7-
listOfClubs = json.load(c)['clubs']
8-
return listOfClubs
6+
with open("clubs.json") as c:
7+
listOfClubs = json.load(c)["clubs"]
8+
return listOfClubs
99

1010

1111
def loadCompetitions():
12-
with open('competitions.json') as comps:
13-
listOfCompetitions = json.load(comps)['competitions']
14-
return listOfCompetitions
12+
with open("competitions.json") as comps:
13+
listOfCompetitions = json.load(comps)["competitions"]
14+
return listOfCompetitions
1515

1616

1717
app = Flask(__name__)
18-
app.secret_key = 'something_special'
18+
app.secret_key = "something_special"
1919

2020
competitions = loadCompetitions()
2121
clubs = loadClubs()
2222

23-
@app.route('/')
23+
24+
@app.route("/")
2425
def index():
25-
return render_template('index.html')
26+
return render_template("index.html")
27+
28+
29+
def get_club_from_email(email):
30+
try:
31+
club = [
32+
club for club in clubs if club["email"] == email
33+
][0]
34+
return club
35+
except IndexError:
36+
return None
2637

27-
@app.route('/showSummary',methods=['POST'])
38+
39+
@app.route("/showSummary", methods=["POST"])
2840
def showSummary():
29-
club = [club for club in clubs if club['email'] == request.form['email']][0]
30-
return render_template('welcome.html',club=club,competitions=competitions)
41+
club = get_club_from_email(request.form["email"])
42+
if club:
43+
return render_template("welcome.html", club=club,
44+
competitions=competitions)
45+
else:
46+
flash("Sorry, that email wasn't found.")
47+
return redirect(url_for("index"))
3148

3249

33-
@app.route('/book/<competition>/<club>')
34-
def book(competition,club):
35-
foundClub = [c for c in clubs if c['name'] == club][0]
36-
foundCompetition = [c for c in competitions if c['name'] == competition][0]
50+
@app.route("/book/<competition>/<club>")
51+
def book(competition, club):
52+
foundClub = [c for c in clubs if c["name"] == club][0]
53+
foundCompetition = [c for c in competitions if c["name"] == competition][0]
3754
if foundClub and foundCompetition:
38-
return render_template('booking.html',club=foundClub,competition=foundCompetition)
55+
return render_template(
56+
"booking.html", club=foundClub, competition=foundCompetition
57+
)
3958
else:
4059
flash("Something went wrong-please try again")
41-
return render_template('welcome.html', club=club, competitions=competitions)
60+
return render_template(
61+
"welcome.html", club=club, competitions=competitions
62+
)
4263

4364

44-
@app.route('/purchasePlaces',methods=['POST'])
65+
@app.route("/purchasePlaces", methods=["POST"])
4566
def purchasePlaces():
46-
competition = [c for c in competitions if c['name'] == request.form['competition']][0]
47-
club = [c for c in clubs if c['name'] == request.form['club']][0]
48-
placesRequired = int(request.form['places'])
49-
competition['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired
50-
flash('Great-booking complete!')
51-
return render_template('welcome.html', club=club, competitions=competitions)
67+
competition = [
68+
c for c in competitions if c["name"] == request.form["competition"]
69+
][0]
70+
club = [c for c in clubs if c["name"] == request.form["club"]][0]
71+
placesRequired = int(request.form["places"])
72+
competition["numberOfPlaces"] = (
73+
int(competition["numberOfPlaces"]) - placesRequired
74+
)
75+
flash("Great-booking complete!")
76+
return render_template("welcome.html", club=club, competitions=competitions)
5277

5378

5479
# TODO: Add route for points display
5580

5681

57-
@app.route('/logout')
82+
@app.route("/logout")
5883
def logout():
59-
return redirect(url_for('index'))
84+
return redirect(url_for("index"))

templates/index.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!DOCTYPE html>
12
<html lang="en">
23
<head>
34
<meta charset="UTF-8">
@@ -6,10 +7,20 @@
67
</head>
78
<body>
89
<h1>Welcome to the GUDLFT Registration Portal!</h1>
10+
{% with messages = get_flashed_messages() %}
11+
{% if messages %}
12+
<ul>
13+
{% for message in messages %}
14+
<li>{{message}}</li>
15+
{% endfor %}
16+
</ul>
17+
{% endif %}
18+
{% endwith %}
19+
920
Please enter your secretary email to continue:
1021
<form action="showSummary" method="post">
1122
<label for="email">Email:</label>
12-
<input type="email" name="email" id=""/>
23+
<input type="email" name="email" id="email"/>
1324
<button type="submit">Enter</button>
1425
</form>
1526
</body>

templates/welcome.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
<body>
88
<h2>Welcome, {{club['email']}} </h2><a href="{{url_for('logout')}}">Logout</a>
99

10-
{% with messages = get_flashed_messages()%}
10+
{% with messages = get_flashed_messages() %}
1111
{% if messages %}
1212
<ul>
1313
{% for message in messages %}
1414
<li>{{message}}</li>
1515
{% endfor %}
1616
</ul>
1717
{% endif%}
18+
1819
Points available: {{club['points']}}
1920
<h3>Competitions:</h3>
2021
<ul>
@@ -30,7 +31,6 @@ <h3>Competitions:</h3>
3031
<hr />
3132
{% endfor %}
3233
</ul>
33-
{%endwith%}
34-
34+
{% endwith %}
3535
</body>
3636
</html>

tests/__init__.py

Whitespace-only changes.

tests/integration_tests/__init__.py

Whitespace-only changes.

tests/integration_tests/conftest.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import json
2+
import pytest
3+
from unittest.mock import mock_open, patch
4+
5+
# Mock data for clubs.json and competitions.json
6+
mock_clubs_json = json.dumps(
7+
{
8+
"clubs": [
9+
{"name": "Club 1", "email": "club1@example.com"},
10+
{"name": "Club 2", "email": "club2@example.com"},
11+
]
12+
}
13+
)
14+
15+
mock_competitions_json = json.dumps(
16+
{
17+
"competitions": [
18+
{"name": "Competition 1", "numberOfPlaces": "25"},
19+
{"name": "Competition 2", "numberOfPlaces": "15"},
20+
]
21+
}
22+
)
23+
24+
25+
def mocked_open(file, *args, **kwargs):
26+
"""
27+
Mock open function to return mock data for
28+
clubs.json and competitions.json
29+
"""
30+
if file == "clubs.json":
31+
return mock_open(read_data=mock_clubs_json)()
32+
elif file == "competitions.json":
33+
return mock_open(read_data=mock_competitions_json)()
34+
else:
35+
raise FileNotFoundError(f"File {file} not found")
36+
37+
38+
# Patch open before importing the app to ensure clubs
39+
# and competitions are loaded with mock data
40+
with patch("builtins.open", side_effect=mocked_open):
41+
from server import app # Import app after patching
42+
43+
44+
@pytest.fixture
45+
def client():
46+
app.config["TESTING"] = True
47+
with app.test_client() as client:
48+
yield client
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
# Integration test for valid email
3+
def test_showSummary_valid_email_integration(client):
4+
# Simulate POST request with a valid email
5+
response = client.post("/showSummary", data={"email": "club1@example.com"})
6+
7+
# Check if the welcome page is rendered with the correct club data
8+
assert response.status_code == 200
9+
assert b"Welcome" in response.data
10+
11+
12+
# Integration test for invalid email
13+
def test_showSummary_invalid_email_integration(client):
14+
# Simulate POST request with an invalid email
15+
response = client.post(
16+
"/showSummary", data={"email": "invalid@example.com"}
17+
)
18+
19+
# Validate that the response redirects to the index page
20+
assert response.status_code == 302
21+
response = client.get(response.headers["Location"]) # Follow the redirect
22+
23+
# Check if the flash message appears in the redirected page
24+
assert b"Sorry, that email wasn&#39;t found." in response.data

tests/unit_tests/__init__.py

Whitespace-only changes.

tests/unit_tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import pytest
2+
from server import app
3+
4+
5+
@pytest.fixture
6+
def client():
7+
with app.test_client() as client:
8+
yield client

0 commit comments

Comments
 (0)