from uuid import uuid4 import pytest from sqlalchemy.exc import IntegrityError from app import create_app from app.auth.models import User from app.auth.services import create_user from app.extensions import db as database from app.tasks.services import add_subtask, configure_focus, create_task @pytest.fixture() def admin_app(): application = create_app("testing", {"FEATURE_ADMIN": True}) with application.app_context(): database.create_all() yield application database.session.remove() database.drop_all() @pytest.fixture() def admin_client(admin_app): return admin_app.test_client() def login(client, email: str) -> None: client.post( "/auth/login", data={"email": email, "password": "secure password"}, ) def user_with_role(username: str, role: str) -> User: user = create_user( username, f"{username}@example.com", "secure password", ) user.role = role database.session.commit() return user def test_admin_feature_is_absent_by_default(app, client): endpoints = {rule.endpoint for rule in app.url_map.iter_rules()} assert "admin.index" not in endpoints assert client.get("/admin/").status_code == 404 def test_enabled_admin_redirects_anonymous_users(admin_client): response = admin_client.get("/admin/") assert response.status_code == 302 assert "/auth/login" in response.headers["Location"] @pytest.mark.parametrize("role", ["user", "viewer", "coach"]) def test_non_admin_roles_receive_calm_forbidden_page(admin_client, role): user = user_with_role(f"non_admin_{role}", role) login(admin_client, user.email) response = admin_client.get("/admin/") assert response.status_code == 403 assert b"not available for your role" in response.data def test_admin_can_open_data_free_placeholder(admin_client): user = user_with_role("admin_user", "admin") login(admin_client, user.email) response = admin_client.get("/admin/") assert response.status_code == 200 assert b"User management" in response.data assert b"Analytics" in response.data assert response.data.count(b"Not implemented") == 2 assert b'href="/admin/"' in response.data def test_database_rejects_unknown_roles(admin_app): user = User( username="invalid_role", email="invalid-role@example.com", role="superuser", ) user.set_password("secure password") database.session.add(user) with pytest.raises(IntegrityError): database.session.commit() database.session.rollback() def test_viewer_can_read_but_every_task_mutation_is_forbidden(client, db): viewer = user_with_role("readonly_user", "viewer") task = create_task( viewer.id, title="Readable task", description=None, status="not_started", priority="normal", due_date=None, ) subtask = add_subtask(task, "Readable step") configure_focus(task, timer_minutes=5, context=None, chunk_minutes=[5]) login(client, viewer.email) assert client.get("/tasks/").status_code == 200 assert client.get(f"/tasks/{task.id}").status_code == 200 assert client.get("/tasks/export/json").status_code == 200 operations = [ ("get", "/tasks/new"), ("get", f"/tasks/{task.id}/edit"), ("post", f"/tasks/{task.id}/delete"), ("post", f"/tasks/{task.id}/subtasks"), ("post", f"/tasks/{task.id}/subtasks/{subtask.id}/toggle"), ("post", f"/tasks/{task.id}/focus-settings"), ("post", f"/tasks/{task.id}/chunks/0/toggle"), ("post", f"/tasks/{task.id}/complete"), ("post", "/tasks/import/markdown"), ("post", "/tasks/import/json"), ("post", "/tasks/completed/clear"), ("post", f"/tasks/completed/undo/{uuid4()}"), ] for method, path in operations: response = getattr(client, method)(path) assert response.status_code == 403, path def test_coach_retains_owned_task_write_access(client, db): coach = user_with_role("coach_user", "coach") login(client, coach.email) response = client.post( "/tasks/new", data={ "title": "Coach-owned task", "status": "not_started", "priority": "normal", "due_date": "", }, ) assert response.status_code == 302