""" Calendar Service Tests """ import pytest from unittest.mock import Mock, patch, MagicMock from datetime import datetime, timedelta import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from main import app, CalendarToolRequest, CreateEventRequest, ConnectRequest from storage import CalendarStorage, CalendarAccount, CalendarReminder from calendar_client import CalDAVClient class TestCalendarStorage: """Test CalendarStorage""" def test_create_account(self): """Test creating calendar account""" storage = CalendarStorage() account = storage.create_account( workspace_id="ws1", user_id="user1", provider="radicale", username="testuser", password="testpass", principal_url="/testuser/", default_calendar_id="default" ) assert account.id.startswith("acc_") assert account.workspace_id == "ws1" assert account.user_id == "user1" assert account.provider == "radicale" assert account.username == "testuser" def test_get_account(self): """Test getting account by ID""" storage = CalendarStorage() account = storage.create_account( workspace_id="ws1", user_id="user1", provider="radicale", username="testuser", password="testpass" ) retrieved = storage.get_account(account.id) assert retrieved is not None assert retrieved.id == account.id def test_list_accounts(self): """Test listing accounts for user""" storage = CalendarStorage() storage.create_account("ws1", "user1", "radicale", "user1", "pass1") storage.create_account("ws1", "user1", "google", "user1@gmail.com", "pass2") storage.create_account("ws1", "user2", "radicale", "user2", "pass3") accounts = storage.list_accounts("ws1", "user1") assert len(accounts) == 2 def test_create_reminder(self): """Test creating reminder""" storage = CalendarStorage() account = storage.create_account( workspace_id="ws1", user_id="user1", provider="radicale", username="testuser", password="testpass" ) reminder = storage.create_reminder( workspace_id="ws1", user_id="user1", account_id=account.id, event_uid="evt123", remind_at=(datetime.utcnow() + timedelta(hours=1)).isoformat(), channel="inapp" ) assert reminder.id.startswith("rem_") assert reminder.event_uid == "evt123" assert reminder.status == "pending" def test_idempotency_key(self): """Test idempotency key storage""" storage = CalendarStorage() storage.store_idempotency_key( key="unique-key-123", workspace_id="ws1", user_id="user1", event_uid="evt123" ) result = storage.get_by_idempotency_key("unique-key-123") assert result is not None assert result["event_uid"] == "evt123" class TestCalDAVClient: """Test CalDAV Client""" def test_client_init(self): """Test client initialization""" client = CalDAVClient( server_url="https://caldav.example.com", username="testuser", password="testpass" ) assert client.server_url == "https://caldav.example.com" assert client.username == "testuser" assert client.principal_url is None def test_discover_principal(self): """Test principal discovery""" client = CalDAVClient( server_url="https://caldav.example.com", username="testuser", password="testpass" ) with patch.object(client, '_request') as mock_request: mock_response = Mock() mock_response.status_code = 207 mock_request.return_value = mock_response principal = client.discover_principal() assert principal == "/testuser/" def test_build_vevent(self): """Test VEVENT building""" client = CalDAVClient( server_url="https://caldav.example.com", username="testuser", password="testpass" ) vevent = client._build_vevent( uid="test-uid-123", title="Test Event", start="2024-01-15T10:00:00", end="2024-01-15T11:00:00", timezone="Europe/Kiev", location="Office", description="Test description", attendees=["test@example.com"] ) assert "BEGIN:VCALENDAR" in vevent assert "BEGIN:VEVENT" in vevent assert "SUMMARY:Test Event" in vevent assert "LOCATION:Office" in vevent assert "ATTENDEE:mailto:test@example.com" in vevent assert "UID:test-uid-123" in vevent def test_parse_vevent(self): """Test VEVENT parsing""" client = CalDAVClient( server_url="https://caldav.example.com", username="testuser", password="testpass" ) ics_data = """BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:test-uid-456 SUMMARY:Parsed Event DTSTART:20240115T100000 DTEND:20240115T110000 LOCATION:Home DESCRIPTION:Test description END:VEVENT END:VCALENDAR""" event = client._parse_vevent(ics_data) assert event["uid"] == "test-uid-456" assert event["title"] == "Parsed Event" assert event["location"] == "Home" class TestCalendarToolEndpoint: """Test calendar tool API endpoint""" @pytest.fixture def client(self): """Test client fixture""" from fastapi.testclient import TestClient return TestClient(app) def test_health_check(self, client): """Test health endpoint""" response = client.get("/health") assert response.status_code == 200 assert response.json()["status"] == "healthy" def test_metrics(self, client): """Test metrics endpoint""" response = client.get("/metrics") assert response.status_code == 200 data = response.json() assert "accounts_count" in data assert "reminders_pending" in data @patch('main.CalDAVClient') def test_connect_radicale(self, mock_caldav_class, client): """Test connecting Radicale account""" mock_client = Mock() mock_client.list_calendars.return_value = [ {"id": "default", "display_name": "Default Calendar"} ] mock_client.principal_url = "/testuser/" mock_caldav_class.return_value = mock_client response = client.post( "/v1/calendar/connect/radicale", json={ "workspace_id": "ws1", "user_id": "user1", "username": "testuser", "password": "testpass" } ) assert response.status_code == 200 data = response.json() assert data["status"] == "connected" assert "account_id" in data if __name__ == "__main__": pytest.main([__file__, "-v"])