Skip to content

Commit 0e3706f

Browse files
authored
Merge pull request #4 from taka-rl/2-add-github-workflow
2 add GitHub workflow
2 parents 8849557 + b0493a6 commit 0e3706f

File tree

19 files changed

+578
-153
lines changed

19 files changed

+578
-153
lines changed

.github/workflows/python-app.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@ jobs:
2626
- name: Install dependencies
2727
run: |
2828
python -m pip install --upgrade pip
29-
pip install flake8 pytest
29+
pip install flake8
3030
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
31+
- name: Set PYTHONPATH
32+
run: echo "PYTHONPATH=$PYTHONPATH:$(pwd)" >> $GITHUB_ENV
33+
- name: Set environment variables
34+
run: echo "SECRET_KEY=${{ secrets.SECRET_KEY }}" >> $GITHUB_ENV
35+
3136
- name: Lint with flake8
3237
run: |
3338
# stop the build if there are Python syntax errors or undefined names

README.md

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,19 @@ This is a Flask web app example where I have been utilizing gained skills and kn
4141
```
4242
6. Run main.py
4343
7. Use one of the accounts written as below to Login
44-
- Super admin user:
45-
46-
password: admin
47-
- user:
48-
email:test@email.com
49-
44+
- Super admin user:
45+
46+
password: admin
47+
- user:
48+
email:test@email.com
49+
5050
8. Register a new user
5151

52-
9. Change user's role on ('/admin-dashboard') route.
53-
54-
52+
9. Change user's role and delete registered users on ('/admin-dashboard') route.
5553

5654
## Design
5755
### Directory structure
56+
├── .github # GitHub actions
5857
├── instance # database file
5958
├── static # static files
6059
│ ├── assets # img files
@@ -64,8 +63,15 @@ This is a Flask web app example where I have been utilizing gained skills and kn
6463
├── templates # route html files
6564
├── tests # tests
6665
│ ├── conftest.py # set up for testing
67-
│ ├── test_auth.py # test for auth routes
68-
│ └── other routes # will be committed later
66+
│ ├── parameters.py # parameters for test
67+
│ ├── test_about.py # test for about route
68+
│ ├── test_admin.py # test for admin route
69+
│ ├── test_auth.py # test for auth route
70+
│ ├── test_blog.py # test for blog route
71+
│ ├── test_collection.py # test for collection route
72+
│ ├── test_contact.py # test for contact route
73+
│ ├── test_error.py # test for error route
74+
│ └── test_useful_info.py # test for useful_info route
6975
├── flask_app
7076
│ ├── __init__.py # Initialize Flask app and extensions
7177
│ ├── forms.py # Forms
@@ -126,6 +132,7 @@ User:
126132
## Current development
127133
- Testing
128134
- Documentation
135+
- Add github actions, which is python-app.yml to execute pytest
129136

130137
## Future development:
131138
- Improve the web page design
@@ -135,5 +142,71 @@ User:
135142

136143

137144
## Testing
138-
The section will be updated as it is under development.
139-
pytest-flask is used for testing.
145+
pytest-flask is used for testing. All the test files are stored in the tests folder.
146+
Testing is connect to GitHub actions and tests are executed when commit or pull request happen.
147+
GitHub actions is still under development because of some errors.
148+
149+
### How to run testing on your local environment
150+
1. run this command in the terminal: ```python -m pytest```
151+
If you would like to see more details on the tests: ```python -m pytest -v```
152+
153+
Here is the executed result of ```python -m pytest``` .
154+
```
155+
PS C:\folder path\flask_webapp> python -m pytest
156+
========================================================================================================= test session starts =========================================================================================================
157+
platform win32 -- Python 3.10.11, pytest-8.3.3, pluggy-1.5.0
158+
rootdir: C:\folder path\flask_webapp
159+
plugins: anyio-4.6.0, flask-1.3.0
160+
collected 24 items
161+
162+
tests\test_about.py . [ 4%]
163+
tests\test_admin.py .... [ 20%]
164+
tests\test_auth.py ...... [ 45%]
165+
tests\test_blog.py .... [ 62%]
166+
tests\test_collection.py .... [ 79%]
167+
tests\test_contact.py . [ 83%]
168+
tests\test_useful_info.py . [100%]
169+
''''
170+
========================================================================================================== warnings summary ===========================================================================================================
171+
172+
=================================================================================================== 24 passed, 2 warnings in 6.34s ====================================================================================================
173+
```
174+
175+
176+
Here is the executed result of ```python -m pytest -v``` .
177+
```
178+
========================================================================================================= test session starts =========================================================================================================
179+
platform win32 -- Python 3.10.11, pytest-8.3.3, pluggy-1.5.0 -- C:-----\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\python.exe
180+
cachedir: .pytest_cache
181+
rootdir: C:\folder path\flask_webapp
182+
plugins: anyio-4.6.0, flask-1.3.0
183+
collected 24 items
184+
185+
tests/test_about.py::test_about_access PASSED [ 4%]
186+
tests/test_admin.py::test_check_super_admin_exist PASSED [ 8%]
187+
tests/test_admin.py::test_admin_dashboard_access PASSED [ 12%]
188+
tests/test_admin.py::test_change_user_role PASSED [ 16%]
189+
tests/test_admin.py::test_delete_user PASSED [ 20%]
190+
tests/test_auth.py::test_register_page PASSED [ 25%]
191+
tests/test_auth.py::test_register_form PASSED [ 29%]
192+
tests/test_auth.py::test_duplicated_email_register PASSED [ 33%]
193+
tests/test_auth.py::test_login_page PASSED [ 37%]
194+
tests/test_auth.py::test_login_form PASSED [ 41%]
195+
tests/test_auth.py::test_logout PASSED [ 45%]
196+
tests/test_blog.py::test_create_new_post PASSED [ 50%]
197+
tests/test_blog.py::test_edit_post PASSED [ 54%]
198+
tests/test_blog.py::test_delete_post PASSED [ 58%]
199+
tests/test_blog.py::test_add_comment PASSED [ 62%]
200+
tests/test_collection.py::test_access_collection_page PASSED [ 66%]
201+
tests/test_collection.py::test_add_place PASSED [ 70%]
202+
tests/test_collection.py::test_edit_place PASSED [ 75%]
203+
tests/test_collection.py::test_delete_place PASSED [ 79%]
204+
tests/test_contact.py::test_access_contact_page PASSED [ 83%]
205+
tests/test_error.py::test_500_page PASSED [ 87%]
206+
tests/test_error.py::test_404_page PASSED [ 91%]
207+
tests/test_useful_info.py::test_access_useful_info_page PASSED [100%]
208+
209+
========================================================================================================== warnings summary ===========================================================================================================
210+
211+
=================================================================================================== 24 passed, 2 warnings in 6.46s ====================================================================================================
212+
```

flask_app/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class Place(db.Model):
7474
location: Mapped[str] = mapped_column(String(50), nullable=False)
7575
open_time: Mapped[str] = mapped_column(String(10), nullable=True)
7676
close_time: Mapped[str] = mapped_column(String(10), nullable=True)
77-
pricing: Mapped[float] = mapped_column(String(15), nullable=True)
77+
pricing: Mapped[str] = mapped_column(String(15), nullable=True)
7878
rating: Mapped[float] = mapped_column(Float, nullable=True)
7979
category: Mapped[str] = mapped_column(String(15), nullable=True)
8080
location_url: Mapped[str] = mapped_column(String(250), nullable=True)

flask_app/routes/blog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def edit_post(post_id):
8888

8989

9090
# Use a decorator so only an admin user can delete a post
91-
@blog_bp.route("/delete-post/<int:post_id>")
91+
@blog_bp.route("/delete-post/<int:post_id>", methods=['POST'])
9292
@admin_only
9393
def delete_post(post_id):
9494
post_to_delete = db.get_or_404(BlogPost, post_id)

flask_app/routes/collection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def edit_place(place_id):
7272
return render_template('add-place.html', place=place, form=edit_form, is_edit=True)
7373

7474

75-
@collection_bp.route('/delete-place/<int:place_id>')
75+
@collection_bp.route('/delete-place/<int:place_id>', methods=['POST'])
7676
@admin_only
7777
def delete_place(place_id):
7878
place_to_delete = db.get_or_404(Place, place_id)

flask_app/routes/errors.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
from flask import Blueprint, render_template
2-
from flask_app.utils import with_translations
3-
4-
5-
errors_bp = Blueprint('errors', __name__)
6-
7-
8-
# 404 Not Found
9-
@errors_bp.app_errorhandler(404)
10-
@with_translations
11-
def not_found(error):
12-
return render_template('404.html'), 404
13-
14-
15-
# 403 Forbidden
16-
@errors_bp.app_errorhandler(403)
17-
@with_translations
18-
def forbidden(error):
19-
return render_template('403.html'), 403
20-
21-
22-
# 500 Internal Server Error
23-
@errors_bp.app_errorhandler(500)
24-
@with_translations
25-
def server_error(error):
26-
return render_template('500.html'), 500
27-
28-
29-
# temporal route for testing 500 errors
30-
@errors_bp.route('/trigger-500')
31-
@with_translations
32-
def trigger_500():
33-
return render_template('500.html'), 500
1+
from flask import Blueprint, render_template
2+
from flask_app.utils import with_translations
3+
4+
5+
errors_bp = Blueprint('errors', __name__)
6+
7+
8+
# 404 Not Found
9+
@errors_bp.app_errorhandler(404)
10+
@with_translations
11+
def not_found(error):
12+
return render_template('404.html'), 404
13+
14+
15+
# 403 Forbidden
16+
@errors_bp.app_errorhandler(403)
17+
@with_translations
18+
def forbidden(error):
19+
return render_template('403.html'), 403
20+
21+
22+
# 500 Internal Server Error
23+
@errors_bp.app_errorhandler(500)
24+
@with_translations
25+
def server_error(error):
26+
return render_template('500.html'), 500
27+
28+
29+
# temporal route for testing 500 errors
30+
@errors_bp.route('/trigger-500')
31+
@with_translations
32+
def trigger_500():
33+
return render_template('500.html'), 500

flask_app/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@ def get_weather_info(location: str) -> dict[str, Any]:
173173
response = requests.get(url)
174174
if response.status_code == 200:
175175
data = response.json() # Parse the JSON response
176-
print(data)
177176

178177
weather_data = {'temperature': data['main']['temp'],
179178
'max_temperature': data['main']['temp_max'],

templates/login.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ <h1>Log In</h1>
3232

3333
<div class="col-lg-8 col-md-10 mx-auto">
3434
<!-- render login form here-->
35+
{{ form.hidden_tag() }}
3536
{{ render_form(form, novalidate=True, button_map={"submit": "primary"}) }}
3637
</div>
3738
</div>

templates/register.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ <h1>Register</h1>
2323
<div class="row">
2424
<div class="col-lg-8 col-md-10 mx-auto">
2525
<!-- render your registration form here-->
26+
{{ form.hidden_tag() }}
2627
{{ render_form(form, novalidate=True, button_map={"submit": "primary"}) }}
2728
</div>
2829
</div>

tests/conftest.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
from flask_app import create_app
33
from flask_app.models import db, User
44
from werkzeug.security import generate_password_hash
5-
6-
7-
SUPER_ADMIN_EMAIL = '[email protected]'
8-
SUPER_ADMIN_PASSWORD = 'admin'
5+
from tests.parameters import SUPER_ADMIN_EMAIL, SUPER_ADMIN_PASSWORD, SUPER_ADMIN_NAME
96

107

118
@pytest.fixture
@@ -18,8 +15,8 @@ def app():
1815
# Create a super admin user
1916
super_admin = User(email=SUPER_ADMIN_EMAIL,
2017
password=generate_password_hash(SUPER_ADMIN_PASSWORD),
21-
name='Super Admin',
22-
role='super_admin'
18+
name=SUPER_ADMIN_NAME,
19+
role='Super Admin'
2320
)
2421
db.session.add(super_admin)
2522
db.session.commit()
@@ -43,8 +40,12 @@ def runner(app):
4340
@pytest.fixture
4441
def super_admin_client(client):
4542
"""Log in as the super admin and return the authenticated client."""
46-
client.post('/login', data={
43+
login_response = client.post('/login', data={
4744
'email': SUPER_ADMIN_EMAIL,
4845
'password': SUPER_ADMIN_PASSWORD
49-
})
46+
}, follow_redirects=True)
47+
48+
# Make sure if login was successful
49+
assert login_response.status_code == 200
50+
assert b"admin-dashboard" in login_response.data
5051
return client

0 commit comments

Comments
 (0)