Files
gurix f1d8bae6a1 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
2025-12-27 22:36:05 +01:00

168 lines
5.9 KiB
Python

"""
Pytest configuration and shared fixtures for the Flask job application tests.
"""
import os
import tempfile
import shutil
from io import BytesIO
from datetime import datetime
import pytest
import yaml
from app import create_app
from flask_mail import Mail
@pytest.fixture
def app():
"""Create and configure a test Flask application instance."""
# Create a temporary directory for test applications
temp_dir = tempfile.mkdtemp()
# Create test Flask app using factory
flask_app = create_app()
# Configure app for testing
flask_app.config['TESTING'] = True
flask_app.config['SECRET_KEY'] = 'test-secret-key'
flask_app.config['APPLICATIONS_FOLDER'] = temp_dir
flask_app.config['WTF_CSRF_ENABLED'] = False # Disable CSRF for testing
flask_app.config['MAIL_SUPPRESS_SEND'] = True # Don't actually send emails
flask_app.config['RATE_LIMIT_SECONDS'] = 0 # Disable rate limiting for testing
yield flask_app
# Cleanup: remove temporary directory
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
@pytest.fixture
def client(app):
"""Create a test client for making requests to the application."""
return app.test_client()
@pytest.fixture
def runner(app):
"""Create a test CLI runner."""
return app.test_cli_runner()
@pytest.fixture
def temp_applications_dir(app):
"""Get the temporary applications directory for the current test."""
return app.config['APPLICATIONS_FOLDER']
@pytest.fixture
def sample_application_data():
"""Sample application data for testing."""
return {
'session_id': 'test-session-123',
'email': 'test@example.com',
'job_name': 'Test Position',
'current_page': 2,
'created_at': datetime.now().isoformat(),
'updated_at': datetime.now().isoformat(),
'personal_info': {
'name': 'Müller',
'firstname': 'Anna',
'address': 'Hauptstrasse 123',
'zip_code': '8001',
'city': 'Zürich',
'phone': '+41 79 123 45 67',
'birth_year': '1990',
'civil_status': 'ledig'
},
'motivation_answers': {
'current_job': 'Software Entwicklerin',
'motivation': 'Ich möchte in einem innovativen Team arbeiten',
'qualifications': 'Python, Flask, 5 Jahre Erfahrung',
'salary': '80000 CHF'
},
'uploaded_files': []
}
@pytest.fixture
def sample_pdf_file():
"""Create a sample PDF file for testing uploads."""
# Create a minimal PDF file
pdf_content = b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Count 1/Kids[3 0 R]>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 612 792]/Parent 2 0 R>>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000056 00000 n\n0000000115 00000 n\ntrailer<</Size 4/Root 1 0 R>>\nstartxref\n190\n%%EOF'
return BytesIO(pdf_content)
@pytest.fixture
def sample_jpg_file():
"""Create a sample JPG file for testing uploads."""
# Create a minimal JPEG file (1x1 pixel red image)
jpg_content = (
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00'
b'\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c'
b'\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c'
b'\x1c $.\' ",#\x1c\x1c(7),01444\x1f\'9=82<.342\xff\xc0\x00\x0b\x08\x00'
b'\x01\x00\x01\x01\x01\x11\x00\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01'
b'\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05'
b'\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04'
b'\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A'
b'\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82'
b'\t\n\x16\x17\x18\x19\x1a%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxy'
b'z\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a'
b'\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9'
b'\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8'
b'\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5'
b'\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x08\x01\x01\x00\x00?\x00\xfb\xfe\x87'
b'\xff\xd9'
)
return BytesIO(jpg_content)
@pytest.fixture
def large_file():
"""Create a file larger than the 4MB limit for testing."""
# Create a 5MB file
content = b'x' * (5 * 1024 * 1024)
return BytesIO(content)
@pytest.fixture
def mock_mail(app, monkeypatch):
"""Mock Flask-Mail to prevent actual email sending."""
class MockMail:
def __init__(self):
self.sent_messages = []
def send(self, message):
self.sent_messages.append(message)
mock = MockMail()
# Patch the mail instance in the app package (used by email_service)
import app.email_service
monkeypatch.setattr(app.email_service, 'mail', mock)
return mock
@pytest.fixture
def create_test_application(app, temp_applications_dir):
"""Factory fixture to create test application data."""
def _create(session_id='test-123', **kwargs):
"""Create a test application with the given session ID and data."""
from app.models import save_application_data
data = {
'session_id': session_id,
'email': kwargs.get('email', 'test@example.com'),
'job_name': kwargs.get('job_name', 'Test Job'),
'current_page': kwargs.get('current_page', 2),
'created_at': datetime.now().isoformat(),
'updated_at': datetime.now().isoformat(),
'personal_info': kwargs.get('personal_info', {}),
'motivation_answers': kwargs.get('motivation_answers', {}),
'uploaded_files': kwargs.get('uploaded_files', [])
}
save_application_data(session_id, data)
return data
return _create