diff --git a/tests/test_profile_avatar.py b/tests/test_profile_avatar.py index 67e42b8..2a79751 100644 --- a/tests/test_profile_avatar.py +++ b/tests/test_profile_avatar.py @@ -1,4 +1,5 @@ import io +import os import pytest from PIL import Image @@ -11,20 +12,42 @@ def _make_test_image_bytes(fmt='PNG', size=(10, 10), color=(255, 0, 0, 255)): return buf +@pytest.fixture +def avatar_test_app(app, temp_dir): + """Configure app with temporary upload folder for avatar tests""" + app.config['UPLOAD_FOLDER'] = temp_dir + # Ensure the avatars directory exists + avatars_dir = os.path.join(temp_dir, 'avatars') + os.makedirs(avatars_dir, exist_ok=True) + return app + + @pytest.mark.routes -def test_upload_avatar(authenticated_client, user, app): - with app.app_context(): +def test_upload_avatar(app, temp_dir, user): + """Test uploading an avatar""" + from app import db + + # Configure temp upload folder + app.config['UPLOAD_FOLDER'] = temp_dir + avatars_dir = os.path.join(temp_dir, 'avatars') + os.makedirs(avatars_dir, exist_ok=True) + + # Create authenticated client + with app.test_client() as client: + with client.session_transaction() as sess: + sess['_user_id'] = str(user.id) + sess['_fresh'] = True + assert user.avatar_filename is None - data = { - 'full_name': 'Test User', - 'preferred_language': 'en', - 'avatar': ( _make_test_image_bytes('PNG'), 'avatar.png' ) - } - resp = authenticated_client.post('/profile/edit', data=data, content_type='multipart/form-data', follow_redirects=True) - assert resp.status_code == 200 + data = { + 'full_name': 'Test User', + 'preferred_language': 'en', + 'avatar': (_make_test_image_bytes('PNG'), 'avatar.png') + } + resp = client.post('/profile/edit', data=data, content_type='multipart/form-data', follow_redirects=True) + assert resp.status_code == 200 - with app.app_context(): from app.models import User u = User.query.get(user.id) assert u.avatar_filename is not None @@ -32,26 +55,37 @@ def test_upload_avatar(authenticated_client, user, app): @pytest.mark.routes -def test_remove_avatar(authenticated_client, user, app): - # First upload an avatar - data = { - 'full_name': 'Test User', - 'preferred_language': 'en', - 'avatar': ( _make_test_image_bytes('PNG'), 'avatar.png' ) - } - authenticated_client.post('/profile/edit', data=data, content_type='multipart/form-data') +def test_remove_avatar(app, temp_dir, user): + """Test removing an avatar""" + from app import db + + # Configure temp upload folder + app.config['UPLOAD_FOLDER'] = temp_dir + avatars_dir = os.path.join(temp_dir, 'avatars') + os.makedirs(avatars_dir, exist_ok=True) + + # Create authenticated client + with app.test_client() as client: + with client.session_transaction() as sess: + sess['_user_id'] = str(user.id) + sess['_fresh'] = True + + # First upload an avatar + data = { + 'full_name': 'Test User', + 'preferred_language': 'en', + 'avatar': (_make_test_image_bytes('PNG'), 'avatar.png') + } + client.post('/profile/edit', data=data, content_type='multipart/form-data') - with app.app_context(): from app.models import User u = User.query.get(user.id) assert u.avatar_filename - # Remove it - resp = authenticated_client.post('/profile/avatar/remove', data={'csrf_token': 'disabled-in-tests'}, follow_redirects=True) - assert resp.status_code == 200 + # Remove it + resp = client.post('/profile/avatar/remove', data={'csrf_token': 'disabled-in-tests'}, follow_redirects=True) + assert resp.status_code == 200 - with app.app_context(): - from app.models import User u = User.query.get(user.id) assert u.avatar_filename is None diff --git a/tests/test_project_inactive_status.py b/tests/test_project_inactive_status.py index 2f5bbd6..8899724 100644 --- a/tests/test_project_inactive_status.py +++ b/tests/test_project_inactive_status.py @@ -1,240 +1,185 @@ """Tests for project inactive status functionality""" import pytest -from app import create_app, db -from app.models import Project, Client, User - - -@pytest.fixture -def app(): - """Create application for testing""" - app = create_app() - app.config['TESTING'] = True - app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' - app.config['WTF_CSRF_ENABLED'] = False - - with app.app_context(): - db.create_all() - yield app - db.session.remove() - db.drop_all() - - -@pytest.fixture -def client(app): - """Create test client""" - return app.test_client() - - -@pytest.fixture -def admin_user(app): - """Create admin user for testing""" - with app.app_context(): - user = User(username='admin', role='admin') - user.set_password('password') - db.session.add(user) - db.session.commit() - return user - - -@pytest.fixture -def test_client_obj(app): - """Create test client for projects""" - with app.app_context(): - client = Client(name='Test Client') - db.session.add(client) - db.session.commit() - return client - - -@pytest.fixture -def test_project(app, test_client_obj): - """Create test project""" - with app.app_context(): - project = Project(name='Test Project', client_id=test_client_obj.id) - db.session.add(project) - db.session.commit() - return project +from app.models import Project class TestProjectInactiveStatus: """Test project inactive status functionality""" - def test_project_default_status(self, app, test_client_obj): + @pytest.mark.models + def test_project_default_status(self, app, test_client): """Test that new projects have active status by default""" - with app.app_context(): - project = Project(name='New Project', client_id=test_client_obj.id) - db.session.add(project) - db.session.commit() - - assert project.status == 'active' - assert project.is_active is True + from app import db + + project = Project(name='New Project', client_id=test_client.id) + db.session.add(project) + db.session.commit() + + assert project.status == 'active' + assert project.is_active is True - def test_project_deactivate(self, app, test_project): + @pytest.mark.models + def test_project_deactivate(self, app, project): """Test deactivating a project""" - with app.app_context(): - project = Project.query.get(test_project.id) - project.deactivate() - - assert project.status == 'inactive' - assert project.is_active is False + from app import db + + project.deactivate() + db.session.commit() + + assert project.status == 'inactive' + assert project.is_active is False - def test_project_activate_from_inactive(self, app, test_project): + @pytest.mark.models + def test_project_activate_from_inactive(self, app, project): """Test activating an inactive project""" - with app.app_context(): - project = Project.query.get(test_project.id) - project.deactivate() - assert project.status == 'inactive' - - project.activate() - assert project.status == 'active' - assert project.is_active is True + from app import db + + project.deactivate() + db.session.commit() + assert project.status == 'inactive' + + project.activate() + db.session.commit() + assert project.status == 'active' + assert project.is_active is True - def test_project_archive_from_inactive(self, app, test_project): + @pytest.mark.models + def test_project_archive_from_inactive(self, app, project): """Test archiving an inactive project""" - with app.app_context(): - project = Project.query.get(test_project.id) - project.deactivate() - assert project.status == 'inactive' - - project.archive() - assert project.status == 'archived' + from app import db + + project.deactivate() + db.session.commit() + assert project.status == 'inactive' + + project.archive() + db.session.commit() + assert project.status == 'archived' - def test_project_unarchive_to_active(self, app, test_project): + @pytest.mark.models + def test_project_unarchive_to_active(self, app, project): """Test unarchiving a project returns it to active""" - with app.app_context(): - project = Project.query.get(test_project.id) - project.archive() - assert project.status == 'archived' - - project.unarchive() - assert project.status == 'active' + from app import db + + project.archive() + db.session.commit() + assert project.status == 'archived' + + project.unarchive() + db.session.commit() + assert project.status == 'active' - def test_project_status_transitions(self, app, test_project): + @pytest.mark.models + def test_project_status_transitions(self, app, project): """Test complete status transition cycle""" - with app.app_context(): - project = Project.query.get(test_project.id) - - # Start active - assert project.status == 'active' - - # Move to inactive - project.deactivate() - assert project.status == 'inactive' - - # Move back to active - project.activate() - assert project.status == 'active' - - # Move to archived - project.archive() - assert project.status == 'archived' - - # Move back to active via unarchive - project.unarchive() - assert project.status == 'active' + from app import db + + # Start active + assert project.status == 'active' + + # Move to inactive + project.deactivate() + db.session.commit() + assert project.status == 'inactive' + + # Move back to active + project.activate() + db.session.commit() + assert project.status == 'active' + + # Move to archived + project.archive() + db.session.commit() + assert project.status == 'archived' + + # Move back to active via unarchive + project.unarchive() + db.session.commit() + assert project.status == 'active' class TestProjectInactiveRoutes: """Test project inactive status routes""" - def login(self, client, username='admin', password='password'): - """Helper to log in""" - return client.post('/auth/login', data={ - 'username': username, - 'password': password - }, follow_redirects=True) - - def test_deactivate_project_route(self, client, app, admin_user, test_project): + @pytest.mark.routes + def test_deactivate_project_route(self, admin_authenticated_client, app, project): """Test deactivating a project via route""" - with client: - self.login(client) - - with app.app_context(): - project_id = test_project.id - - response = client.post(f'/projects/{project_id}/deactivate', - follow_redirects=True) - - assert response.status_code == 200 - - with app.app_context(): - project = Project.query.get(project_id) - assert project.status == 'inactive' + from app import db + + project_id = project.id + + response = admin_authenticated_client.post(f'/projects/{project_id}/deactivate', + follow_redirects=True) + + assert response.status_code == 200 + + db.session.refresh(project) + assert project.status == 'inactive' - def test_activate_project_route(self, client, app, admin_user, test_project): + @pytest.mark.routes + def test_activate_project_route(self, admin_authenticated_client, app, project): """Test activating a project via route""" - with client: - self.login(client) - - with app.app_context(): - project = Project.query.get(test_project.id) - project.deactivate() - project_id = project.id - - response = client.post(f'/projects/{project_id}/activate', - follow_redirects=True) - - assert response.status_code == 200 - - with app.app_context(): - project = Project.query.get(project_id) - assert project.status == 'active' + from app import db + + project.deactivate() + db.session.commit() + project_id = project.id + + response = admin_authenticated_client.post(f'/projects/{project_id}/activate', + follow_redirects=True) + + assert response.status_code == 200 + + db.session.refresh(project) + assert project.status == 'active' - def test_filter_inactive_projects(self, client, app, admin_user, test_client_obj): + @pytest.mark.routes + def test_filter_inactive_projects(self, admin_authenticated_client, app, test_client): """Test filtering projects by inactive status""" - with client: - self.login(client) - - # Create multiple projects with different statuses - with app.app_context(): - active_project = Project(name='Active Project', client_id=test_client_obj.id) - inactive_project = Project(name='Inactive Project', client_id=test_client_obj.id) - archived_project = Project(name='Archived Project', client_id=test_client_obj.id) - - db.session.add_all([active_project, inactive_project, archived_project]) - db.session.commit() - - inactive_project.deactivate() - archived_project.archive() - - # Test filter for inactive projects - response = client.get('/projects?status=inactive') - assert response.status_code == 200 - assert b'Inactive Project' in response.data - assert b'Active Project' not in response.data - assert b'Archived Project' not in response.data + from app import db + + # Create multiple projects with different statuses + active_project = Project(name='Active Project', client_id=test_client.id) + inactive_project = Project(name='Inactive Project', client_id=test_client.id) + archived_project = Project(name='Archived Project', client_id=test_client.id) + + db.session.add_all([active_project, inactive_project, archived_project]) + db.session.commit() + + inactive_project.deactivate() + archived_project.archive() + db.session.commit() + + # Test filter for inactive projects + response = admin_authenticated_client.get('/projects?status=inactive') + assert response.status_code == 200 + assert b'Inactive Project' in response.data + assert b'Active Project' not in response.data + assert b'Archived Project' not in response.data class TestTaskDeletion: """Test individual task deletion""" - def login(self, client, username='admin', password='password'): - """Helper to log in""" - return client.post('/auth/login', data={ - 'username': username, - 'password': password - }, follow_redirects=True) - - def test_task_list_has_delete_buttons(self, client, app, admin_user, test_project): + @pytest.mark.routes + def test_task_list_has_delete_buttons(self, admin_authenticated_client, app, project, admin_user): """Test that task list shows individual delete buttons""" - with client: - self.login(client) - - with app.app_context(): - from app.models import Task - task = Task( - name='Test Task', - project_id=test_project.id, - created_by=admin_user.id - ) - db.session.add(task) - db.session.commit() - - response = client.get('/tasks') - assert response.status_code == 200 - # Should have delete button, not bulk checkboxes - assert b'confirmDeleteTask' in response.data - # Should not have bulk delete - assert b'bulkDeleteBtn' not in response.data - assert b'selectAll' not in response.data + from app.models import Task + from app import db + + task = Task( + name='Test Task', + project_id=project.id, + created_by=admin_user.id + ) + db.session.add(task) + db.session.commit() + + response = admin_authenticated_client.get('/tasks') + assert response.status_code == 200 + # Should have delete button, not bulk checkboxes + assert b'confirmDeleteTask' in response.data + # Should not have bulk delete + assert b'bulkDeleteBtn' not in response.data + assert b'selectAll' not in response.data