Skip to content
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
34 changes: 34 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Run Flask Backend Tests

# Trigger on PRs to master
on:
pull_request:
branches:
- master

jobs:
tests:
runs-on: ubuntu-latest

steps:
# Step 1: Checkout repo
- name: Checkout code
uses: actions/checkout@v4

# Step 2: Set up Python
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"

# Step 3: Install dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r backend/requirements.txt

# Step 4: Run tests
- name: Run pytest
working-directory: backend
run: |
pytest -v
Empty file added backend/__init__.py
Empty file.
14 changes: 10 additions & 4 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from config import Config
from extensions import db, jwt, migrate
from backend.config import Config
from backend.extensions import db, jwt, migrate
from flask import Flask


def create_app():
def create_app(config_object=None):
app = Flask(__name__)

# Default config
app.config.from_object(Config)

# Override config (for tests)
if config_object:
app.config.update(config_object)

db.init_app(app)
jwt.init_app(app)
migrate.init_app(app, db)

from routes.auth_routes import auth_bp
from backend.routes.auth_routes import auth_bp

app.register_blueprint(auth_bp)

Expand Down
2 changes: 1 addition & 1 deletion backend/models/user.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import string
import uuid

from extensions import db
from backend.extensions import db
from werkzeug.security import check_password_hash, generate_password_hash


Expand Down
2 changes: 1 addition & 1 deletion backend/models/user_stats.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from extensions import db
from backend.extensions import db


class UserStats(db.Model):
Expand Down
8 changes: 7 additions & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ black==25.11.0
blinker==1.9.0
click==8.1.8
colorama==0.4.6
exceptiongroup==1.3.1
Flask==3.1.2
Flask-JWT-Extended==4.7.1
Flask-Migrate==4.1.0
Flask-SQLAlchemy==3.1.1
importlib_metadata==8.7.1
iniconfig==2.1.0
itsdangerous==2.2.0
Jinja2==3.1.6
Mako==1.3.10
Expand All @@ -16,12 +18,16 @@ mypy_extensions==1.1.0
packaging==26.0
pathspec==1.0.4
platformdirs==4.4.0
pluggy==1.6.0
psycopg2-binary==2.9.11
Pygments==2.19.2
PyJWT==2.10.1
pytest==8.4.2
pytest-flask==1.3.0
python-dotenv==1.2.1
pytokens==0.4.1
SQLAlchemy==2.0.46
tomli==2.4.0
typing_extensions==4.15.0
Werkzeug==3.1.5
zipp==3.23.0
psycopg2-binary
6 changes: 3 additions & 3 deletions backend/routes/auth_routes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import logging
from extensions import db
from backend.extensions import db
from flask import Blueprint, jsonify, request
from models.user import User
from models.user_stats import UserStats
from backend.models.user import User
from backend.models.user_stats import UserStats
from werkzeug.security import generate_password_hash

# Auth Blueprint
Expand Down
Empty file added backend/tests/__init__.py
Empty file.
32 changes: 32 additions & 0 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import json
import pytest
from backend.app import create_app
from backend.extensions import db


@pytest.fixture(scope="session")
def test_data():
with open("tests/data/users.json") as f:
return json.load(f)


@pytest.fixture
def app():
app = create_app(
{
"TESTING": True,
"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
"SQLALCHEMY_TRACK_MODIFICATIONS": False,
}
)

with app.app_context():
db.create_all()
yield app
db.session.remove()
db.drop_all()


@pytest.fixture
def client(app):
return app.test_client()
17 changes: 17 additions & 0 deletions backend/tests/data/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"valid_user": {
"username": "testuser",
"email": "testuser@example.com",
"password": "StrongP@ssw0rd"
},
"weak_password": {
"username": "weakuser",
"email": "weak@example.com",
"password": "weak"
},
"missing_fields": {
"username": "",
"email": "missing@example.com",
"password": ""
}
}
30 changes: 30 additions & 0 deletions backend/tests/test_auth_register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
def test_register_success(client, test_data):
res = client.post("/register", json=test_data["valid_user"])

assert res.status_code == 201
data = res.get_json()
assert "User registered successfully" in data["msg"]


def test_register_weak_password(client, test_data):
res = client.post("/register", json=test_data["weak_password"])

assert res.status_code == 400
data = res.get_json()
assert "password" in data["error"].lower()


def test_register_missing_fields(client, test_data):
res = client.post("/register", json=test_data["missing_fields"])

assert res.status_code == 400


def test_register_duplicate_username(client, test_data):
client.post("/register", json=test_data["valid_user"])

res = client.post(
"/register", json={**test_data["valid_user"], "email": "another@example.com"}
)

assert res.status_code == 409