Skip to main content
Glama
cbcoutinho

Nextcloud MCP Server

by cbcoutinho
test_calendar_operations.py13.9 kB
"""Integration tests for Calendar CalDAV operations. Note: These tests use the shared temporary_calendar fixture from conftest.py which reuses a session-scoped calendar to avoid Nextcloud rate limiting issues. Each test cleans up its own events/todos but shares the same calendar. """ 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_event(nc_client: NextcloudClient, temporary_calendar: str): """Create a temporary event for testing and clean up afterward. Uses the shared temporary_calendar fixture from conftest.py which reuses a session-scoped calendar to avoid Nextcloud rate limiting. """ event_uid = None calendar_name = temporary_calendar # Create a test event tomorrow = datetime.now() + timedelta(days=1) event_data = { "title": f"Test Event {uuid.uuid4().hex[:8]}", "start_datetime": tomorrow.strftime("%Y-%m-%dT14:00:00"), "end_datetime": tomorrow.strftime("%Y-%m-%dT15:00:00"), "description": "Test event created by integration tests", "location": "Test Location", "categories": "testing", "status": "CONFIRMED", "priority": 5, } try: logger.info(f"Creating temporary event in calendar: {calendar_name}") result = await nc_client.calendar.create_event(calendar_name, event_data) event_uid = result.get("uid") if not event_uid: pytest.fail("Failed to create temporary event") logger.info(f"Created temporary event with UID: {event_uid}") yield {"uid": event_uid, "calendar_name": calendar_name, "data": event_data} finally: # Cleanup if event_uid: try: logger.info(f"Cleaning up temporary event: {event_uid}") await nc_client.calendar.delete_event(calendar_name, event_uid) logger.info(f"Successfully deleted temporary event: {event_uid}") except HTTPStatusError as e: if e.response.status_code != 404: logger.error(f"Error deleting temporary event {event_uid}: {e}") except Exception as e: logger.error( f"Unexpected error deleting temporary event {event_uid}: {e}" ) async def test_list_calendars(nc_client: NextcloudClient): """Test listing available calendars.""" calendars = await nc_client.calendar.list_calendars() assert isinstance(calendars, list) if not calendars: pytest.skip("No calendars available - Calendar app may not be enabled") logger.info(f"Found {len(calendars)} calendars") # Check structure of calendars for calendar in calendars: assert "name" in calendar assert "display_name" in calendar assert "href" in calendar # Optional fields assert "description" in calendar assert "color" in calendar logger.info(f"Calendar: {calendar['name']} - {calendar['display_name']}") async def test_create_and_delete_event( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating and deleting a basic event.""" calendar_name = temporary_calendar # Create event tomorrow = datetime.now() + timedelta(days=1) event_data = { "title": "Integration Test Event", "start_datetime": tomorrow.strftime("%Y-%m-%dT10:00:00"), "end_datetime": tomorrow.strftime("%Y-%m-%dT11:00:00"), "description": "Test event for integration testing", "location": "Test Room", "categories": "testing,integration", "status": "CONFIRMED", "priority": 3, } try: result = await nc_client.calendar.create_event(calendar_name, event_data) assert "uid" in result assert result["status_code"] in [200, 201, 204] event_uid = result["uid"] logger.info(f"Created event with UID: {event_uid}") # Verify event was created by retrieving it retrieved_event, etag = await nc_client.calendar.get_event( calendar_name, event_uid ) assert retrieved_event["uid"] == event_uid assert retrieved_event["title"] == "Integration Test Event" assert retrieved_event["location"] == "Test Room" # Delete event delete_result = await nc_client.calendar.delete_event(calendar_name, event_uid) assert delete_result["status_code"] in [200, 204, 404] logger.info(f"Successfully deleted event: {event_uid}") except Exception as e: logger.error(f"Test failed: {e}") raise async def test_create_all_day_event( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating an all-day event.""" calendar_name = temporary_calendar tomorrow = datetime.now() + timedelta(days=1) event_data = { "title": "All Day Test Event", "start_datetime": tomorrow.strftime("%Y-%m-%d"), "all_day": True, "description": "Test all-day event", "categories": "testing", } try: result = await nc_client.calendar.create_event(calendar_name, event_data) event_uid = result["uid"] logger.info(f"Created all-day event with UID: {event_uid}") # Verify event retrieved_event, _ = await nc_client.calendar.get_event( calendar_name, event_uid ) assert retrieved_event["title"] == "All Day Test Event" assert retrieved_event.get("all_day") is True # Cleanup await nc_client.calendar.delete_event(calendar_name, event_uid) except Exception as e: logger.error(f"All-day event test failed: {e}") raise async def test_create_recurring_event( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating a recurring event.""" calendar_name = temporary_calendar tomorrow = datetime.now() + timedelta(days=1) event_data = { "title": "Weekly Recurring Test", "start_datetime": tomorrow.strftime("%Y-%m-%dT14:00:00"), "end_datetime": tomorrow.strftime("%Y-%m-%dT15:00:00"), "description": "Test recurring event", "recurring": True, "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "reminder_minutes": 30, } try: result = await nc_client.calendar.create_event(calendar_name, event_data) event_uid = result["uid"] logger.info(f"Created recurring event with UID: {event_uid}") # Verify event retrieved_event, _ = await nc_client.calendar.get_event( calendar_name, event_uid ) assert retrieved_event["title"] == "Weekly Recurring Test" assert retrieved_event.get("recurring") is True # Cleanup await nc_client.calendar.delete_event(calendar_name, event_uid) except Exception as e: logger.error(f"Recurring event test failed: {e}") raise async def test_list_events_in_range(nc_client: NextcloudClient, temporary_event: dict): """Test listing events within a date range.""" calendar_name = temporary_event["calendar_name"] # Get events for the next week start_datetime = datetime.now() end_datetime = datetime.now() + timedelta(days=7) events = await nc_client.calendar.get_calendar_events( calendar_name=calendar_name, start_datetime=start_datetime, end_datetime=end_datetime, limit=50, ) assert isinstance(events, list) logger.info(f"Found {len(events)} events in date range") # Our temporary event should be in the list event_uids = [event.get("uid") for event in events] assert temporary_event["uid"] in event_uids # Check event structure for event in events: assert "uid" in event assert "title" in event assert "start_datetime" in event async def test_update_event(nc_client: NextcloudClient, temporary_event: dict): """Test updating an existing event.""" calendar_name = temporary_event["calendar_name"] event_uid = temporary_event["uid"] # Update event data updated_data = { "title": "Updated Test Event Title", "description": "Updated description for test event", "location": "Updated Location", "priority": 1, # High priority } try: result = await nc_client.calendar.update_event( calendar_name, event_uid, updated_data ) assert result["uid"] == event_uid # Verify updates updated_event, _ = await nc_client.calendar.get_event(calendar_name, event_uid) assert updated_event["title"] == "Updated Test Event Title" assert updated_event["description"] == "Updated description for test event" assert updated_event["location"] == "Updated Location" assert updated_event["priority"] == 1 logger.info(f"Successfully updated event: {event_uid}") except Exception as e: logger.error(f"Event update test failed: {e}") raise async def test_create_event_with_attendees( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating an event with attendees.""" calendar_name = temporary_calendar tomorrow = datetime.now() + timedelta(days=1) event_data = { "title": "Meeting with Attendees", "start_datetime": tomorrow.strftime("%Y-%m-%dT16:00:00"), "end_datetime": tomorrow.strftime("%Y-%m-%dT17:00:00"), "description": "Test meeting with multiple attendees", "location": "Conference Room A", "attendees": "test1@example.com,test2@example.com", "reminder_minutes": 15, "status": "TENTATIVE", } try: result = await nc_client.calendar.create_event(calendar_name, event_data) event_uid = result["uid"] logger.info(f"Created event with attendees, UID: {event_uid}") # Verify event retrieved_event, _ = await nc_client.calendar.get_event( calendar_name, event_uid ) assert retrieved_event["title"] == "Meeting with Attendees" assert "test1@example.com" in retrieved_event.get("attendees", "") assert retrieved_event["status"] == "TENTATIVE" # Cleanup await nc_client.calendar.delete_event(calendar_name, event_uid) except Exception as e: logger.error(f"Event with attendees test failed: {e}") raise async def test_get_nonexistent_event( nc_client: NextcloudClient, temporary_calendar: str ): """Test retrieving a non-existent event.""" calendar_name = temporary_calendar fake_uid = f"nonexistent-{uuid.uuid4()}" # caldav library raises generic Exception for missing events, not HTTPStatusError with pytest.raises(Exception, match="not found"): await nc_client.calendar.get_event(calendar_name, fake_uid) logger.info(f"Correctly raised exception for nonexistent event: {fake_uid}") async def test_delete_nonexistent_event( nc_client: NextcloudClient, temporary_calendar: str ): """Test deleting a non-existent event.""" calendar_name = temporary_calendar fake_uid = f"nonexistent-{uuid.uuid4()}" result = await nc_client.calendar.delete_event(calendar_name, fake_uid) assert result["status_code"] == 404 logger.info(f"Correctly got 404 for deleting nonexistent event: {fake_uid}") async def test_event_with_url_and_categories( nc_client: NextcloudClient, temporary_calendar: str ): """Test creating an event with URL and multiple categories.""" calendar_name = temporary_calendar tomorrow = datetime.now() + timedelta(days=1) event_data = { "title": "Event with URL and Categories", "start_datetime": tomorrow.strftime("%Y-%m-%dT09:00:00"), "end_datetime": tomorrow.strftime("%Y-%m-%dT10:30:00"), "description": "Test event with additional metadata", "categories": "work,meeting,important,quarterly", "url": "https://zoom.us/j/123456789", "privacy": "PRIVATE", "priority": 2, } try: result = await nc_client.calendar.create_event(calendar_name, event_data) event_uid = result["uid"] logger.info(f"Created event with metadata, UID: {event_uid}") # Verify event retrieved_event, _ = await nc_client.calendar.get_event( calendar_name, event_uid ) assert retrieved_event["title"] == "Event with URL and Categories" assert "work" in retrieved_event.get("categories", "") assert "important" in retrieved_event.get("categories", "") assert retrieved_event.get("url") == "https://zoom.us/j/123456789" assert retrieved_event.get("privacy") == "PRIVATE" assert retrieved_event.get("priority") == 2 # Cleanup await nc_client.calendar.delete_event(calendar_name, event_uid) except Exception as e: logger.error(f"Event with metadata test failed: {e}") raise async def test_calendar_operations_error_handling( nc_client: NextcloudClient, ): """Test error handling for calendar operations.""" # Test with non-existent calendar fake_calendar = f"nonexistent_calendar_{uuid.uuid4().hex}" # caldav library returns empty list for non-existent calendars, doesn't raise # Testing that it doesn't crash and returns empty results events = await nc_client.calendar.get_calendar_events(fake_calendar) assert isinstance(events, list) # Empty list is expected for non-existent calendar assert len(events) == 0 logger.info("Error handling tests completed successfully")

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