233 lines
8.5 KiB
Python
233 lines
8.5 KiB
Python
"""
|
|||
|
|
Tests for file upload functionality.
|
||
|
|
"""
|
||
|
|
import pytest
|
||
|
|
import os
|
||
|
|
from io import BytesIO
|
||
|
|
from app 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
|