Skip to content

Make the Async Test Suite Symmetric with the Sync Test Suite #667

@tobixen

Description

@tobixen

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.

  • - testCreateOverwriteDeleteEventtest_create_overwrite_delete_event
    Tests no_create/no_overwrite flags, overwrite-same-UID, delete, 404 after delete.

  • - testLookupEventtest_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.

  • - testObjectByUIDtest_object_by_uid
    Add a TODO with known UID; retrieve via get_object_by_uid; verify NotFoundError
    for non-existing / prefix-match UIDs.

  • - testLoadEventtest_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).

  • - testCopyEventtest_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.

  • - testMultiGettest_multi_get
    Add two events; retrieve both via calendar_multiget([url1, url2]); call
    event.load_by_multiget().

  • - testGetSupportedComponentstest_get_supported_components
    calendar.get_supported_components() must include "VEVENT".

  • - testObjectBySyncTokentest_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.

  • - testSynctest_sync
    Same lifecycle but via my_objects.sync() returning (updated, deleted) pairs.

  • - testSearchShouldYieldDatatest_search_should_yield_data
    Ref issue Skipped event with missing 'vevent' property (Home Assistant) #201: search(event=True) on a populated calendar must return objects
    with non-empty .data.

  • - 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.

  • - testSearchCompTypetest_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.

  • - testSearchWithoutCompTypetest_search_without_comp_type
    search() with no filter returns all items.

  • - testSearchSortTodotest_search_sort_todo
    Add todos with different priorities; verify sort order via
    search(todo=True, sort_keys=["priority"]).

  • - testDateSearchAndFreeBusytest_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).

  • - testRecurringDateSearchtest_recurring_date_search
    Add evr (yearly RRULE); search with expand=True over two occurrences;
    verify expansion results.

  • - testRecurringDateWithExceptionSearchtest_recurring_date_with_exception_search
    Add evr2 (bi-weekly with exception); search with expand=True over date range
    containing two occurrences; verify RECURRENCE-IDs.

  • - testEditSingleRecurrencetest_edit_single_recurrence
    Add daily recurring event; expand to find one occurrence; edit summary; verify
    only that day changed; test save(all_recurrences=True).

  • - testAlarmtest_alarm
    Add event with VALARM; verify search(alarm_start=…, alarm_end=…) counts.

  • - testSetDuetest_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.

  • - testTodostest_todos
    Pending / completed / include_completed filtering; complete() on a todo.

  • - testTodoCompletiontest_todo_completion
    Verify todo.complete() transitions STATUS to COMPLETED; verify pending-only
    search no longer returns it; verify include_completed search does return it.

  • - testTodoRecurringCompleteSafetest_todo_recurring_complete_safe
    Recurring todo with fixed COUNT; complete() with handle_rrule=True; verify
    next occurrence appears with NEEDS-ACTION.

  • - testTodoRecurringCompleteThisandfuturetest_todo_recurring_complete_thisandfuture
    complete(rrule_mode="thisandfuture") on a recurring todo.

  • - testTodoDatesearchtest_todo_datesearch
    Search todos by DUE date ranges; verify start/end filtering works.

  • - testCreateJournalListAndJournalEntrytest_create_journal_list_and_journal_entry
    Make a VJOURNAL calendar; add a journal entry; verify get_journals().

  • - testGetCalendarHomeSettest_get_calendar_home_set
    principal.get_properties([cdav.CalendarHomeSet()]) must contain the key.

  • - testGetDefaultCalendartest_get_default_calendar
    principal.get_calendars() must be non-empty (gated on get-current-user-principal.has-calendar).

  • - testSetCalendarPropertiestest_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.

  • - testCalendarByFullURLtest_calendar_by_full_url
    Look up calendar by full URL string and by URL object as cal_id.

  • - testFindCalendarOwnertest_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.

  • - testPrincipalstest_principals
    caldav.principals() (list-all); optionally principals(name=…) search.

  • - testIssue397test_issue_397
    Recurring VEVENT with attendee + RECURRENCE-ID; store and retrieve; check
    RELATED-TO / RECURRENCE-ID structure.

  • - testIssue399ChangeAttendeeStatusUsernameEmailFallbacktest_issue_399_change_attendee_status
    Build invite with ATTENDEE matching client username; call
    change_attendee_status(partstat="ACCEPTED") without explicit attendee arg;
    verify PARTSTAT set.

  • - testAddOrganizer (full) → extend test_add_organizer_no_arg
    Add explicit string arg case and explicit vCalAddress arg case.

  • - testSupporttest_support
    check_dav_support(), check_cdav_support(), check_scheduling_support().

  • - testSchedulingInfotest_scheduling_info
    principal.calendar_user_address_set() and get_vcal_address().

  • - testSchedulingMailboxestest_scheduling_mailboxes
    principal.schedule_inbox() and schedule_outbox().

  • - testPropfindtest_propfind
    Raw XML propfind to principal URL; verify "resourcetype" in response.

  • - testChangeAttendeeStatusWithEmailGiventest_change_attendee_status_with_email_given
    Create event with ATTENDEE; call change_attendee_status(attendee="…", PARTSTAT="ACCEPTED");
    save; reload and verify.

  • - testWrongAuthTypetest_wrong_auth_type
    With digest or bearer auth_type, connecting must raise AuthorizationError.

  • - testWrongPasswordtest_wrong_password
    Bad password must raise AuthorizationError (gate on wrong-password-check).

  • - testCreateChildParenttest_create_child_parent
    Add parent + child + grandparent events with RELATED-TO; retrieve and verify
    RELTYPE values.

  • - testOffsetURLtest_offset_url
    Connect with url=principal.url and url=calendar.url; verify
    principal().get_calendars() still works.

  • - testUtf8Eventtest_utf8_event
    Calendar with non-ASCII name; event with non-ASCII summary; retrieve and verify.

  • - testCreateCalendarAndEventFromVobjecttest_create_calendar_and_event_from_vobject
    Add event from vobject.readOne(ev1); verify count. Note: vobject is optional
    dependency – gate with pytest.importorskip("vobject").

  • - testCreateEventFromiCaltest_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+).

  • - testAcceptInviteUsernameEmailFallbacktest_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, todo2todo8, 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 teststestWrongAuthType 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.sleepawait 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions