You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue was AI-generated using Claude Code with Sonnet 4.6
Prompt: The sync test suite takes 164k while the async test suite takes 40k, most likely there are lots of code paths not excersised with the async client. Make a plan for making the async test suite more or less symmetric with the sync test suite. write the plan to /tmp/testplan.md as for now. Followup-Prompt: Please create checkbox lists (* [ ] - asdfasdf)
The sync integration tests live in tests/test_caldav.py (4264 lines).
The async integration tests live in tests/test_async_integration.py (1034 lines).
The size disparity reflects a large coverage gap: the sync suite has ~56 functional
tests + 8 scheduling tests; the async suite has only 9 functional tests + 7
scheduling tests (one scheduling test is missing too).
Async test
Rough sync equivalent
test_principal_calendars
testPrincipal (partial)
test_principal_make_calendar
testCreateDeleteCalendar (partial)
test_search_events
testSearchEvent (partial)
test_search_events_by_date_range
testSearchEvent (partial)
test_search_todos_pending
testSearchTodos (partial)
test_search_todos_all
testSearchTodos (partial)
test_events_method
testCreateEvent (partial)
test_todos_method
testCreateTaskListAndTodo (partial)
test_add_organizer_no_arg
testAddOrganizer (partial – no-arg case only)
Async test
Sync equivalent
test_invite_and_respond
testInviteAndRespond ✓
test_freebusy
testFreeBusy ✓
(missing)
testAcceptInviteUsernameEmailFallback
test_schedule_tag_returned_on_save
testScheduleTagReturnedOnSave ✓
test_schedule_tag_stable_on_partstate_update
testScheduleTagStableOnPartstateUpdate ✓
test_schedule_tag_changes_on_organizer_update
testScheduleTagChangesOnOrganizerUpdate ✓
test_schedule_tag_mismatch_raises_error
testScheduleTagMismatchRaisesError ✓
test_schedule_tag_match_succeeds
testScheduleTagMatchSucceeds ✓
These are all in RepeatedFunctionalTestsBaseClass unless noted.
- testLookupEvent → test_lookup_event
Add event; look up by URL (event_by_url), by UID (get_event_by_uid), and
directly via Event(url=…).load(); verify NotFoundError on bad UID.
- testObjectByUID → test_object_by_uid
Add a TODO with known UID; retrieve via get_object_by_uid; verify NotFoundError
for non-existing / prefix-match UIDs.
- testLoadEvent → test_load_event
Add event to calendar 1; call load() on the returned object and on a freshly
fetched one. Needs a second calendar fixture (see Infrastructure section).
- testCopyEvent → test_copy_event event.copy() within same calendar (new uid); copy(new_parent=c2, keep_uid=True)
cross-calendar; copy(keep_uid=True) same calendar (no-op). Needs two calendars.
- testMultiGet → test_multi_get
Add two events; retrieve both via calendar_multiget([url1, url2]); call event.load_by_multiget().
- testGetSupportedComponents → test_get_supported_components calendar.get_supported_components() must include "VEVENT".
- testObjectBySyncToken → test_object_by_sync_token
Full sync-token cycle: add objects → objects() → verify initial count and
token; modify object → get_objects_by_sync_token → get 1 back (loaded);
add another → get 1 back; delete → get deleted (data=None). Use asyncio.sleep(1) for time-based tokens. Use is_supported("sync-token", dict)
to detect fragile/time-based behaviour.
- testSync → test_sync
Same lifecycle but via my_objects.sync() returning (updated, deleted) pairs.
- testSearchEvent (comprehensive) → test_search_event
Add ev1, ev3, evr (old-date events with search.time-range.event.old-dates
gate); test search() with no args, comp_class=Event, todo=True (empty),
date range, UID, text fields (summary, description, category – if supported). Note: the existing test_search_events uses near-future events and only checks
counts; this new test covers old-date and text-search code paths.
- testSearchCompType → test_search_comp_type
Add event and todo to a mixed calendar; verify search(comp_class=Event) / search(comp_class=Todo) / search(event=True, todo=True) counts.
- testSearchWithoutCompType → test_search_without_comp_type search() with no filter returns all items.
- testSearchSortTodo → test_search_sort_todo
Add todos with different priorities; verify sort order via search(todo=True, sort_keys=["priority"]).
- testDateSearchAndFreeBusy → test_date_search_and_freebusy
Add event, search by date range (no deprecated API, just search(event=True, start=…, end=…)); modify event, re-search to verify date moved; if freebusy-query supported, call calendar.freebusy_request(…) and verify isinstance(result, FreeBusy).
- testRecurringDateSearch → test_recurring_date_search
Add evr (yearly RRULE); search with expand=True over two occurrences;
verify expansion results.
- testRecurringDateWithExceptionSearch → test_recurring_date_with_exception_search
Add evr2 (bi-weekly with exception); search with expand=True over date range
containing two occurrences; verify RECURRENCE-IDs.
- testEditSingleRecurrence → test_edit_single_recurrence
Add daily recurring event; expand to find one occurrence; edit summary; verify
only that day changed; test save(all_recurrences=True).
- testSetDue → test_set_due
Create a todo; call todo.set_due(…) or todo.complete(); verify field values.
- testCreateTaskListAndTodo (comprehensive) → extend test_todos_method or new test
Add todo with add_todo(uid=…, summary=…); verify get_todos(); verify get_object_by_uid.
- testTodos → test_todos
Pending / completed / include_completed filtering; complete() on a todo.
- testTodoCompletion → test_todo_completion
Verify todo.complete() transitions STATUS to COMPLETED; verify pending-only
search no longer returns it; verify include_completed search does return it.
- testTodoRecurringCompleteSafe → test_todo_recurring_complete_safe
Recurring todo with fixed COUNT; complete() with handle_rrule=True; verify
next occurrence appears with NEEDS-ACTION.
- testTodoRecurringCompleteThisandfuture → test_todo_recurring_complete_thisandfuture complete(rrule_mode="thisandfuture") on a recurring todo.
- testTodoDatesearch → test_todo_datesearch
Search todos by DUE date ranges; verify start/end filtering works.
- testCreateJournalListAndJournalEntry → test_create_journal_list_and_journal_entry
Make a VJOURNAL calendar; add a journal entry; verify get_journals().
- testGetCalendarHomeSet → test_get_calendar_home_set principal.get_properties([cdav.CalendarHomeSet()]) must contain the key.
- testGetDefaultCalendar → test_get_default_calendar principal.get_calendars() must be non-empty (gated on get-current-user-principal.has-calendar).
- testSetCalendarProperties → test_set_calendar_properties
Get display name via get_properties; set a new name via set_properties; read
back and verify. Gate on create-calendar.set-displayname.
- testCalendarByFullURL → test_calendar_by_full_url
Look up calendar by full URL string and by URL object as cal_id.
- testFindCalendarOwner → test_find_calendar_owner calendar.get_property(dav.Owner()); if non-None, construct a Principal from
it and verify get_vcal_address().
- testPrincipal → extend test_principal_calendars
Check that all items returned by get_calendars() are AsyncCalendar instances.
- testSchedulingInfo → test_scheduling_info principal.calendar_user_address_set() and get_vcal_address().
- testSchedulingMailboxes → test_scheduling_mailboxes principal.schedule_inbox() and schedule_outbox().
- testPropfind → test_propfind
Raw XML propfind to principal URL; verify "resourcetype" in response.
- testChangeAttendeeStatusWithEmailGiven → test_change_attendee_status_with_email_given
Create event with ATTENDEE; call change_attendee_status(attendee="…", PARTSTAT="ACCEPTED");
save; reload and verify.
- testWrongAuthType → test_wrong_auth_type
With digest or bearer auth_type, connecting must raise AuthorizationError.
- testWrongPassword → test_wrong_password
Bad password must raise AuthorizationError (gate on wrong-password-check).
- testCreateChildParent → test_create_child_parent
Add parent + child + grandparent events with RELATED-TO; retrieve and verify
RELTYPE values.
- testOffsetURL → test_offset_url
Connect with url=principal.url and url=calendar.url; verify principal().get_calendars() still works.
- testUtf8Event → test_utf8_event
Calendar with non-ASCII name; event with non-ASCII summary; retrieve and verify.
- testCreateCalendarAndEventFromVobject → test_create_calendar_and_event_from_vobject
Add event from vobject.readOne(ev1); verify count. Note: vobject is optional
dependency – gate with pytest.importorskip("vobject").
- testCreateEventFromiCal → test_create_event_from_ical
Add event from an icalendar.Calendar object and from an icalendar.Event
object. Gate with icalendar.Calendar.new existence (requires icalendar 7+).
- testAcceptInviteUsernameEmailFallback → test_accept_invite_username_email_fallback
Build invite with ATTENDEE matching attendee's username as email; call change_attendee_status(partstat="ACCEPTED") with no explicit attendee; verify
PARTSTAT updated.
- testCheckCompatibility – runs caldav_server_tester externally; skip for async
(the sync version already covers it per server).
- testObjects – constructs bare DAVObject; trivial unit-level, skip.
- Second calendar fixture (async_calendar2) – Several tests need two distinct
calendars at once (testLoadEvent, testCopyEvent). Add async_calendar2 fixture in AsyncFunctionalTestsBaseClass following the same pattern as async_calendar but
with a different calendar_name.
- Journal list fixture (async_journal_list) – Similar to async_task_list but
with ["VJOURNAL"] component set. Add alongside.
- Test data constants – The async file uses inline make_event/make_todo helpers
with near-future dates. For old-date searches (Group C) we need the verbatim sync
test data: ev1, ev2, ev3, evr, evr2, todo, todo2–todo8, journal.
Preferred: import from test_caldav to avoid duplication: from .test_caldav import ev1, ev2, ev3, evr, evr2, todo, todo2, todo3, journal
- Server-params helper for auth tests – testWrongAuthType and testWrongPassword
need raw connection parameters. The TestServer object (self.server) already
carries config; add _make_async_client_with_params(**overrides) on the base
class that builds a fresh async client from the server config with some params
overridden.
- asyncio.sleep instead of time.sleep – already done in existing async tests;
maintain this discipline throughout.
- async_task_list fixture – already exists and handles supported_calendar_component_set;
reuse for todo date-search and sort tests.
- Phase 1 – Infrastructure (do first, enables everything else)
Add async_calendar2, async_journal_list, import shared test data constants,
add _make_async_client_with_params helper.
- Phase 2 – Core CRUD (Group A, tests 1–7)
Self-contained tests of the most basic library operations; most likely to surface
async-specific bugs.
- Phase 3 – Sync tokens (Group B, tests 8–9)
Verbose but straightforward; add after Group A is stable.
- Phase 4 – Search (Group C, tests 10–19)
Tests using old-date events must be gated on search.time-range.event.old-dates / search.unlimited-time-range.
- Phase 5 – Todos (Group D, tests 20–27)
Gate all on save-load.todo.
- Phase 6 – Properties and meta (Group E, tests 28–34)
Low risk, mostly property reads.
- Phase 7 – Regressions, auth errors, misc (Groups F and G, tests 35–50)
Use is_supported() exclusively (not the legacy check_compatibility_flag API).
All server I/O must be await-ed.
time.sleep → await asyncio.sleep.
Use the existing self.skip_unless_support(feature) helper already on the base class.
Event/todo data: prefer icalendar_component accessors over vobject_instance for
new tests.
Keep each test method focused on one behaviour; resist combining two sync tests into
one large async test.
For tests gated on near-future dates (existing async tests), keep using _get_base_date(). For old-date searches, use the imported constants and gate on
the appropriate feature flag.
All changes go into tests/test_async_integration.py.
No changes to tests/test_caldav.py are needed (its infrastructure is sync-only).
This issue was AI-generated using Claude Code with Sonnet 4.6
Prompt: The sync test suite takes 164k while the async test suite takes 40k, most likely there are lots of code paths not excersised with the async client. Make a plan for making the async test suite more or less symmetric with the sync test suite. write the plan to /tmp/testplan.md as for now.
Followup-Prompt: Please create checkbox lists (
* [ ] - asdfasdf)The sync integration tests live in
tests/test_caldav.py(4264 lines).The async integration tests live in
tests/test_async_integration.py(1034 lines).The size disparity reflects a large coverage gap: the sync suite has ~56 functional
tests + 8 scheduling tests; the async suite has only 9 functional tests + 7
scheduling tests (one scheduling test is missing too).
test_principal_calendarstestPrincipal(partial)test_principal_make_calendartestCreateDeleteCalendar(partial)test_search_eventstestSearchEvent(partial)test_search_events_by_date_rangetestSearchEvent(partial)test_search_todos_pendingtestSearchTodos(partial)test_search_todos_alltestSearchTodos(partial)test_events_methodtestCreateEvent(partial)test_todos_methodtestCreateTaskListAndTodo(partial)test_add_organizer_no_argtestAddOrganizer(partial – no-arg case only)test_invite_and_respondtestInviteAndRespond✓test_freebusytestFreeBusy✓testAcceptInviteUsernameEmailFallbacktest_schedule_tag_returned_on_savetestScheduleTagReturnedOnSave✓test_schedule_tag_stable_on_partstate_updatetestScheduleTagStableOnPartstateUpdate✓test_schedule_tag_changes_on_organizer_updatetestScheduleTagChangesOnOrganizerUpdate✓test_schedule_tag_mismatch_raises_errortestScheduleTagMismatchRaisesError✓test_schedule_tag_match_succeedstestScheduleTagMatchSucceeds✓These are all in
RepeatedFunctionalTestsBaseClassunless noted.-
testCreateOverwriteDeleteEvent→test_create_overwrite_delete_eventTests
no_create/no_overwriteflags, overwrite-same-UID, delete, 404 after delete.-
testLookupEvent→test_lookup_eventAdd event; look up by URL (
event_by_url), by UID (get_event_by_uid), anddirectly via
Event(url=…).load(); verifyNotFoundErroron bad UID.-
testObjectByUID→test_object_by_uidAdd a TODO with known UID; retrieve via
get_object_by_uid; verifyNotFoundErrorfor non-existing / prefix-match UIDs.
-
testLoadEvent→test_load_eventAdd event to calendar 1; call
load()on the returned object and on a freshlyfetched one. Needs a second calendar fixture (see Infrastructure section).
-
testCopyEvent→test_copy_eventevent.copy()within same calendar (new uid);copy(new_parent=c2, keep_uid=True)cross-calendar;
copy(keep_uid=True)same calendar (no-op). Needs two calendars.-
testMultiGet→test_multi_getAdd two events; retrieve both via
calendar_multiget([url1, url2]); callevent.load_by_multiget().-
testGetSupportedComponents→test_get_supported_componentscalendar.get_supported_components()must include"VEVENT".-
testObjectBySyncToken→test_object_by_sync_tokenFull sync-token cycle: add objects →
objects()→ verify initial count andtoken; modify object →
get_objects_by_sync_token→ get 1 back (loaded);add another → get 1 back; delete → get deleted (data=None). Use
asyncio.sleep(1)for time-based tokens. Useis_supported("sync-token", dict)to detect fragile/time-based behaviour.
-
testSync→test_syncSame lifecycle but via
my_objects.sync()returning(updated, deleted)pairs.-
testSearchShouldYieldData→test_search_should_yield_dataRef issue Skipped event with missing 'vevent' property (Home Assistant) #201:
search(event=True)on a populated calendar must return objectswith non-empty
.data.-
testSearchEvent(comprehensive) →test_search_eventAdd
ev1,ev3,evr(old-date events withsearch.time-range.event.old-datesgate); test
search()with no args,comp_class=Event,todo=True(empty),date range, UID, text fields (summary, description, category – if supported).
Note: the existing
test_search_eventsuses near-future events and only checkscounts; this new test covers old-date and text-search code paths.
-
testSearchCompType→test_search_comp_typeAdd event and todo to a mixed calendar; verify
search(comp_class=Event)/search(comp_class=Todo)/search(event=True, todo=True)counts.-
testSearchWithoutCompType→test_search_without_comp_typesearch()with no filter returns all items.-
testSearchSortTodo→test_search_sort_todoAdd todos with different priorities; verify sort order via
search(todo=True, sort_keys=["priority"]).-
testDateSearchAndFreeBusy→test_date_search_and_freebusyAdd event, search by date range (no deprecated API, just
search(event=True, start=…, end=…)); modify event, re-search to verify date moved; iffreebusy-querysupported, callcalendar.freebusy_request(…)and verifyisinstance(result, FreeBusy).-
testRecurringDateSearch→test_recurring_date_searchAdd
evr(yearly RRULE); search withexpand=Trueover two occurrences;verify expansion results.
-
testRecurringDateWithExceptionSearch→test_recurring_date_with_exception_searchAdd
evr2(bi-weekly with exception); search withexpand=Trueover date rangecontaining two occurrences; verify RECURRENCE-IDs.
-
testEditSingleRecurrence→test_edit_single_recurrenceAdd daily recurring event; expand to find one occurrence; edit summary; verify
only that day changed; test
save(all_recurrences=True).-
testAlarm→test_alarmAdd event with VALARM; verify
search(alarm_start=…, alarm_end=…)counts.-
testSetDue→test_set_dueCreate a todo; call
todo.set_due(…)ortodo.complete(); verify field values.-
testCreateTaskListAndTodo(comprehensive) → extendtest_todos_methodor new testAdd todo with
add_todo(uid=…, summary=…); verifyget_todos(); verifyget_object_by_uid.-
testTodos→test_todosPending / completed / include_completed filtering;
complete()on a todo.-
testTodoCompletion→test_todo_completionVerify
todo.complete()transitions STATUS to COMPLETED; verify pending-onlysearch no longer returns it; verify include_completed search does return it.
-
testTodoRecurringCompleteSafe→test_todo_recurring_complete_safeRecurring todo with fixed COUNT;
complete()withhandle_rrule=True; verifynext occurrence appears with NEEDS-ACTION.
-
testTodoRecurringCompleteThisandfuture→test_todo_recurring_complete_thisandfuturecomplete(rrule_mode="thisandfuture")on a recurring todo.-
testTodoDatesearch→test_todo_datesearchSearch todos by DUE date ranges; verify
start/endfiltering works.-
testCreateJournalListAndJournalEntry→test_create_journal_list_and_journal_entryMake a VJOURNAL calendar; add a journal entry; verify
get_journals().-
testGetCalendarHomeSet→test_get_calendar_home_setprincipal.get_properties([cdav.CalendarHomeSet()])must contain the key.-
testGetDefaultCalendar→test_get_default_calendarprincipal.get_calendars()must be non-empty (gated onget-current-user-principal.has-calendar).-
testSetCalendarProperties→test_set_calendar_propertiesGet display name via
get_properties; set a new name viaset_properties; readback and verify. Gate on
create-calendar.set-displayname.-
testCalendarByFullURL→test_calendar_by_full_urlLook up calendar by full URL string and by URL object as
cal_id.-
testFindCalendarOwner→test_find_calendar_ownercalendar.get_property(dav.Owner()); if non-None, construct aPrincipalfromit and verify
get_vcal_address().-
testPrincipal→ extendtest_principal_calendarsCheck that all items returned by
get_calendars()areAsyncCalendarinstances.-
testPrincipals→test_principalscaldav.principals()(list-all); optionallyprincipals(name=…)search.-
testIssue397→test_issue_397Recurring VEVENT with attendee + RECURRENCE-ID; store and retrieve; check
RELATED-TO / RECURRENCE-ID structure.
-
testIssue399ChangeAttendeeStatusUsernameEmailFallback→test_issue_399_change_attendee_statusBuild invite with ATTENDEE matching client username; call
change_attendee_status(partstat="ACCEPTED")without explicit attendee arg;verify PARTSTAT set.
-
testAddOrganizer(full) → extendtest_add_organizer_no_argAdd explicit string arg case and explicit vCalAddress arg case.
-
testSupport→test_supportcheck_dav_support(),check_cdav_support(),check_scheduling_support().-
testSchedulingInfo→test_scheduling_infoprincipal.calendar_user_address_set()andget_vcal_address().-
testSchedulingMailboxes→test_scheduling_mailboxesprincipal.schedule_inbox()andschedule_outbox().-
testPropfind→test_propfindRaw XML propfind to principal URL; verify
"resourcetype"in response.-
testChangeAttendeeStatusWithEmailGiven→test_change_attendee_status_with_email_givenCreate event with ATTENDEE; call
change_attendee_status(attendee="…", PARTSTAT="ACCEPTED");save; reload and verify.
-
testWrongAuthType→test_wrong_auth_typeWith digest or bearer auth_type, connecting must raise
AuthorizationError.-
testWrongPassword→test_wrong_passwordBad password must raise
AuthorizationError(gate onwrong-password-check).-
testCreateChildParent→test_create_child_parentAdd parent + child + grandparent events with RELATED-TO; retrieve and verify
RELTYPE values.
-
testOffsetURL→test_offset_urlConnect with
url=principal.urlandurl=calendar.url; verifyprincipal().get_calendars()still works.-
testUtf8Event→test_utf8_eventCalendar with non-ASCII name; event with non-ASCII summary; retrieve and verify.
-
testCreateCalendarAndEventFromVobject→test_create_calendar_and_event_from_vobjectAdd event from
vobject.readOne(ev1); verify count. Note: vobject is optionaldependency – gate with
pytest.importorskip("vobject").-
testCreateEventFromiCal→test_create_event_from_icalAdd event from an
icalendar.Calendarobject and from anicalendar.Eventobject. Gate with
icalendar.Calendar.newexistence (requires icalendar 7+).-
testAcceptInviteUsernameEmailFallback→test_accept_invite_username_email_fallbackBuild invite with ATTENDEE matching attendee's username as email; call
change_attendee_status(partstat="ACCEPTED")with no explicit attendee; verifyPARTSTAT updated.
-
testCheckCompatibility– runscaldav_server_testerexternally; skip for async(the sync version already covers it per server).
-
testObjects– constructs bareDAVObject; trivial unit-level, skip.- Second calendar fixture (
async_calendar2) – Several tests need two distinctcalendars at once (testLoadEvent, testCopyEvent). Add
async_calendar2fixture inAsyncFunctionalTestsBaseClassfollowing the same pattern asasync_calendarbutwith a different
calendar_name.- Journal list fixture (
async_journal_list) – Similar toasync_task_listbutwith
["VJOURNAL"]component set. Add alongside.- Test data constants – The async file uses inline
make_event/make_todohelperswith near-future dates. For old-date searches (Group C) we need the verbatim sync
test data:
ev1,ev2,ev3,evr,evr2,todo,todo2–todo8,journal.Preferred: import from
test_caldavto avoid duplication:from .test_caldav import ev1, ev2, ev3, evr, evr2, todo, todo2, todo3, journal- Server-params helper for auth tests –
testWrongAuthTypeandtestWrongPasswordneed raw connection parameters. The
TestServerobject (self.server) alreadycarries
config; add_make_async_client_with_params(**overrides)on the baseclass that builds a fresh async client from the server config with some params
overridden.
-
asyncio.sleepinstead oftime.sleep– already done in existing async tests;maintain this discipline throughout.
-
async_task_listfixture – already exists and handlessupported_calendar_component_set;reuse for todo date-search and sort tests.
- Phase 1 – Infrastructure (do first, enables everything else)
Add
async_calendar2,async_journal_list, import shared test data constants,add
_make_async_client_with_paramshelper.- Phase 2 – Core CRUD (Group A, tests 1–7)
Self-contained tests of the most basic library operations; most likely to surface
async-specific bugs.
- Phase 3 – Sync tokens (Group B, tests 8–9)
Verbose but straightforward; add after Group A is stable.
- Phase 4 – Search (Group C, tests 10–19)
Tests using old-date events must be gated on
search.time-range.event.old-dates/search.unlimited-time-range.- Phase 5 – Todos (Group D, tests 20–27)
Gate all on
save-load.todo.- Phase 6 – Properties and meta (Group E, tests 28–34)
Low risk, mostly property reads.
- Phase 7 – Regressions, auth errors, misc (Groups F and G, tests 35–50)
is_supported()exclusively (not the legacycheck_compatibility_flagAPI).await-ed.time.sleep→await asyncio.sleep.self.skip_unless_support(feature)helper already on the base class.icalendar_componentaccessors overvobject_instancefornew tests.
one large async test.
_get_base_date(). For old-date searches, use the imported constants and gate onthe appropriate feature flag.
All changes go into
tests/test_async_integration.py.No changes to
tests/test_caldav.pyare needed (its infrastructure is sync-only).