Flask Full-Stack

Testing, Security Hardening and Deployment

45 min

Testing with pytest

# tests/conftest.py
import pytest
from app import create_app
from app.extensions import db

@pytest.fixture
def app():
    app = create_app("app.config.TestingConfig")
    with app.app_context():
        db.create_all()
        yield app
        db.drop_all()

@pytest.fixture
def client(app):
    return app.test_client()
def test_home(client):
    assert client.get("/").status_code == 200

def test_cannot_edit_someone_elses_project(client, make_user, make_project, login):
    owner, other = make_user("a@x.com"), make_user("b@x.com")
    project = make_project(owner=owner)
    login(client, other)
    assert client.post(f"/projects/{project.id}/edit", data={"title": "x"}).status_code == 404

Test: every page loads (a smoke test over all GET routes), permissions for each role, ownership, form validation and payment webhooks.

Security hardening checklist

  • [ ] SECRET_KEY long, random and from the environment
  • [ ] CSRF on all forms; POST for every state change
  • [ ] Rate limits on login, sign-up, contact and API endpoints (Redis storage when running several workers)
  • [ ] Secure cookie flags: Secure, HttpOnly, SameSite=Lax
  • [ ] Security headers: HSTS, X-Content-Type-Options, Referrer-Policy, a Content Security Policy, frame protection
  • [ ] Ownership checks on every object; admin pages return 404 to non-staff
  • [ ] Uploads validated and stored outside static/
  • [ ] Dependencies audited (pip-audit) and updated regularly
  • [ ] Two-factor authentication for admin accounts
@app.after_request
def security_headers(resp):
    resp.headers["X-Content-Type-Options"] = "nosniff"
    resp.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
    resp.headers["X-Frame-Options"] = "DENY"
    resp.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    return resp

Deploying

# wsgi.py
from app import create_app
app = create_app("app.config.ProductionConfig")

Start command: gunicorn wsgi:app -c gunicorn.conf.py; build: install requirements and run flask db upgrade. See Web App Deployment and Deploy to Render for the full walk-through.

Final project

Build a "client portal" app: registration and login with rate limits; projects owned by users with tags; file uploads per project; an admin area (404 for non-admins) listing all users; Stripe Checkout for a paid plan with access granted by webhook; full pytest suite including permissions; deployed to a PaaS with PostgreSQL, HTTPS and monitoring. This is exactly the kind of app small businesses hire developers to build.

Check yourself

  • What should a smoke test cover?
  • Why do rate limits need Redis with several workers?
  • Which config class does production use, and what does it change?

Tu progreso se guarda solo en este navegador.

Enviar para revisión (10 puntos)