Skip to content

Commit eb2c079

Browse files
tobixenclaude
andcommitted
test: async Phase 2 – Group A core CRUD tests
Add 7 async equivalents of the sync RepeatedFunctionalTestsBaseClass CRUD tests: - test_get_supported_components: verify get_supported_components() includes VEVENT - test_lookup_event: add_event(), get_event_by_uid(), Event(url=…).load(), NotFoundError on missing UID - test_create_overwrite_delete_event: no_create/no_overwrite flag semantics, same-UID overwrite, delete and 404 verification - test_object_by_uid: add_todo with known UID, get_object_by_uid(), exact match only (prefix and suffix must not match) - test_load_event: add_event() returns usable object; load() populates data on both the returned handle and a freshly fetched one (uses async_calendar2) - test_copy_event: copy() within same calendar (new UID), cross-calendar copy with keep_uid, same-calendar keep_uid overwrite (uses async_calendar2) - test_multi_get: calendar_multiget() with two URLs, load_by_multiget() All tests use add_event/add_todo (not the deprecated save_event/save_todo), await every coroutine, and gate on is_supported() feature flags. prompt: continue with phase 2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2bd328f commit eb2c079

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

tests/test_async_integration.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,183 @@ async def test_add_organizer_no_arg(self, async_client: Any, async_calendar: Any
566566
f"ORGANIZER {org!r} should match the principal's address {expected_vcal!r}"
567567
)
568568

569+
# ==================== Group A – Core CRUD ====================
570+
571+
@pytest.mark.asyncio
572+
async def test_get_supported_components(self, async_calendar: Any) -> None:
573+
"""get_supported_components() must include VEVENT."""
574+
components = await async_calendar.get_supported_components()
575+
assert components
576+
assert "VEVENT" in components
577+
578+
@pytest.mark.asyncio
579+
async def test_lookup_event(self, async_calendar: Any) -> None:
580+
"""Add an event and look it up by URL, by UID, and via Event(url=…).load()."""
581+
from caldav import Event
582+
from caldav.lib import error
583+
584+
self.skip_unless_support("save-load.event")
585+
c = async_calendar
586+
587+
e1 = await c.add_event(ev1_static)
588+
assert e1.url is not None
589+
590+
# look up by UID
591+
e3 = await c.get_event_by_uid("20010712T182145Z-123401@example.com")
592+
assert str(e3.icalendar_component["uid"]) == "20010712T182145Z-123401@example.com"
593+
assert e3.url == e1.url
594+
595+
# load directly from URL without going through the calendar object
596+
e4 = Event(client=c.client, url=e1.url)
597+
await e4.load()
598+
assert str(e4.icalendar_component["uid"]) == "20010712T182145Z-123401@example.com"
599+
600+
with pytest.raises(error.NotFoundError):
601+
await c.get_event_by_uid("nonexistent-uid-000")
602+
603+
@pytest.mark.asyncio
604+
async def test_create_overwrite_delete_event(self, async_calendar: Any) -> None:
605+
"""no_create/no_overwrite flags, same-UID overwrite, and delete."""
606+
from caldav.lib import error
607+
608+
self.skip_unless_support("save-load.event")
609+
c = async_calendar
610+
611+
# attempting to update a non-existing event must raise ConsistencyError
612+
with pytest.raises(error.ConsistencyError):
613+
await c.add_event(ev1_static, no_create=True)
614+
615+
# no_create + no_overwrite is always an error
616+
with pytest.raises(error.ConsistencyError):
617+
await c.add_event(ev1_static, no_create=True, no_overwrite=True)
618+
619+
e1 = await c.add_event(ev1_static)
620+
assert e1.url is not None
621+
622+
# same UID again → overwrite (unless server forbids it)
623+
if not self.is_supported("no-overwrite"):
624+
e2 = await c.add_event(ev1_static)
625+
626+
# no_create on an existing event must succeed
627+
e2 = await c.add_event(ev1_static, no_create=True)
628+
629+
# modify and save with no_create
630+
e2.icalendar_component["summary"] = "Bastille Day Party!"
631+
await e2.save(no_create=True)
632+
633+
e3 = await c.event_by_url(e1.url)
634+
assert e3.icalendar_component["summary"] == "Bastille Day Party!"
635+
636+
# no_overwrite on an existing event must raise ConsistencyError
637+
with pytest.raises(error.ConsistencyError):
638+
await c.add_event(ev1_static, no_overwrite=True)
639+
640+
await e1.delete()
641+
642+
with pytest.raises(error.NotFoundError):
643+
await c.event_by_url(e1.url)
644+
with pytest.raises(error.NotFoundError):
645+
await c.get_event_by_uid("20010712T182145Z-123401@example.com")
646+
647+
@pytest.mark.asyncio
648+
async def test_object_by_uid(self, async_task_list: Any) -> None:
649+
"""Add a TODO with a known UID and retrieve it via get_object_by_uid()."""
650+
from caldav.lib import error
651+
652+
c = async_task_list
653+
await c.add_todo(summary="Some test task with a well-known uid", uid="well_known_1")
654+
655+
foo = await c.get_object_by_uid("well_known_1")
656+
assert str(foo.icalendar_component["summary"]) == "Some test task with a well-known uid"
657+
658+
# prefix match must NOT succeed
659+
with pytest.raises(error.NotFoundError):
660+
await c.get_object_by_uid("well_known")
661+
662+
# suffix match must NOT succeed
663+
with pytest.raises(error.NotFoundError):
664+
await c.get_object_by_uid("well_known_10")
665+
666+
@pytest.mark.asyncio
667+
async def test_load_event(self, async_calendar: Any, async_calendar2: Any) -> None:
668+
"""add_event() returns an object; load() must populate it."""
669+
self.skip_unless_support("save-load.event")
670+
self.skip_unless_support("create-calendar")
671+
672+
c1 = async_calendar
673+
674+
e1_ = await c1.add_event(ev1_static)
675+
await e1_.load() # load the object returned by add_event
676+
677+
events = await c1.get_events()
678+
assert len(events) >= 1
679+
e1 = events[0]
680+
await e1.load() # load a freshly fetched handle
681+
assert e1.url == e1_.url
682+
683+
@pytest.mark.asyncio
684+
async def test_copy_event(self, async_calendar: Any, async_calendar2: Any) -> None:
685+
"""copy() within same calendar and cross-calendar."""
686+
self.skip_unless_support("save-load.event")
687+
self.skip_unless_support("create-calendar")
688+
689+
c1 = async_calendar
690+
c2 = async_calendar2
691+
692+
e1_ = await c1.add_event(ev1_static)
693+
events = await c1.get_events()
694+
e1 = events[0]
695+
696+
# duplicate in same calendar with a new UID
697+
e1_dup = e1.copy()
698+
await e1_dup.save()
699+
assert len(await c1.get_events()) == 2
700+
701+
# copy cross-calendar keeping the same UID
702+
if self.is_supported("save.duplicate-uid.cross-calendar"):
703+
e1_in_c2 = e1.copy(new_parent=c2, keep_uid=True)
704+
await e1_in_c2.save()
705+
assert len(await c2.get_events()) == 1
706+
707+
# modifying the copy in c2 must not affect c1's event
708+
e1_in_c2.icalendar_component["summary"] = "asdf"
709+
await e1_in_c2.save()
710+
await e1.load()
711+
assert str(e1.icalendar_component["summary"]) == "Bastille Day Party"
712+
713+
# copy in same calendar keeping UID — same-UID PUT is a no-op / overwrite
714+
e1_dup2 = e1.copy(keep_uid=True)
715+
await e1_dup2.save()
716+
# count should still be 2 (not 3) because same UID overwrites
717+
assert len(await c1.get_events()) == 2
718+
719+
@pytest.mark.asyncio
720+
async def test_multi_get(self, async_calendar: Any) -> None:
721+
"""calendar_multiget() retrieves multiple events in one request."""
722+
self.skip_unless_support("save-load.event")
723+
724+
c = async_calendar
725+
726+
event1 = await c.add_event(
727+
uid="test-multiget-1",
728+
dtstart=datetime(2015, 1, 1, 8, 0, 0),
729+
dtend=datetime(2015, 1, 1, 9, 0, 0),
730+
summary="test-multiget-1",
731+
)
732+
event2 = await c.add_event(
733+
uid="test-multiget-2",
734+
dtstart=datetime(2015, 1, 1, 8, 0, 0),
735+
dtend=datetime(2015, 1, 1, 9, 0, 0),
736+
summary="test-multiget-2",
737+
)
738+
739+
results = await c.calendar_multiget([event1.url, event2.url])
740+
assert len(results) == 2
741+
uids = {str(r.icalendar_component["uid"]) for r in results}
742+
assert uids == {"test-multiget-1", "test-multiget-2"}
743+
744+
await event1.load_by_multiget()
745+
569746

570747
class _AsyncTestSchedulingBase:
571748
"""

0 commit comments

Comments
 (0)