Files
application-form-7/tests/test_uploads.py
T
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

233 lines
8.5 KiB
Python

"""
Tests for file upload functionality.
"""
import pytest
import os
from io import BytesIO
from app.models import load_application_data, get_attachments_path
class TestFileUpload:
"""Tests for file upload functionality."""
def test_upload_valid_pdf(self, client, create_test_application, sample_pdf_file):
"""Test uploading a valid PDF file."""
session_id = 'test-upload-pdf'
create_test_application(session_id=session_id, current_page=4)
sample_pdf_file.seek(0)
response = client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_pdf_file, 'resume.pdf')},
content_type='multipart/form-data',
follow_redirects=True
)
assert response.status_code == 200
# Verify file was added to data
loaded_data = load_application_data(session_id)
assert len(loaded_data['uploaded_files']) == 1
assert loaded_data['uploaded_files'][0]['original_name'] == 'resume.pdf'
def test_upload_valid_jpg(self, client, create_test_application, sample_jpg_file):
"""Test uploading a valid JPG image."""
session_id = 'test-upload-jpg'
create_test_application(session_id=session_id, current_page=4)
sample_jpg_file.seek(0)
response = client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_jpg_file, 'photo.jpg')},
content_type='multipart/form-data',
follow_redirects=True
)
assert response.status_code == 200
loaded_data = load_application_data(session_id)
assert len(loaded_data['uploaded_files']) == 1
def test_upload_multiple_files(self, client, create_test_application, sample_pdf_file, sample_jpg_file):
"""Test uploading multiple files."""
session_id = 'test-upload-multiple'
create_test_application(session_id=session_id, current_page=4)
# Upload first file
sample_pdf_file.seek(0)
client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_pdf_file, 'file1.pdf')},
content_type='multipart/form-data'
)
# Upload second file
sample_jpg_file.seek(0)
client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_jpg_file, 'file2.jpg')},
content_type='multipart/form-data'
)
loaded_data = load_application_data(session_id)
assert len(loaded_data['uploaded_files']) == 2
def test_upload_exceeds_max_files(self, client, create_test_application, sample_pdf_file):
"""Test that uploading more than 3 files is rejected."""
session_id = 'test-upload-max'
create_test_application(
session_id=session_id,
current_page=4,
uploaded_files=[
{'original_name': 'file1.pdf', 'stored_name': 'file1.pdf', 'size': 1000},
{'original_name': 'file2.pdf', 'stored_name': 'file2.pdf', 'size': 1000},
{'original_name': 'file3.pdf', 'stored_name': 'file3.pdf', 'size': 1000}
]
)
sample_pdf_file.seek(0)
response = client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_pdf_file, 'file4.pdf')},
content_type='multipart/form-data',
follow_redirects=True
)
assert response.status_code == 200
# Should still have 3 files (4th was rejected)
loaded_data = load_application_data(session_id)
assert len(loaded_data['uploaded_files']) == 3
def test_upload_invalid_file_type(self, client, create_test_application):
"""Test that invalid file types are rejected."""
session_id = 'test-upload-invalid'
create_test_application(session_id=session_id, current_page=4)
invalid_file = BytesIO(b'executable content')
response = client.post(
f'/apply/{session_id}/upload-file',
data={'file': (invalid_file, 'virus.exe')},
content_type='multipart/form-data',
follow_redirects=True
)
assert response.status_code == 200
loaded_data = load_application_data(session_id)
assert len(loaded_data.get('uploaded_files', [])) == 0
def test_upload_file_too_large(self, client, create_test_application, large_file):
"""Test that files larger than 4MB are rejected."""
session_id = 'test-upload-large'
create_test_application(session_id=session_id, current_page=4)
large_file.seek(0)
response = client.post(
f'/apply/{session_id}/upload-file',
data={'file': (large_file, 'large.pdf')},
content_type='multipart/form-data',
follow_redirects=True
)
assert response.status_code == 200
loaded_data = load_application_data(session_id)
assert len(loaded_data.get('uploaded_files', [])) == 0
def test_upload_no_file_selected(self, client, create_test_application):
"""Test uploading without selecting a file."""
session_id = 'test-upload-nofile'
create_test_application(session_id=session_id, current_page=4)
response = client.post(
f'/apply/{session_id}/upload-file',
data={},
content_type='multipart/form-data',
follow_redirects=True
)
assert response.status_code == 200
class TestFileRemoval:
"""Tests for file removal functionality."""
def test_remove_uploaded_file(self, client, create_test_application, sample_pdf_file, app):
"""Test removing an uploaded file."""
session_id = 'test-remove-file'
create_test_application(session_id=session_id, current_page=4)
# Upload a file
sample_pdf_file.seek(0)
client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_pdf_file, 'remove_me.pdf')},
content_type='multipart/form-data'
)
# Verify it was uploaded
loaded_data = load_application_data(session_id)
assert len(loaded_data['uploaded_files']) == 1
# Remove the file
response = client.post(
f'/apply/{session_id}/remove-file/0',
follow_redirects=True
)
assert response.status_code == 200
loaded_data = load_application_data(session_id)
assert len(loaded_data['uploaded_files']) == 0
def test_remove_nonexistent_file(self, client, create_test_application):
"""Test removing a file with invalid index."""
session_id = 'test-remove-invalid'
create_test_application(session_id=session_id, current_page=4)
response = client.post(
f'/apply/{session_id}/remove-file/999',
follow_redirects=True
)
assert response.status_code == 200
class TestFileStorage:
"""Tests for file storage on disk."""
def test_file_saved_to_attachments_folder(self, client, create_test_application, sample_pdf_file, app):
"""Test that uploaded files are saved to the attachments folder."""
session_id = 'test-file-storage'
create_test_application(session_id=session_id, current_page=4)
sample_pdf_file.seek(0)
client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_pdf_file, 'stored.pdf')},
content_type='multipart/form-data'
)
# Check that file exists in attachments folder
attachments_path = get_attachments_path(session_id)
assert os.path.exists(attachments_path)
# Check that a file was created
files = os.listdir(attachments_path)
assert len(files) > 0
def test_filename_sanitization(self, client, create_test_application, sample_pdf_file):
"""Test that filenames are sanitized for security."""
session_id = 'test-sanitize'
create_test_application(session_id=session_id, current_page=4)
sample_pdf_file.seek(0)
# Try uploading a file with potentially dangerous name
client.post(
f'/apply/{session_id}/upload-file',
data={'file': (sample_pdf_file, '../../../etc/passwd.pdf')},
content_type='multipart/form-data'
)
loaded_data = load_application_data(session_id)
if len(loaded_data['uploaded_files']) > 0:
# Filename should be sanitized
stored_name = loaded_data['uploaded_files'][0]['stored_name']
assert '../' not in stored_name
assert '/' not in stored_name.split('_')[-1] # After timestamp