You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
simple-agent/tests/test_calendar_meeting_inter...

212 lines
6.0 KiB
Python

from __future__ import annotations
from datetime import date
import pytest
import app.calendar_agent as calendar_module
from app.calendar_agent import CalendarAvailabilityAgent
class _FakeFreeBusy:
def __init__(self, payload: dict[str, object]) -> None:
self.payload = payload
def query(self, body: dict[str, object]) -> _FakeFreeBusy:
return self
def execute(self) -> dict[str, object]:
return self.payload
class _FakeCalendarService:
def __init__(self, payload: dict[str, object]) -> None:
self._freebusy = _FakeFreeBusy(payload)
def freebusy(self) -> _FakeFreeBusy:
return self._freebusy
def _build_agent(payload: dict[str, object]) -> CalendarAvailabilityAgent:
return CalendarAvailabilityAgent(calendar_service=_FakeCalendarService(payload))
def test_weekday_boundaries_and_busy_subtraction() -> None:
agent = _build_agent(
{
"calendars": {
"primary": {
"busy": [
{
"start": "2026-03-09T09:00:00+01:00",
"end": "2026-03-09T10:00:00+01:00",
}
]
},
"work": {
"busy": [
{
"start": "2026-03-09T09:30:00+01:00",
"end": "2026-03-09T11:00:00+01:00",
},
{
"start": "2026-03-09T12:00:00+01:00",
"end": "2026-03-09T12:20:00+01:00",
},
]
},
}
}
)
result = agent.get_available_meeting_intervals(
start="2026-03-09T07:00:00+01:00",
end="2026-03-09T22:00:00+01:00",
calendar_ids=["primary", "work"],
)
assert result.timezone == "Europe/Paris"
assert result.checked_calendars == ["primary", "work"]
assert result.meeting_intervals == [
{
"start": "2026-03-09T08:30:00+01:00",
"end": "2026-03-09T09:00:00+01:00",
},
{
"start": "2026-03-09T11:00:00+01:00",
"end": "2026-03-09T12:00:00+01:00",
},
{
"start": "2026-03-09T12:20:00+01:00",
"end": "2026-03-09T21:30:00+01:00",
},
]
def test_saturday_window_only() -> None:
agent = _build_agent({"calendars": {"primary": {"busy": []}}})
result = agent.get_available_meeting_intervals(
start="2026-03-14T08:00:00+01:00",
end="2026-03-14T13:00:00+01:00",
calendar_ids=None,
)
assert result.meeting_intervals == [
{
"start": "2026-03-14T09:30:00+01:00",
"end": "2026-03-14T12:00:00+01:00",
}
]
def test_sunday_excluded() -> None:
agent = _build_agent({"calendars": {"primary": {"busy": []}}})
result = agent.get_available_meeting_intervals(
start="2026-03-15T08:00:00+01:00",
end="2026-03-15T14:00:00+01:00",
calendar_ids=None,
)
assert result.meeting_intervals == []
def test_france_holiday_excluded(monkeypatch) -> None:
def _fake_country_holidays(
country: str,
*,
years: range,
observed: bool,
subdiv: str | None = None,
) -> dict[date, str]:
assert observed is False
if country == "FR":
return {date(2026, 7, 14): "Bastille Day"}
if country == "CA" and subdiv == "QC":
return {}
return {}
monkeypatch.setattr(calendar_module.holidays, "country_holidays", _fake_country_holidays)
agent = _build_agent({"calendars": {"primary": {"busy": []}}})
result = agent.get_available_meeting_intervals(
start="2026-07-14T08:00:00+02:00",
end="2026-07-14T22:00:00+02:00",
calendar_ids=None,
)
assert result.meeting_intervals == []
def test_quebec_holiday_excluded(monkeypatch) -> None:
def _fake_country_holidays(
country: str,
*,
years: range,
observed: bool,
subdiv: str | None = None,
) -> dict[date, str]:
assert observed is False
if country == "FR":
return {}
if country == "CA" and subdiv == "QC":
return {date(2026, 6, 24): "Saint-Jean-Baptiste Day"}
return {}
monkeypatch.setattr(calendar_module.holidays, "country_holidays", _fake_country_holidays)
agent = _build_agent({"calendars": {"primary": {"busy": []}}})
result = agent.get_available_meeting_intervals(
start="2026-06-24T08:00:00+02:00",
end="2026-06-24T22:00:00+02:00",
calendar_ids=None,
)
assert result.meeting_intervals == []
def test_intervals_shorter_than_30_minutes_are_filtered() -> None:
agent = _build_agent(
{
"calendars": {
"primary": {
"busy": [
{
"start": "2026-03-09T08:40:00+01:00",
"end": "2026-03-09T09:30:00+01:00",
}
]
}
}
}
)
result = agent.get_available_meeting_intervals(
start="2026-03-09T08:30:00+01:00",
end="2026-03-09T09:30:00+01:00",
calendar_ids=None,
)
assert result.meeting_intervals == []
def test_range_greater_than_90_days_rejected() -> None:
agent = _build_agent({"calendars": {"primary": {"busy": []}}})
with pytest.raises(ValueError, match="cannot exceed 90 days"):
agent.get_available_meeting_intervals(
start="2026-01-01T08:00:00+01:00",
end="2026-04-02T08:00:01+02:00",
calendar_ids=None,
)
def test_requires_timezone_offset() -> None:
agent = _build_agent({"calendars": {"primary": {"busy": []}}})
with pytest.raises(ValueError, match="timezone offset"):
agent.get_available_meeting_intervals(
start="2026-03-10T08:00:00",
end="2026-03-10T10:00:00+01:00",
calendar_ids=None,
)