Skip to main content
Glama
cbcoutinho

Nextcloud MCP Server

by cbcoutinho
test_task_operations.py16.6 kB
"""Integration tests for Calendar VTODO (task) operations.""" import logging import uuid from datetime import datetime, timedelta import pytest from httpx import HTTPStatusError from nextcloud_mcp_server.client import NextcloudClient logger = logging.getLogger(__name__) # Mark all tests in this module as integration tests pytestmark = pytest.mark.integration @pytest.fixture async def temporary_todo(nc_client: NextcloudClient, temporary_calendar: str): """Create a temporary todo for testing and clean up afterward.""" todo_uid = None calendar_name = temporary_calendar # Create a test todo tomorrow = datetime.now() + timedelta(days=1) todo_data = { "summary": f"Test Task {uuid.uuid4().hex[:8]}", "description": "Test todo created by integration tests", "status": "NEEDS-ACTION", "priority": 5, "due": tomorrow.strftime("%Y-%m-%dT18:00:00"), "categories": "testing", } try: logger.info(f"Creating temporary todo in calendar: {calendar_name}") result = await nc_client.calendar.create_todo(calendar_name, todo_data) todo_uid = result.get("uid") if not todo_uid: pytest.fail("Failed to create temporary todo") logger.info(f"Created temporary todo with UID: {todo_uid}") yield {"uid": todo_uid, "calendar_name": calendar_name, "data": todo_data} finally: # Cleanup if todo_uid: try: logger.info(f"Cleaning up temporary todo: {todo_uid}") await nc_client.calendar.delete_todo(calendar_name, todo_uid) logger.info(f"Successfully deleted temporary todo: {todo_uid}") except HTTPStatusError as e: if e.response.status_code != 404: logger.error(f"Error deleting temporary todo {todo_uid}: {e}") except Exception as e: logger.error( f"Unexpected error deleting temporary todo {todo_uid}: {e}" ) # ============= Basic CRUD Tests ============= async def test_create_and_delete_todo( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating and deleting a basic todo.""" calendar_name = temporary_calendar # Create todo tomorrow = datetime.now() + timedelta(days=1) todo_data = { "summary": "Integration Test Task", "description": "Test task for integration testing", "status": "NEEDS-ACTION", "priority": 3, "due": tomorrow.strftime("%Y-%m-%dT18:00:00"), "categories": "testing,integration", } try: result = await nc_client.calendar.create_todo(calendar_name, todo_data) assert "uid" in result assert result["status_code"] in [200, 201, 204] todo_uid = result["uid"] logger.info(f"Created todo with UID: {todo_uid}") # Verify todo was created by listing todos todos = await nc_client.calendar.list_todos(calendar_name) todo_uids = [todo.get("uid") for todo in todos] assert todo_uid in todo_uids # Find our todo in the list our_todo = next((t for t in todos if t.get("uid") == todo_uid), None) assert our_todo is not None assert our_todo["summary"] == "Integration Test Task" assert our_todo["status"] == "NEEDS-ACTION" assert our_todo["priority"] == 3 # Delete todo delete_result = await nc_client.calendar.delete_todo(calendar_name, todo_uid) assert delete_result["status_code"] in [200, 204, 404] logger.info(f"Successfully deleted todo: {todo_uid}") except Exception as e: logger.error(f"Test failed: {e}") raise async def test_list_todos(nc_client: NextcloudClient, temporary_calendar: str): """Test listing todos in a calendar.""" calendar_name = temporary_calendar # Create multiple todos todo_uids = [] for i in range(3): todo_data = { "summary": f"Test Task {i + 1}", "description": f"Task number {i + 1}", "status": "NEEDS-ACTION", "priority": i + 1, } result = await nc_client.calendar.create_todo(calendar_name, todo_data) todo_uids.append(result["uid"]) try: # List todos todos = await nc_client.calendar.list_todos(calendar_name) assert isinstance(todos, list) assert len(todos) >= 3 # At least our 3 todos # Check structure for todo in todos: assert "uid" in todo assert "summary" in todo assert "status" in todo assert "priority" in todo # Verify our todos are in the list listed_uids = [todo["uid"] for todo in todos] for uid in todo_uids: assert uid in listed_uids logger.info(f"Found {len(todos)} todos in calendar") finally: # Cleanup for uid in todo_uids: try: await nc_client.calendar.delete_todo(calendar_name, uid) except Exception: pass async def test_update_todo(nc_client: NextcloudClient, temporary_todo: dict): """Test updating an existing todo.""" calendar_name = temporary_todo["calendar_name"] todo_uid = temporary_todo["uid"] # Update todo data updated_data = { "summary": "Updated Test Task Title", "description": "Updated description for test task", "status": "IN-PROCESS", "priority": 1, # High priority "percent_complete": 50, } try: result = await nc_client.calendar.update_todo( calendar_name, todo_uid, updated_data ) assert result["uid"] == todo_uid # Verify updates by listing todos todos = await nc_client.calendar.list_todos(calendar_name) updated_todo = next((t for t in todos if t["uid"] == todo_uid), None) assert updated_todo is not None assert updated_todo["summary"] == "Updated Test Task Title" assert updated_todo["description"] == "Updated description for test task" assert updated_todo["status"] == "IN-PROCESS" assert updated_todo["priority"] == 1 assert updated_todo["percent_complete"] == 50 logger.info(f"Successfully updated todo: {todo_uid}") except Exception as e: logger.error(f"Todo update test failed: {e}") raise async def test_todo_with_dates(nc_client: NextcloudClient, temporary_calendar: str): """Test creating a todo with start, due, and completed dates.""" calendar_name = temporary_calendar now = datetime.now() start_date = now + timedelta(days=1) due_date = now + timedelta(days=7) todo_data = { "summary": "Task with Dates", "description": "Test task with various date fields", "status": "NEEDS-ACTION", "dtstart": start_date.strftime("%Y-%m-%dT09:00:00"), "due": due_date.strftime("%Y-%m-%dT17:00:00"), } try: result = await nc_client.calendar.create_todo(calendar_name, todo_data) todo_uid = result["uid"] logger.info(f"Created todo with dates, UID: {todo_uid}") # Verify dates todos = await nc_client.calendar.list_todos(calendar_name) created_todo = next((t for t in todos if t["uid"] == todo_uid), None) assert created_todo is not None assert created_todo["summary"] == "Task with Dates" assert "dtstart" in created_todo assert "due" in created_todo # Cleanup await nc_client.calendar.delete_todo(calendar_name, todo_uid) except Exception as e: logger.error(f"Date handling test failed: {e}") raise # ============= Advanced Feature Tests ============= async def test_todo_status_transitions( nc_client: NextcloudClient, temporary_calendar: str ): """Test transitioning through different todo statuses.""" calendar_name = temporary_calendar todo_data = { "summary": "Status Transition Test", "description": "Testing status changes", "status": "NEEDS-ACTION", } result = await nc_client.calendar.create_todo(calendar_name, todo_data) todo_uid = result["uid"] try: # Transition: NEEDS-ACTION → IN-PROCESS await nc_client.calendar.update_todo( calendar_name, todo_uid, {"status": "IN-PROCESS", "percent_complete": 25}, ) todos = await nc_client.calendar.list_todos(calendar_name) todo = next((t for t in todos if t["uid"] == todo_uid), None) assert todo["status"] == "IN-PROCESS" assert todo["percent_complete"] == 25 # Transition: IN-PROCESS → COMPLETED completed_time = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") await nc_client.calendar.update_todo( calendar_name, todo_uid, { "status": "COMPLETED", "percent_complete": 100, "completed": completed_time, }, ) todos = await nc_client.calendar.list_todos(calendar_name) todo = next((t for t in todos if t["uid"] == todo_uid), None) assert todo["status"] == "COMPLETED" assert todo["percent_complete"] == 100 assert "completed" in todo logger.info(f"Successfully transitioned todo through statuses: {todo_uid}") finally: await nc_client.calendar.delete_todo(calendar_name, todo_uid) async def test_todo_priority_levels( nc_client: NextcloudClient, temporary_calendar: str ): """Test different priority levels (0=undefined, 1=highest, 9=lowest).""" calendar_name = temporary_calendar priorities = [0, 1, 5, 9] priority_labels = {0: "Undefined", 1: "Highest", 5: "Medium", 9: "Lowest"} todo_uids = [] try: # Create todos with different priorities for priority in priorities: todo_data = { "summary": f"Priority {priority} Task ({priority_labels[priority]})", "status": "NEEDS-ACTION", "priority": priority, } result = await nc_client.calendar.create_todo(calendar_name, todo_data) todo_uids.append((result["uid"], priority)) # Verify all priorities todos = await nc_client.calendar.list_todos(calendar_name) for uid, expected_priority in todo_uids: todo = next((t for t in todos if t["uid"] == uid), None) assert todo is not None assert todo["priority"] == expected_priority logger.info(f"Successfully tested priority levels: {priorities}") finally: # Cleanup for uid, _ in todo_uids: try: await nc_client.calendar.delete_todo(calendar_name, uid) except Exception: pass async def test_todo_with_categories( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating a todo with multiple categories.""" calendar_name = temporary_calendar todo_data = { "summary": "Task with Categories", "description": "Testing category support", "status": "NEEDS-ACTION", "categories": "work,meeting,important,quarterly", } try: result = await nc_client.calendar.create_todo(calendar_name, todo_data) todo_uid = result["uid"] logger.info(f"Created todo with categories, UID: {todo_uid}") # Verify categories todos = await nc_client.calendar.list_todos(calendar_name) created_todo = next((t for t in todos if t["uid"] == todo_uid), None) assert created_todo is not None assert "categories" in created_todo categories_str = created_todo["categories"] assert "work" in categories_str assert "meeting" in categories_str assert "important" in categories_str assert "quarterly" in categories_str # Cleanup await nc_client.calendar.delete_todo(calendar_name, todo_uid) except Exception as e: logger.error(f"Categories test failed: {e}") raise async def test_search_todos_across_calendars( nc_client: NextcloudClient, temporary_calendar: str, shared_calendar_2: str ): """Test searching for todos across multiple calendars. Uses two shared test calendars to avoid rate limiting. """ # Use existing shared calendars to avoid rate limits cal1_name = temporary_calendar # First shared test calendar cal2_name = shared_calendar_2 # Second shared test calendar try: # Create todos in both calendars todo1_data = {"summary": "Task in Calendar 1", "status": "NEEDS-ACTION"} todo2_data = {"summary": "Task in Calendar 2", "status": "IN-PROCESS"} result1 = await nc_client.calendar.create_todo(cal1_name, todo1_data) result2 = await nc_client.calendar.create_todo(cal2_name, todo2_data) # Search across all calendars all_todos = await nc_client.calendar.search_todos_across_calendars() assert isinstance(all_todos, list) # Find our todos todo1 = next((t for t in all_todos if t["uid"] == result1["uid"]), None) todo2 = next((t for t in all_todos if t["uid"] == result2["uid"]), None) assert todo1 is not None assert todo2 is not None assert "calendar_name" in todo1 assert "calendar_name" in todo2 assert todo1["calendar_name"] == cal1_name assert todo2["calendar_name"] == cal2_name logger.info(f"Found {len(all_todos)} todos across all calendars") finally: # Cleanup: Delete only the todos we created (calendars are reused/built-in) try: await nc_client.calendar.delete_todo(cal1_name, result1["uid"]) except Exception: pass try: await nc_client.calendar.delete_todo(cal2_name, result2["uid"]) except Exception: pass # ============= Edge Case Tests ============= async def test_get_nonexistent_todo( nc_client: NextcloudClient, temporary_calendar: str ): """Test attempting to retrieve a non-existent todo.""" calendar_name = temporary_calendar fake_uid = f"nonexistent-{uuid.uuid4()}" # List todos to ensure it doesn't exist todos = await nc_client.calendar.list_todos(calendar_name) matching_todos = [t for t in todos if t.get("uid") == fake_uid] assert len(matching_todos) == 0 logger.info(f"Verified nonexistent todo UID: {fake_uid}") async def test_delete_nonexistent_todo( nc_client: NextcloudClient, temporary_calendar: str ): """Test deleting a non-existent todo.""" calendar_name = temporary_calendar fake_uid = f"nonexistent-{uuid.uuid4()}" result = await nc_client.calendar.delete_todo(calendar_name, fake_uid) assert result["status_code"] == 404 logger.info(f"Correctly got 404 for deleting nonexistent todo: {fake_uid}") async def test_list_todos_with_filters( nc_client: NextcloudClient, temporary_calendar: str ): """Test listing todos with various filters.""" calendar_name = temporary_calendar # Create todos with different statuses and priorities test_todos = [ { "summary": "High Priority Task", "status": "NEEDS-ACTION", "priority": 1, "categories": "urgent", }, { "summary": "In Progress Task", "status": "IN-PROCESS", "priority": 5, "categories": "work", }, { "summary": "Low Priority Task", "status": "NEEDS-ACTION", "priority": 9, "categories": "someday", }, ] created_uids = [] try: # Create test todos for todo_data in test_todos: result = await nc_client.calendar.create_todo(calendar_name, todo_data) created_uids.append(result["uid"]) # Test basic list without filters all_todos = await nc_client.calendar.list_todos(calendar_name) assert len(all_todos) >= 3 # Verify all our todos are in the list our_todo_uids = [t["uid"] for t in all_todos if t["uid"] in created_uids] assert len(our_todo_uids) == 3 logger.info(f"Successfully created and listed {len(created_uids)} test todos") finally: # Cleanup for uid in created_uids: try: await nc_client.calendar.delete_todo(calendar_name, uid) except Exception: pass

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cbcoutinho/nextcloud-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server