import pytest import uuid from app.models import User, Space, Project, Task, TaskStatus, Comment, Notification, Blocker @pytest.fixture def test_user(db): """Create a test user.""" user = User( id=str(uuid.uuid4()), email="testuser@example.com", name="Test User", role_id="00000000-0000-0000-0000-000000000003", is_active=True, is_system_admin=False, ) db.add(user) db.commit() return user @pytest.fixture def user_token(client, mock_redis, test_user): """Get a user token for testing.""" from app.core.security import create_access_token, create_token_payload token_data = create_token_payload( user_id=test_user.id, email=test_user.email, role="engineer", department_id=None, is_system_admin=False, ) token = create_access_token(token_data) mock_redis.setex(f"session:{test_user.id}", 900, token) return token @pytest.fixture def test_space(db): """Create a test space.""" space = Space( id=str(uuid.uuid4()), name="Test Space", description="A test space", owner_id="00000000-0000-0000-0000-000000000001", ) db.add(space) db.commit() return space @pytest.fixture def test_project(db, test_space): """Create a test project.""" project = Project( id=str(uuid.uuid4()), space_id=test_space.id, title="Test Project", description="A test project", owner_id="00000000-0000-0000-0000-000000000001", security_level="public", ) db.add(project) db.commit() return project @pytest.fixture def test_status(db, test_project): """Create a test task status.""" status = TaskStatus( id=str(uuid.uuid4()), project_id=test_project.id, name="To Do", color="#3498db", position=0, ) db.add(status) db.commit() return status @pytest.fixture def test_task(db, test_project, test_status): """Create a test task.""" task = Task( id=str(uuid.uuid4()), project_id=test_project.id, title="Test Task", description="A test task", status_id=test_status.id, created_by="00000000-0000-0000-0000-000000000001", ) db.add(task) db.commit() return task class TestComments: """Tests for Comments API.""" def test_create_comment(self, client, admin_token, test_task): """Test creating a comment.""" response = client.post( f"/api/tasks/{test_task.id}/comments", headers={"Authorization": f"Bearer {admin_token}"}, json={"content": "This is a test comment"}, ) assert response.status_code == 201 data = response.json() assert data["content"] == "This is a test comment" assert data["task_id"] == test_task.id assert data["is_edited"] is False assert data["is_deleted"] is False def test_list_comments(self, client, admin_token, db, test_task): """Test listing comments.""" # Create a comment first comment = Comment( id=str(uuid.uuid4()), task_id=test_task.id, author_id="00000000-0000-0000-0000-000000000001", content="Test comment", ) db.add(comment) db.commit() response = client.get( f"/api/tasks/{test_task.id}/comments", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert data["total"] == 1 assert len(data["comments"]) == 1 assert data["comments"][0]["content"] == "Test comment" def test_update_comment(self, client, admin_token, db, test_task): """Test updating a comment.""" comment = Comment( id=str(uuid.uuid4()), task_id=test_task.id, author_id="00000000-0000-0000-0000-000000000001", content="Original content", ) db.add(comment) db.commit() response = client.put( f"/api/comments/{comment.id}", headers={"Authorization": f"Bearer {admin_token}"}, json={"content": "Updated content"}, ) assert response.status_code == 200 data = response.json() assert data["content"] == "Updated content" assert data["is_edited"] is True def test_delete_comment(self, client, admin_token, db, test_task): """Test deleting a comment (soft delete).""" comment = Comment( id=str(uuid.uuid4()), task_id=test_task.id, author_id="00000000-0000-0000-0000-000000000001", content="To be deleted", ) db.add(comment) db.commit() response = client.delete( f"/api/comments/{comment.id}", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 204 # Verify soft delete db.refresh(comment) assert comment.is_deleted is True def test_mention_limit(self, client, admin_token, test_task): """Test that @mention limit is enforced.""" # Create content with more than 10 mentions mentions = " ".join([f"@user{i}" for i in range(15)]) response = client.post( f"/api/tasks/{test_task.id}/comments", headers={"Authorization": f"Bearer {admin_token}"}, json={"content": f"Test with many mentions: {mentions}"}, ) assert response.status_code == 400 assert "10 mentions" in response.json()["detail"] class TestNotifications: """Tests for Notifications API.""" def test_list_notifications(self, client, admin_token, db): """Test listing notifications.""" # Create a notification notification = Notification( id=str(uuid.uuid4()), user_id="00000000-0000-0000-0000-000000000001", type="mention", reference_type="comment", reference_id=str(uuid.uuid4()), title="Test notification", message="Test message", ) db.add(notification) db.commit() response = client.get( "/api/notifications", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert data["total"] >= 1 assert data["unread_count"] >= 1 def test_mark_notification_as_read(self, client, admin_token, db): """Test marking a notification as read.""" notification = Notification( id=str(uuid.uuid4()), user_id="00000000-0000-0000-0000-000000000001", type="assignment", reference_type="task", reference_id=str(uuid.uuid4()), title="New assignment", ) db.add(notification) db.commit() response = client.put( f"/api/notifications/{notification.id}/read", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert data["is_read"] is True assert data["read_at"] is not None def test_mark_all_as_read(self, client, admin_token, db): """Test marking all notifications as read.""" # Create multiple unread notifications for i in range(3): notification = Notification( id=str(uuid.uuid4()), user_id="00000000-0000-0000-0000-000000000001", type="comment", reference_type="task", reference_id=str(uuid.uuid4()), title=f"Notification {i}", ) db.add(notification) db.commit() response = client.put( "/api/notifications/read-all", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert data["updated_count"] >= 3 def test_get_unread_count(self, client, admin_token, db): """Test getting unread notification count.""" # Create unread notifications for i in range(2): notification = Notification( id=str(uuid.uuid4()), user_id="00000000-0000-0000-0000-000000000001", type="blocker", reference_type="task", reference_id=str(uuid.uuid4()), title=f"Blocker {i}", ) db.add(notification) db.commit() response = client.get( "/api/notifications/unread-count", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert data["unread_count"] >= 2 class TestBlockers: """Tests for Blockers API.""" def test_create_blocker(self, client, admin_token, test_task): """Test creating a blocker.""" response = client.post( f"/api/tasks/{test_task.id}/blockers", headers={"Authorization": f"Bearer {admin_token}"}, json={"reason": "Waiting for external dependency"}, ) assert response.status_code == 201 data = response.json() assert data["reason"] == "Waiting for external dependency" assert data["resolved_at"] is None def test_resolve_blocker(self, client, admin_token, db, test_task): """Test resolving a blocker.""" blocker = Blocker( id=str(uuid.uuid4()), task_id=test_task.id, reported_by="00000000-0000-0000-0000-000000000001", reason="Test blocker", ) db.add(blocker) test_task.blocker_flag = True db.commit() response = client.put( f"/api/blockers/{blocker.id}/resolve", headers={"Authorization": f"Bearer {admin_token}"}, json={"resolution_note": "Issue resolved by updating config"}, ) assert response.status_code == 200 data = response.json() assert data["resolved_at"] is not None assert data["resolution_note"] == "Issue resolved by updating config" # Verify task blocker_flag is cleared db.refresh(test_task) assert test_task.blocker_flag is False def test_list_blockers(self, client, admin_token, db, test_task): """Test listing blockers for a task.""" blocker = Blocker( id=str(uuid.uuid4()), task_id=test_task.id, reported_by="00000000-0000-0000-0000-000000000001", reason="Test blocker", ) db.add(blocker) db.commit() response = client.get( f"/api/tasks/{test_task.id}/blockers", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert data["total"] == 1 assert data["blockers"][0]["reason"] == "Test blocker" def test_cannot_create_duplicate_active_blocker(self, client, admin_token, db, test_task): """Test that duplicate active blockers are prevented.""" # Create first blocker blocker = Blocker( id=str(uuid.uuid4()), task_id=test_task.id, reported_by="00000000-0000-0000-0000-000000000001", reason="First blocker", ) db.add(blocker) db.commit() # Try to create second blocker response = client.post( f"/api/tasks/{test_task.id}/blockers", headers={"Authorization": f"Bearer {admin_token}"}, json={"reason": "Second blocker"}, ) assert response.status_code == 400 assert "already has an unresolved blocker" in response.json()["detail"] class TestUserSearch: """Tests for User Search API.""" def test_search_users(self, client, admin_token, db): """Test searching users by name.""" # Create test users user1 = User( id=str(uuid.uuid4()), email="john@example.com", name="John Doe", is_active=True, ) user2 = User( id=str(uuid.uuid4()), email="jane@example.com", name="Jane Doe", is_active=True, ) db.add_all([user1, user2]) db.commit() response = client.get( "/api/users/search?q=Doe", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert len(data) >= 2 def test_search_users_by_email(self, client, admin_token, db): """Test searching users by email.""" user = User( id=str(uuid.uuid4()), email="searchtest@example.com", name="Search Test", is_active=True, ) db.add(user) db.commit() response = client.get( "/api/users/search?q=searchtest", headers={"Authorization": f"Bearer {admin_token}"}, ) assert response.status_code == 200 data = response.json() assert len(data) >= 1 assert any(u["email"] == "searchtest@example.com" for u in data)