refactor: split monolithic app.py into modular Flask application with factory pattern
- Created app/ package with 6 modules: - __init__.py: Application factory (create_app function) - routes.py: All route handlers (473 lines) - models.py: Data persistence layer (41 lines) - validators.py: Input validation functions (48 lines) - email_service.py: Email sending functions (85 lines) - utils.py: Utility functions (30 lines) - Implemented Flask application factory pattern - Created run.py as minimal entry point - Updated all test imports to use new module structure - Fixed template/static folder paths for package structure - All 102 tests passing - Improved maintainability with clear separation of concerns - Follows Flask best practices for scalable applications
This commit is contained in:
+62
-52
@@ -2,7 +2,7 @@
|
||||
Tests for validation functions in the Flask job application system.
|
||||
"""
|
||||
import pytest
|
||||
from app import validate_email, validate_phone, validate_year, validate_zip, allowed_file
|
||||
from app.validators import validate_email, validate_phone, validate_year, validate_zip, allowed_file
|
||||
|
||||
|
||||
class TestEmailValidation:
|
||||
@@ -63,86 +63,96 @@ class TestPhoneValidation:
|
||||
class TestYearValidation:
|
||||
"""Tests for birth year validation."""
|
||||
|
||||
def test_valid_years(self):
|
||||
def test_valid_years(self, app):
|
||||
"""Test that valid birth years are accepted."""
|
||||
assert validate_year('1940') is True # Minimum
|
||||
assert validate_year('1990') is True # Middle
|
||||
assert validate_year('2010') is True # Maximum
|
||||
with app.app_context():
|
||||
assert validate_year('1940') is True # Minimum
|
||||
assert validate_year('1990') is True # Middle
|
||||
assert validate_year('2010') is True # Maximum
|
||||
|
||||
def test_invalid_years_out_of_range(self):
|
||||
def test_invalid_years_out_of_range(self, app):
|
||||
"""Test that years outside the valid range are rejected."""
|
||||
assert validate_year('1939') is False # Too old
|
||||
assert validate_year('2011') is False # Too young
|
||||
assert validate_year('1900') is False
|
||||
assert validate_year('2025') is False
|
||||
with app.app_context():
|
||||
assert validate_year('1939') is False # Too old
|
||||
assert validate_year('2011') is False # Too young
|
||||
assert validate_year('1900') is False
|
||||
assert validate_year('2025') is False
|
||||
|
||||
def test_invalid_year_format(self):
|
||||
def test_invalid_year_format(self, app):
|
||||
"""Test that invalid year formats are rejected."""
|
||||
assert validate_year('') is False
|
||||
assert validate_year('90') is False # 2 digits
|
||||
assert validate_year('990') is False # 3 digits
|
||||
assert validate_year('19900') is False # 5 digits
|
||||
assert validate_year('abcd') is False # Non-numeric
|
||||
assert validate_year('199a') is False # Mixed
|
||||
with app.app_context():
|
||||
assert validate_year('') is False
|
||||
assert validate_year('90') is False # 2 digits
|
||||
assert validate_year('990') is False # 3 digits
|
||||
assert validate_year('19900') is False # 5 digits
|
||||
assert validate_year('abcd') is False # Non-numeric
|
||||
assert validate_year('199a') is False # Mixed
|
||||
|
||||
|
||||
class TestZipValidation:
|
||||
"""Tests for ZIP code validation."""
|
||||
|
||||
def test_valid_zip_codes(self):
|
||||
def test_valid_zip_codes(self, app):
|
||||
"""Test that valid ZIP codes are accepted."""
|
||||
assert validate_zip('8001') is True
|
||||
assert validate_zip('12345') is True
|
||||
assert validate_zip('1') is True # Single digit
|
||||
assert validate_zip('1234567890') is True # Max 10 digits
|
||||
with app.app_context():
|
||||
assert validate_zip('8001') is True
|
||||
assert validate_zip('12345') is True
|
||||
assert validate_zip('1') is True # Single digit
|
||||
assert validate_zip('1234567890') is True # Max 10 digits
|
||||
|
||||
def test_invalid_zip_codes(self):
|
||||
def test_invalid_zip_codes(self, app):
|
||||
"""Test that invalid ZIP codes are rejected."""
|
||||
assert validate_zip('') is False
|
||||
assert validate_zip('12345678901') is False # 11 digits (too long)
|
||||
assert validate_zip('abc') is False # Non-numeric
|
||||
assert validate_zip('123a5') is False # Mixed
|
||||
with app.app_context():
|
||||
assert validate_zip('') is False
|
||||
assert validate_zip('12345678901') is False # 11 digits (too long)
|
||||
assert validate_zip('abc') is False # Non-numeric
|
||||
assert validate_zip('123a5') is False # Mixed
|
||||
|
||||
def test_zip_edge_cases(self):
|
||||
def test_zip_edge_cases(self, app):
|
||||
"""Test edge cases for ZIP validation."""
|
||||
assert validate_zip('0') is True # Zero is valid
|
||||
assert validate_zip('00000') is True # Leading zeros
|
||||
with app.app_context():
|
||||
assert validate_zip('0') is True # Zero is valid
|
||||
assert validate_zip('00000') is True # Leading zeros
|
||||
|
||||
|
||||
class TestFileExtensionValidation:
|
||||
"""Tests for file extension validation."""
|
||||
|
||||
def test_valid_file_extensions(self):
|
||||
def test_valid_file_extensions(self, app):
|
||||
"""Test that files with valid extensions are accepted."""
|
||||
assert allowed_file('document.pdf') is True
|
||||
assert allowed_file('resume.doc') is True
|
||||
assert allowed_file('cover.docx') is True
|
||||
assert allowed_file('notes.txt') is True
|
||||
assert allowed_file('photo.jpg') is True
|
||||
assert allowed_file('image.jpeg') is True
|
||||
assert allowed_file('picture.png') is True
|
||||
with app.app_context():
|
||||
assert allowed_file('document.pdf') is True
|
||||
assert allowed_file('resume.doc') is True
|
||||
assert allowed_file('cover.docx') is True
|
||||
assert allowed_file('notes.txt') is True
|
||||
assert allowed_file('photo.jpg') is True
|
||||
assert allowed_file('image.jpeg') is True
|
||||
assert allowed_file('picture.png') is True
|
||||
|
||||
def test_invalid_file_extensions(self):
|
||||
def test_invalid_file_extensions(self, app):
|
||||
"""Test that files with invalid extensions are rejected."""
|
||||
assert allowed_file('script.exe') is False
|
||||
assert allowed_file('data.zip') is False
|
||||
assert allowed_file('code.py') is False
|
||||
assert allowed_file('file.unknown') is False
|
||||
with app.app_context():
|
||||
assert allowed_file('script.exe') is False
|
||||
assert allowed_file('data.zip') is False
|
||||
assert allowed_file('code.py') is False
|
||||
assert allowed_file('file.unknown') is False
|
||||
|
||||
def test_file_without_extension(self):
|
||||
"""Test that files without extensions are rejected."""
|
||||
assert allowed_file('noextension') is False
|
||||
assert allowed_file('') is False
|
||||
|
||||
def test_case_insensitive_extensions(self):
|
||||
def test_case_insensitive_extensions(self, app):
|
||||
"""Test that file extension validation is case-insensitive."""
|
||||
assert allowed_file('document.PDF') is True
|
||||
assert allowed_file('document.Pdf') is True
|
||||
assert allowed_file('image.JPG') is True
|
||||
assert allowed_file('image.JPEG') is True
|
||||
with app.app_context():
|
||||
assert allowed_file('document.PDF') is True
|
||||
assert allowed_file('document.Pdf') is True
|
||||
assert allowed_file('image.JPG') is True
|
||||
assert allowed_file('image.JPEG') is True
|
||||
|
||||
def test_multiple_dots_in_filename(self):
|
||||
def test_multiple_dots_in_filename(self, app):
|
||||
"""Test files with multiple dots in the filename."""
|
||||
assert allowed_file('my.document.pdf') is True
|
||||
assert allowed_file('file.name.with.dots.jpg') is True
|
||||
assert allowed_file('test.tar.gz') is False # .gz not allowed
|
||||
with app.app_context():
|
||||
assert allowed_file('my.document.pdf') is True
|
||||
assert allowed_file('file.name.with.dots.jpg') is True
|
||||
assert allowed_file('test.tar.gz') is False # .gz not allowed
|
||||
|
||||
Reference in New Issue
Block a user