Files
PROJECT-CONTORL/backend/tests/test_tasks.py
beabigegg daca7798e3 feat: implement task management module
Backend (FastAPI):
- Database migration for spaces, projects, task_statuses, tasks tables
- SQLAlchemy models with relationships
- Pydantic schemas for CRUD operations
- Spaces API: CRUD with soft delete
- Projects API: CRUD with auto-created default statuses
- Tasks API: CRUD, status change, assign, subtask support
- Permission middleware with Security Level filtering
- Subtask depth limit (max 2 levels)

Frontend (React + Vite):
- Layout component with navigation
- Spaces list page
- Projects list page
- Tasks list page with status management

Fixes:
- auth_client.py: use 'username' field for external API
- config.py: extend JWT expiry to 7 days
- auth/router.py: sync Redis session with JWT expiry

Tests: 36 passed (unit + integration)
E2E: All APIs verified with real authentication

OpenSpec: add-task-management archived

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 00:31:34 +08:00

117 lines
3.6 KiB
Python

import pytest
from unittest.mock import MagicMock
from fastapi.testclient import TestClient
from app.main import app
from app.models import Task
from app.middleware.auth import check_task_access, check_task_edit_access
client = TestClient(app)
def get_mock_user(is_admin=False):
user = MagicMock()
user.id = "test-user-id"
user.is_system_admin = is_admin
user.department_id = "dept-1"
return user
def get_mock_project(owner_id="owner-id"):
project = MagicMock()
project.id = "project-id"
project.owner_id = owner_id
project.security_level = "public"
project.department_id = "dept-1"
return project
def get_mock_task(created_by="creator-id", assignee_id=None):
task = MagicMock()
task.id = "task-id"
task.created_by = created_by
task.assignee_id = assignee_id
return task
class TestTaskModel:
"""Test Task model."""
def test_task_creation(self):
"""Test Task model can be instantiated."""
task = Task(
id="test-id",
project_id="project-id",
title="Test Task",
priority="medium",
created_by="user-id",
)
assert task.title == "Test Task"
assert task.priority == "medium"
class TestTaskRoutes:
"""Test task routes exist."""
def test_task_routes_exist(self):
"""Test that all task routes are registered."""
routes = [route.path for route in app.routes if hasattr(route, 'path')]
assert "/api/projects/{project_id}/tasks" in routes
assert "/api/tasks/{task_id}" in routes
assert "/api/tasks/{task_id}/status" in routes
assert "/api/tasks/{task_id}/assign" in routes
class TestTaskPermissions:
"""Test task permission logic."""
def test_admin_has_full_access(self):
"""Test that admin has full access to all tasks."""
admin = get_mock_user(is_admin=True)
project = get_mock_project()
task = get_mock_task()
assert check_task_access(admin, task, project) == True
assert check_task_edit_access(admin, task, project) == True
def test_project_owner_can_edit_any_task(self):
"""Test that project owner can edit any task in the project."""
user = get_mock_user()
project = get_mock_project(owner_id=user.id)
task = get_mock_task(created_by="other-user")
assert check_task_edit_access(user, task, project) == True
def test_creator_can_edit_own_task(self):
"""Test that task creator can edit their own task."""
user = get_mock_user()
project = get_mock_project(owner_id="other-user")
task = get_mock_task(created_by=user.id)
assert check_task_edit_access(user, task, project) == True
def test_assignee_can_edit_assigned_task(self):
"""Test that assignee can edit their assigned task."""
user = get_mock_user()
project = get_mock_project(owner_id="other-user")
task = get_mock_task(created_by="other-user", assignee_id=user.id)
assert check_task_edit_access(user, task, project) == True
def test_unrelated_user_cannot_edit(self):
"""Test that unrelated user cannot edit task."""
user = get_mock_user()
project = get_mock_project(owner_id="project-owner")
task = get_mock_task(created_by="creator", assignee_id="assignee")
assert check_task_edit_access(user, task, project) == False
class TestSubtaskDepth:
"""Test subtask depth limiting."""
def test_max_depth_constant(self):
"""Test that MAX_SUBTASK_DEPTH is defined."""
from app.api.tasks.router import MAX_SUBTASK_DEPTH
assert MAX_SUBTASK_DEPTH == 2