From f6e117b1c301c275beea628429899a862881e3f0 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 24 Aug 2026 07:57:27 +0200 Subject: [PATCH] =?UTF-8?q?fix(d1):=20Calendar-Suite=20+=20ai=5Fproactive?= =?UTF-8?q?=20repariert=20=E2=80=94=20conftest=20CalendarPlugin-Import=20w?= =?UTF-8?q?iederhergestellt=20(abbe7a1-Regression),=20CalendarContract-Zug?= =?UTF-8?q?riffe=20snake=5Fcase=E2=86=92PascalCase=20(context=5Ftools,=20s?= =?UTF-8?q?ervices=20=C3=972,=20mail/routes),=202=20stale=20Rate-Limit-Tes?= =?UTF-8?q?ts=20auf=20zentrale=20check=5Frate=5Flimit-Grenze=20umgestellt;?= =?UTF-8?q?=20test=5Fcalendar=2034/34,=20ai=5Fproactive-Failures=20behoben?= =?UTF-8?q?;=20Mail-Vorbestand=20dokumentiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builtins/ai_proactive/context_tools.py | 4 +-- app/plugins/builtins/ai_proactive/services.py | 8 ++--- app/plugins/builtins/mail/routes.py | 4 +-- tests/conftest.py | 1 + tests/test_ai_proactive.py | 29 ++++++++++--------- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/plugins/builtins/ai_proactive/context_tools.py b/app/plugins/builtins/ai_proactive/context_tools.py index 9cfac28..76cd565 100644 --- a/app/plugins/builtins/ai_proactive/context_tools.py +++ b/app/plugins/builtins/ai_proactive/context_tools.py @@ -165,8 +165,8 @@ async def get_open_tasks_handler(arguments: dict[str, Any], context: dict[str, A from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract _cal = get_calendar_contract() - calendar_entry = _cal.calendar_entry - calendar_entry_link = _cal.calendar_entry_link + calendar_entry = _cal.CalendarEntry + calendar_entry_link = _cal.CalendarEntryLink db, tenant_id, _ = await _get_db_and_tenant(context) entity_type = arguments["entity_type"] diff --git a/app/plugins/builtins/ai_proactive/services.py b/app/plugins/builtins/ai_proactive/services.py index ea315f4..26b010c 100644 --- a/app/plugins/builtins/ai_proactive/services.py +++ b/app/plugins/builtins/ai_proactive/services.py @@ -274,8 +274,8 @@ async def gather_context( # Upcoming calendar events from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract _cal = get_calendar_contract() - calendar_entry = _cal.calendar_entry - calendar_entry_link = _cal.calendar_entry_link + calendar_entry = _cal.CalendarEntry + calendar_entry_link = _cal.CalendarEntryLink now = datetime.now(UTC) event_result = await db.execute( @@ -389,8 +389,8 @@ async def gather_context( # Upcoming events from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract _cal = get_calendar_contract() - calendar_entry = _cal.calendar_entry - calendar_entry_link = _cal.calendar_entry_link + calendar_entry = _cal.CalendarEntry + calendar_entry_link = _cal.CalendarEntryLink now = datetime.now(UTC) event_result = await db.execute( diff --git a/app/plugins/builtins/mail/routes.py b/app/plugins/builtins/mail/routes.py index e5bca52..aa6daba 100644 --- a/app/plugins/builtins/mail/routes.py +++ b/app/plugins/builtins/mail/routes.py @@ -1494,8 +1494,8 @@ async def create_event_from_mail( try: from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract _cal = get_calendar_contract() - calendar = _cal.calendar - calendar_entry = _cal.calendar_entry + calendar = _cal.Calendar + calendar_entry = _cal.CalendarEntry except ImportError: return {"created": False, "error": "calendar plugin not available"} cal_id = _parse_uuid(data.calendar_id, "calendar_id") diff --git a/tests/conftest.py b/tests/conftest.py index a35a2e0..8f1d67c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -53,6 +53,7 @@ from app.models.tenant import Tenant from app.models.user import User, UserTenant from app.models.user_preference import UserPreference # noqa: F401 from app.models.workflow import Workflow, WorkflowInstance, WorkflowStepHistory # noqa: F401 +from app.plugins.builtins.calendar import CalendarPlugin # noqa: F401 from app.models.outbox import EventOutbox # noqa: F401 from app.models.consumer_inbox import ConsumerInbox # noqa: F401 from app.models.saved_filter import SavedFilter # noqa: F401 diff --git a/tests/test_ai_proactive.py b/tests/test_ai_proactive.py index e0d6aae..cd4f0a7 100644 --- a/tests/test_ai_proactive.py +++ b/tests/test_ai_proactive.py @@ -15,6 +15,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest import pytest_asyncio +from fastapi import HTTPException from httpx import ASGITransport, AsyncClient from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker @@ -1104,14 +1105,11 @@ async def test_generate_suggestion_llm_failure(): @pytest.mark.asyncio @patch("app.plugins.builtins.ai_proactive.services.create_db_session") -async def test_handle_context_change_rate_limited(mock_create_session, redis_client): +async def test_handle_context_change_rate_limited(mock_create_session): """Rate-limit prevents suggestion generation.""" tenant_id = uuid.uuid4() user_id = uuid.uuid4() - # Pre-set rate limit key in Redis - await redis_client.setex(f"ai_proactive:rate:{user_id}", 10, "1") - mock_session = AsyncMock() mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=None) @@ -1127,8 +1125,14 @@ async def test_handle_context_change_rate_limited(mock_create_session, redis_cli rate_limit_seconds=10, model="ollama/deepseek-v4", ) + # Since bb36378 the rate limit goes through app.core.rate_limit.check_rate_limit; + # simulate an exceeded limit instead of patching the removed services.get_cache. with ( - patch("app.plugins.builtins.ai_proactive.services.get_cache", return_value=redis_client), + patch( + "app.core.rate_limit.check_rate_limit", + new_callable=AsyncMock, + side_effect=HTTPException(status_code=429), + ), patch( "app.plugins.builtins.ai_proactive.services.get_user_settings", new_callable=AsyncMock, @@ -1147,7 +1151,7 @@ async def test_handle_context_change_rate_limited(mock_create_session, redis_cli @pytest.mark.asyncio @patch("app.plugins.builtins.ai_proactive.services.create_db_session") -async def test_handle_context_change_disabled(mock_create_session, redis_client): +async def test_handle_context_change_disabled(mock_create_session): """Settings disabled → no suggestion.""" tenant_id = uuid.uuid4() user_id = uuid.uuid4() @@ -1167,13 +1171,12 @@ async def test_handle_context_change_disabled(mock_create_session, redis_client) rate_limit_seconds=10, model="ollama/deepseek-v4", ) - with ( - patch("app.plugins.builtins.ai_proactive.services.get_cache", return_value=redis_client), - patch( - "app.plugins.builtins.ai_proactive.services.get_user_settings", - new_callable=AsyncMock, - return_value=disabled_settings, - ), + # Disabled settings cause an early return before any Redis access; + # no cache/rate-limit patch needed since bb36378. + with patch( + "app.plugins.builtins.ai_proactive.services.get_user_settings", + new_callable=AsyncMock, + return_value=disabled_settings, ): await handle_context_change({ "user_id": str(user_id),