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:
+101
-92
@@ -5,7 +5,7 @@ import pytest
|
||||
import os
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from app import (
|
||||
from app.models import (
|
||||
save_application_data,
|
||||
load_application_data,
|
||||
get_application_path,
|
||||
@@ -19,84 +19,89 @@ class TestYAMLStorage:
|
||||
|
||||
def test_save_application_data(self, app, sample_application_data):
|
||||
"""Test saving application data to YAML file."""
|
||||
session_id = sample_application_data['session_id']
|
||||
save_application_data(session_id, sample_application_data)
|
||||
with app.app_context():
|
||||
session_id = sample_application_data['session_id']
|
||||
save_application_data(session_id, sample_application_data)
|
||||
|
||||
# Verify file was created
|
||||
data_file = get_data_file_path(session_id)
|
||||
assert os.path.exists(data_file)
|
||||
# Verify file was created
|
||||
data_file = get_data_file_path(session_id)
|
||||
assert os.path.exists(data_file)
|
||||
|
||||
# Verify content
|
||||
with open(data_file, 'r', encoding='utf-8') as f:
|
||||
loaded_data = yaml.safe_load(f)
|
||||
# Verify content
|
||||
with open(data_file, 'r', encoding='utf-8') as f:
|
||||
loaded_data = yaml.safe_load(f)
|
||||
|
||||
assert loaded_data['session_id'] == session_id
|
||||
assert loaded_data['email'] == sample_application_data['email']
|
||||
assert loaded_data['job_name'] == sample_application_data['job_name']
|
||||
assert loaded_data['session_id'] == session_id
|
||||
assert loaded_data['email'] == sample_application_data['email']
|
||||
assert loaded_data['job_name'] == sample_application_data['job_name']
|
||||
|
||||
def test_load_application_data(self, app, sample_application_data):
|
||||
"""Test loading application data from YAML file."""
|
||||
session_id = sample_application_data['session_id']
|
||||
with app.app_context():
|
||||
session_id = sample_application_data['session_id']
|
||||
|
||||
# Save data first
|
||||
save_application_data(session_id, sample_application_data)
|
||||
# Save data first
|
||||
save_application_data(session_id, sample_application_data)
|
||||
|
||||
# Load and verify
|
||||
loaded_data = load_application_data(session_id)
|
||||
assert loaded_data is not None
|
||||
assert loaded_data['session_id'] == session_id
|
||||
assert loaded_data['email'] == sample_application_data['email']
|
||||
assert loaded_data['personal_info']['name'] == 'Müller'
|
||||
# Load and verify
|
||||
loaded_data = load_application_data(session_id)
|
||||
assert loaded_data is not None
|
||||
assert loaded_data['session_id'] == session_id
|
||||
assert loaded_data['email'] == sample_application_data['email']
|
||||
assert loaded_data['personal_info']['name'] == 'Müller'
|
||||
|
||||
def test_load_nonexistent_application(self, app):
|
||||
"""Test loading data for non-existent application."""
|
||||
loaded_data = load_application_data('nonexistent-session')
|
||||
assert loaded_data is None
|
||||
with app.app_context():
|
||||
loaded_data = load_application_data('nonexistent-session')
|
||||
assert loaded_data is None
|
||||
|
||||
def test_unicode_characters_in_yaml(self, app):
|
||||
"""Test that German umlauts and special characters are preserved."""
|
||||
session_id = 'test-unicode-123'
|
||||
data = {
|
||||
'session_id': session_id,
|
||||
'email': 'test@example.com',
|
||||
'job_name': 'Test Job',
|
||||
'current_page': 2,
|
||||
'personal_info': {
|
||||
'name': 'Müller',
|
||||
'firstname': 'Jürgen',
|
||||
'city': 'München',
|
||||
'notes': 'Grüße aus Zürich'
|
||||
with app.app_context():
|
||||
session_id = 'test-unicode-123'
|
||||
data = {
|
||||
'session_id': session_id,
|
||||
'email': 'test@example.com',
|
||||
'job_name': 'Test Job',
|
||||
'current_page': 2,
|
||||
'personal_info': {
|
||||
'name': 'Müller',
|
||||
'firstname': 'Jürgen',
|
||||
'city': 'München',
|
||||
'notes': 'Grüße aus Zürich'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save_application_data(session_id, data)
|
||||
loaded_data = load_application_data(session_id)
|
||||
save_application_data(session_id, data)
|
||||
loaded_data = load_application_data(session_id)
|
||||
|
||||
assert loaded_data['personal_info']['name'] == 'Müller'
|
||||
assert loaded_data['personal_info']['firstname'] == 'Jürgen'
|
||||
assert loaded_data['personal_info']['city'] == 'München'
|
||||
assert loaded_data['personal_info']['notes'] == 'Grüße aus Zürich'
|
||||
assert loaded_data['personal_info']['name'] == 'Müller'
|
||||
assert loaded_data['personal_info']['firstname'] == 'Jürgen'
|
||||
assert loaded_data['personal_info']['city'] == 'München'
|
||||
assert loaded_data['personal_info']['notes'] == 'Grüße aus Zürich'
|
||||
|
||||
def test_update_existing_application(self, app, sample_application_data):
|
||||
"""Test updating existing application data."""
|
||||
session_id = sample_application_data['session_id']
|
||||
with app.app_context():
|
||||
session_id = sample_application_data['session_id']
|
||||
|
||||
# Save initial data
|
||||
save_application_data(session_id, sample_application_data)
|
||||
# Save initial data
|
||||
save_application_data(session_id, sample_application_data)
|
||||
|
||||
# Update data
|
||||
sample_application_data['current_page'] = 3
|
||||
sample_application_data['motivation_answers'] = {
|
||||
'current_job': 'Developer',
|
||||
'motivation': 'Great company'
|
||||
}
|
||||
save_application_data(session_id, sample_application_data)
|
||||
# Update data
|
||||
sample_application_data['current_page'] = 3
|
||||
sample_application_data['motivation_answers'] = {
|
||||
'current_job': 'Developer',
|
||||
'motivation': 'Great company'
|
||||
}
|
||||
save_application_data(session_id, sample_application_data)
|
||||
|
||||
# Verify update
|
||||
loaded_data = load_application_data(session_id)
|
||||
assert loaded_data['current_page'] == 3
|
||||
assert 'motivation_answers' in loaded_data
|
||||
assert loaded_data['motivation_answers']['current_job'] == 'Developer'
|
||||
# Verify update
|
||||
loaded_data = load_application_data(session_id)
|
||||
assert loaded_data['current_page'] == 3
|
||||
assert 'motivation_answers' in loaded_data
|
||||
assert loaded_data['motivation_answers']['current_job'] == 'Developer'
|
||||
|
||||
|
||||
class TestFolderStructure:
|
||||
@@ -104,30 +109,33 @@ class TestFolderStructure:
|
||||
|
||||
def test_application_folder_created(self, app, sample_application_data):
|
||||
"""Test that application folder is created when saving data."""
|
||||
session_id = sample_application_data['session_id']
|
||||
save_application_data(session_id, sample_application_data)
|
||||
with app.app_context():
|
||||
session_id = sample_application_data['session_id']
|
||||
save_application_data(session_id, sample_application_data)
|
||||
|
||||
app_path = get_application_path(session_id)
|
||||
assert os.path.exists(app_path)
|
||||
assert os.path.isdir(app_path)
|
||||
app_path = get_application_path(session_id)
|
||||
assert os.path.exists(app_path)
|
||||
assert os.path.isdir(app_path)
|
||||
|
||||
def test_data_yaml_created(self, app, sample_application_data):
|
||||
"""Test that data.yaml file is created."""
|
||||
session_id = sample_application_data['session_id']
|
||||
save_application_data(session_id, sample_application_data)
|
||||
with app.app_context():
|
||||
session_id = sample_application_data['session_id']
|
||||
save_application_data(session_id, sample_application_data)
|
||||
|
||||
data_file = get_data_file_path(session_id)
|
||||
assert os.path.exists(data_file)
|
||||
assert os.path.isfile(data_file)
|
||||
assert data_file.endswith('data.yaml')
|
||||
data_file = get_data_file_path(session_id)
|
||||
assert os.path.exists(data_file)
|
||||
assert os.path.isfile(data_file)
|
||||
assert data_file.endswith('data.yaml')
|
||||
|
||||
def test_attachments_folder_path(self, app):
|
||||
"""Test that attachments folder path is correct."""
|
||||
session_id = 'test-attachments-123'
|
||||
attachments_path = get_attachments_path(session_id)
|
||||
with app.app_context():
|
||||
session_id = 'test-attachments-123'
|
||||
attachments_path = get_attachments_path(session_id)
|
||||
|
||||
assert 'attachments' in attachments_path
|
||||
assert session_id in attachments_path
|
||||
assert 'attachments' in attachments_path
|
||||
assert session_id in attachments_path
|
||||
|
||||
|
||||
class TestDataPersistence:
|
||||
@@ -143,7 +151,7 @@ class TestDataPersistence:
|
||||
|
||||
# Extract session ID from response
|
||||
# For testing, we'll create a known session
|
||||
from app import save_application_data
|
||||
from app.models import save_application_data
|
||||
session_id = 'test-persist-123'
|
||||
save_application_data(session_id, {
|
||||
'session_id': session_id,
|
||||
@@ -189,26 +197,27 @@ class TestDataPersistence:
|
||||
|
||||
def test_empty_optional_fields_preserved(self, app):
|
||||
"""Test that empty optional fields are preserved correctly."""
|
||||
session_id = 'test-empty-fields'
|
||||
data = {
|
||||
'session_id': session_id,
|
||||
'email': 'test@example.com',
|
||||
'job_name': 'Test',
|
||||
'current_page': 3,
|
||||
'personal_info': {
|
||||
'civil_status': '' # Optional, empty
|
||||
},
|
||||
'motivation_answers': {
|
||||
'current_job': '', # Optional, empty
|
||||
'motivation': 'Some text',
|
||||
'qualifications': '',
|
||||
'salary': ''
|
||||
with app.app_context():
|
||||
session_id = 'test-empty-fields'
|
||||
data = {
|
||||
'session_id': session_id,
|
||||
'email': 'test@example.com',
|
||||
'job_name': 'Test',
|
||||
'current_page': 3,
|
||||
'personal_info': {
|
||||
'civil_status': '' # Optional, empty
|
||||
},
|
||||
'motivation_answers': {
|
||||
'current_job': '', # Optional, empty
|
||||
'motivation': 'Some text',
|
||||
'qualifications': '',
|
||||
'salary': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save_application_data(session_id, data)
|
||||
loaded_data = load_application_data(session_id)
|
||||
save_application_data(session_id, data)
|
||||
loaded_data = load_application_data(session_id)
|
||||
|
||||
assert loaded_data['personal_info']['civil_status'] == ''
|
||||
assert loaded_data['motivation_answers']['current_job'] == ''
|
||||
assert loaded_data['motivation_answers']['motivation'] == 'Some text'
|
||||
assert loaded_data['personal_info']['civil_status'] == ''
|
||||
assert loaded_data['motivation_answers']['current_job'] == ''
|
||||
assert loaded_data['motivation_answers']['motivation'] == 'Some text'
|
||||
|
||||
Reference in New Issue
Block a user