This commit is contained in:
Dries Peeters
2025-10-23 14:00:53 +02:00
parent e418c77590
commit 9241fbd5a5
2 changed files with 208 additions and 229 deletions
+58 -24
View File
@@ -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
+150 -205
View File
@@ -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