Files
email-organizer/tests/test_routes.py
2025-08-06 21:30:33 -07:00

372 lines
18 KiB
Python

import pytest
from app.models import User, Folder
from app import db
import uuid
from bs4 import BeautifulSoup
def test_index_route(client, app, mock_user):
"""Test the index route requires authentication."""
response = client.get('/')
# Should redirect to login page
assert response.status_code == 302
assert '/login' in response.location
def test_index_route_authenticated(authenticated_client, app, mock_user):
"""Test the index route works for authenticated users."""
response = authenticated_client.get('/')
assert response.status_code == 200
# Check if the page contains expected elements
assert b'Email Organizer' in response.data
assert b'Folders' in response.data
assert b'Test User' in response.data # Should show user's name
def test_add_folder_route(authenticated_client, mock_user):
"""Test the add folder API endpoint."""
# Get initial count of folders for the user
initial_folder_count = Folder.query.count()
# Send form data (URL encoded) instead of JSON
response = authenticated_client.post('/api/folders',
data={'name': 'Test Folder', 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
# Verify the response status is 201 Created
assert response.status_code == 201
# Verify that the number of folders has increased
final_folder_count = Folder.query.count()
assert final_folder_count > initial_folder_count
# Verify the folder belongs to the authenticated user
created_folder = Folder.query.filter_by(name='Test Folder').first()
assert created_folder.user_id == mock_user.id
# Validation failure tests
def test_add_folder_validation_failure_empty_name(authenticated_client, mock_user):
"""Test validation failure when folder name is empty."""
response = authenticated_client.post('/api/folders',
data={'name': '', 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
# Check that the specific error message is present
error_text = soup.find(string='Folder name is required')
assert error_text is not None
# Check that the input field has the error class
name_input = soup.find('input', {'name': 'name'})
assert name_input is not None
assert 'input-error' in name_input.get('class', [])
def test_add_folder_validation_failure_short_name(authenticated_client, mock_user):
"""Test validation failure when folder name is too short."""
response = authenticated_client.post('/api/folders',
data={'name': 'ab', 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Folder name must be at least 3 characters')
assert error_text is not None
def test_add_folder_validation_failure_long_name(authenticated_client, mock_user):
"""Test validation failure when folder name is too long."""
long_name = 'a' * 51
response = authenticated_client.post('/api/folders',
data={'name': long_name, 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Folder name must be less than 50 characters')
assert error_text is not None
def test_add_folder_validation_failure_empty_rule(authenticated_client, mock_user):
"""Test validation failure when rule text is empty."""
response = authenticated_client.post('/api/folders',
data={'name': 'Test Folder', 'rule_text': ''},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Rule text is required')
assert error_text is not None
def test_add_folder_validation_failure_short_rule(authenticated_client, mock_user):
"""Test validation failure when rule text is too short."""
response = authenticated_client.post('/api/folders',
data={'name': 'Test Folder', 'rule_text': 'short'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Rule text must be at least 10 characters')
assert error_text is not None
def test_add_folder_validation_failure_long_rule(authenticated_client, mock_user):
"""Test validation failure when rule text is too long."""
long_rule = 'a' * 201
response = authenticated_client.post('/api/folders',
data={'name': 'Test Folder', 'rule_text': long_rule},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Rule text must be less than 200 characters')
assert error_text is not None
def test_add_folder_validation_multiple_errors(authenticated_client, mock_user):
"""Test validation failure with multiple errors."""
response = authenticated_client.post('/api/folders',
data={'name': 'ab', 'rule_text': 'short'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text1 = soup.find(string='Folder name must be at least 3 characters')
error_text2 = soup.find(string='Rule text must be at least 10 characters')
assert error_text1 is not None
assert error_text2 is not None
# Edit folder validation failure tests
def test_edit_folder_validation_failure_empty_name(authenticated_client, mock_folder):
"""Test validation failure when folder name is empty during edit."""
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': '', 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Folder name is required')
assert error_text is not None
def test_edit_folder_validation_failure_short_name(authenticated_client, mock_folder):
"""Test validation failure when folder name is too short during edit."""
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': 'ab', 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Folder name must be at least 3 characters')
assert error_text is not None
def test_edit_folder_validation_failure_long_name(authenticated_client, mock_folder):
"""Test validation failure when folder name is too long during edit."""
long_name = 'a' * 51
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': long_name, 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Folder name must be less than 50 characters')
assert error_text is not None
def test_edit_folder_validation_failure_empty_rule(authenticated_client, mock_folder):
"""Test validation failure when rule text is empty during edit."""
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': 'Test Folder', 'rule_text': ''},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Rule text is required')
assert error_text is not None
def test_edit_folder_validation_failure_short_rule(authenticated_client, mock_folder):
"""Test validation failure when rule text is too short during edit."""
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': 'Test Folder', 'rule_text': 'short'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Rule text must be at least 10 characters')
assert error_text is not None
def test_edit_folder_validation_failure_long_rule(authenticated_client, mock_folder):
"""Test validation failure when rule text is too long during edit."""
long_rule = 'a' * 201
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': 'Test Folder', 'rule_text': long_rule},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text = soup.find(string='Rule text must be less than 200 characters')
assert error_text is not None
def test_edit_folder_validation_multiple_errors(authenticated_client, mock_folder):
"""Test validation failure with multiple errors during edit."""
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': 'ab', 'rule_text': 'short'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
soup = BeautifulSoup(response.data, 'html.parser')
error_text1 = soup.find(string='Folder name must be at least 3 characters')
error_text2 = soup.find(string='Rule text must be at least 10 characters')
assert error_text1 is not None
assert error_text2 is not None
# Dialog close tests
def test_add_folder_success_closes_dialog(authenticated_client, mock_user):
"""Test that successful folder creation triggers dialog close."""
response = authenticated_client.post('/api/folders',
data={'name': 'Test Folder', 'rule_text': 'Test rule something ok yes'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 201
# Check for close-modal trigger in response headers
assert 'HX-Trigger' in response.headers
assert 'close-modal' in response.headers['HX-Trigger']
def test_edit_folder_success_closes_dialog(authenticated_client, mock_folder):
"""Test that successful folder update triggers dialog close."""
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': 'Updated Folder', 'rule_text': 'Updated rule text'},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
# Check for close-modal trigger in response headers
assert 'HX-Trigger' in response.headers
assert 'close-modal' in response.headers['HX-Trigger']
# Content matching tests
def test_add_folder_content_matches_submission(authenticated_client, mock_user):
"""Test that submitted folder content matches what was sent."""
test_name = 'Test Folder Content'
test_rule = 'Test rule content matching submission'
test_priority = '1'
response = authenticated_client.post('/api/folders',
data={'name': test_name, 'rule_text': test_rule, 'priority': test_priority},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 201
# Verify the folder was created with correct content
created_folder = Folder.query.filter_by(name=test_name).first()
assert created_folder is not None
assert created_folder.name == test_name.strip()
assert created_folder.rule_text == test_rule.strip()
assert created_folder.priority == int(test_priority)
assert created_folder.user_id == mock_user.id
def test_edit_folder_content_matches_submission(authenticated_client, mock_folder):
"""Test that updated folder content matches what was sent."""
test_name = 'Updated Folder Content'
test_rule = 'Updated rule content matching submission'
test_priority = '-1'
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': test_name, 'rule_text': test_rule, 'priority': test_priority},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
# Verify the folder was updated with correct content
updated_folder = Folder.query.filter_by(id=mock_folder.id).first()
assert updated_folder is not None
assert updated_folder.name == test_name.strip()
assert updated_folder.rule_text == test_rule.strip()
assert updated_folder.priority == int(test_priority)
def test_add_folder_content_whitespace_handling(authenticated_client, mock_user):
"""Test that whitespace is properly handled in submitted content."""
test_name = ' Test Folder With Whitespace '
test_rule = ' Test rule with whitespace around it '
test_priority = '0'
response = authenticated_client.post('/api/folders',
data={'name': test_name, 'rule_text': test_rule, 'priority': test_priority},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 201
# Verify the folder was created with properly trimmed content
created_folder = Folder.query.filter_by(name='Test Folder With Whitespace').first()
assert created_folder is not None
assert created_folder.name == 'Test Folder With Whitespace' # Should be trimmed
assert created_folder.rule_text == 'Test rule with whitespace around it' # Should be trimmed
assert created_folder.priority == int(test_priority)
def test_edit_folder_content_whitespace_handling(authenticated_client, mock_folder):
"""Test that whitespace is properly handled in updated content."""
test_name = ' Updated Folder With Whitespace '
test_rule = ' Updated rule with whitespace around it '
test_priority = '1'
response = authenticated_client.put(f'/api/folders/{mock_folder.id}',
data={'name': test_name, 'rule_text': test_rule, 'priority': test_priority},
content_type='application/x-www-form-urlencoded')
assert response.status_code == 200
# Verify the folder was updated with properly trimmed content
updated_folder = Folder.query.filter_by(id=mock_folder.id).first()
assert updated_folder is not None
assert updated_folder.name == 'Updated Folder With Whitespace' # Should be trimmed
assert updated_folder.rule_text == 'Updated rule with whitespace around it' # Should be trimmed
assert updated_folder.priority == int(test_priority)
def test_toggle_folder_organize_enabled(authenticated_client, mock_folder):
"""Test toggling the organize_enabled flag for a folder."""
# Verify initial state is True (default)
assert mock_folder.organize_enabled is True
# Toggle the flag
response = authenticated_client.put(f'/api/folders/{mock_folder.id}/type', data={'folder_type': 'ignore'}, content_type='application/x-www-form-urlencoded')
# Should return 200 OK
assert response.status_code == 200
# Verify the folder was updated in the database
updated_folder = Folder.query.filter_by(id=mock_folder.id).first()
assert updated_folder is not None
assert updated_folder.folder_type == 'ignore'
# Toggle back to make sure it works both ways
response2 = authenticated_client.put(f'/api/folders/{mock_folder.id}/type', data={'folder_type': 'tidy'}, content_type='application/x-www-form-urlencoded')
# Should return 200 OK
assert response2.status_code == 200
# Verify the folder was updated in the database again
updated_folder2 = Folder.query.filter_by(id=mock_folder.id).first()
assert updated_folder2 is not None
assert updated_folder2.folder_type == 'tidy'
def test_toggle_folder_organize_enabled_not_found(authenticated_client, mock_user):
"""Test toggling organize_enabled flag for a non-existent folder."""
# Try to toggle a folder that doesn't exist
response = authenticated_client.put('/api/folders/999/toggle')
# Should return 404 Not Found
assert response.status_code == 404
def test_toggle_folder_organize_enabled_unauthorized(authenticated_client, mock_user, app):
"""Test toggling organize_enabled flag for a folder that doesn't belong to the user."""
# Create a folder that belongs to a different user
other_user = User(email='other@example.com', first_name='Other', last_name='User')
other_user.set_password('password')
with app.app_context():
db.session.add(other_user)
db.session.commit()
# Create a folder for the other user
other_folder = Folder(
user_id=other_user.id,
name='Other User Folder',
rule_text='Test rule text'
)
db.session.add(other_folder)
db.session.commit()
# Try to toggle the flag for the folder that doesn't belong to authenticated user
response = authenticated_client.put(f'/api/folders/{other_folder.id}/toggle')
# Should return 404 Not Found (folder not found due to authorization check)
assert response.status_code == 404