Skip to content

Commit 11abd77

Browse files
tobixenclaude
andcommitted
feat: add scheduling.mailbox and scheduling.calendar-user-address-set sub-features
- Add scheduling.mailbox and scheduling.calendar-user-address-set to FeatureSet.FEATURES (RFC6638 sections 2.2-2.4.1) - Set both to unsupported in the gmx server entry (scheduling is advertised but sub-features are non-functional) - Adapt testSchedulingInfo/testSchedulingMailboxes to use the new sub-feature flags instead of the top-level scheduling flag - Refactor TestScheduling into TestSchedulingBase with a configurable _users list; legacy TestScheduling created from rfc6638_users - Generate TestSchedulingForServer* classes for servers with scheduling_users configured in their server entry - Pass scheduling_users through in TestServer.get_server_params() - Document and enable scheduling_users for Cyrus in the example config (Cyrus pre-creates user1-user5 with password 'x') Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 10eaee1 commit 11abd77

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

caldav/compatibility_hints.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,17 @@ class FeatureSet:
262262
"description": "Server correctly handles sync-collection reports after objects have been deleted from the calendar (solved in Nextcloud in https://github.com/nextcloud/server/pull/44130)"
263263
},
264264
"scheduling": {
265-
"description": "Server supports CalDAV Scheduling (RFC6638). Detected via the presence of the schedule-outbox and/or schedule-inbox in DAV headers. Corresponds to the legacy 'no_scheduling' flag.",
265+
"description": "Server supports CalDAV Scheduling (RFC6638). Detected via the presence of 'calendar-auto-schedule' in the DAV response header.",
266266
"links": ["https://datatracker.ietf.org/doc/html/rfc6638"],
267267
},
268+
"scheduling.mailbox": {
269+
"description": "Server provides schedule-inbox and schedule-outbox collections for the principal (RFC6638 sections 2.2-2.3). When unsupported, calls to schedule_inbox() or schedule_outbox() raise NotFoundError.",
270+
"links": ["https://datatracker.ietf.org/doc/html/rfc6638#section-2.2"],
271+
},
272+
"scheduling.calendar-user-address-set": {
273+
"description": "Server provides the calendar-user-address-set property on the principal (RFC6638 section 2.4.1), used to identify a user's email/URI for scheduling purposes. When unsupported, calendar_user_address_set() raises NotFoundError.",
274+
"links": ["https://datatracker.ietf.org/doc/html/rfc6638#section-2.4.1"],
275+
},
268276
'freebusy-query': {'description': "freebusy queries come in two flavors, one query can be done towards a CalDAV server as defined in RFC4791, another query can be done through the scheduling framework, RFC 6638. Only RFC4791 is tested for as today"},
269277
"freebusy-query.rfc4791": {
270278
"description": "Server supports free/busy-query REPORT as specified in RFC4791 section 7.10. The REPORT allows clients to query for free/busy time information for a time range. Servers without this support will typically return an error (often 500 Internal Server Error or 501 Not Implemented). Note: RFC6638 defines a different freebusy mechanism for scheduling",
@@ -1440,8 +1448,9 @@ def dotted_feature_set_list(self, compact=False):
14401448
#'search.time-range.alarm': {'support': 'unsupported'},
14411449
## GMX advertises calendar-auto-schedule but inbox/mailbox and
14421450
## calendar-user-address-set are not functional (RFC6638 sub-features).
1443-
## TODO: add scheduling.mailbox and scheduling.calendar-user-address-set sub-features
14441451
"scheduling": {"support": "full"},
1452+
"scheduling.mailbox": {"support": "unsupported"},
1453+
"scheduling.calendar-user-address-set": {"support": "unsupported"},
14451454
"old_flags": [
14461455
#"text_search_is_case_insensitive",
14471456
"no_search_openended",

tests/caldav_test_servers.yaml.example

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ test-servers:
6060
port: ${CYRUS_PORT:-8802}
6161
username: ${CYRUS_USERNAME:-testuser@test.local}
6262
password: ${CYRUS_PASSWORD:-testpassword}
63+
# Cyrus pre-creates user1-user5 (password 'x'), enabling scheduling tests.
64+
scheduling_users:
65+
- url: http://${CYRUS_HOST:-localhost}:${CYRUS_PORT:-8802}/dav/calendars/user/user1
66+
username: user1@test.local
67+
password: x
68+
- url: http://${CYRUS_HOST:-localhost}:${CYRUS_PORT:-8802}/dav/calendars/user/user2
69+
username: user2@test.local
70+
password: x
71+
- url: http://${CYRUS_HOST:-localhost}:${CYRUS_PORT:-8802}/dav/calendars/user/user3
72+
username: user3@test.local
73+
password: x
6374

6475
sogo:
6576
type: docker

tests/test_caldav.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -670,15 +670,11 @@ def test_multi_server_meta_section(self) -> None:
670670
assert len(clients) == 2
671671

672672

673-
@pytest.mark.skipif(
674-
not rfc6638_users, reason="need rfc6638_users to be set in order to run this test"
675-
)
676-
@pytest.mark.skipif(
677-
len(rfc6638_users) < 3,
678-
reason="need at least three users in rfc6638_users to be set in order to run this test",
679-
)
680-
class TestScheduling:
681-
"""Testing support of RFC6638.
673+
class TestSchedulingBase:
674+
"""
675+
Base class for RFC6638 scheduling tests. Not collected directly by
676+
pytest (no ``Test`` prefix); concrete subclasses supply ``_users``.
677+
682678
TODO: work in progress. Stalled a bit due to lack of proper testing accounts. I haven't managed to get this test to pass at any systems yet, but I believe the problem is not on the library side.
683679
* icloud: cannot really test much with only one test account
684680
available. I did some testing forth and back with emails sent
@@ -701,6 +697,9 @@ class TestScheduling:
701697
RFC6638.
702698
"""
703699

700+
## Subclasses set this to the list of user connection dicts to use.
701+
_users: list[dict] = []
702+
704703
def _getCalendar(self, i):
705704
calendar_id = "schedulingnosetestcalendar%i" % i
706705
calendar_name = "caldav scheduling test %i" % i
@@ -713,7 +712,7 @@ def _getCalendar(self, i):
713712
def setup_method(self):
714713
self.clients = []
715714
self.principals = []
716-
for foo in rfc6638_users:
715+
for foo in self._users:
717716
c = client(**foo)
718717
if not c.check_scheduling_support():
719718
continue ## ignoring user because server does not support scheduling.
@@ -790,6 +789,15 @@ def testInviteAndRespond(self):
790789
## inbox/outbox?
791790

792791

792+
## Legacy: run TestScheduling against the top-level rfc6638_users config.
793+
if rfc6638_users:
794+
TestScheduling = type(
795+
"TestScheduling",
796+
(TestSchedulingBase,),
797+
{"_users": rfc6638_users},
798+
)
799+
800+
793801
def _delay_decorator(f, t=20):
794802
def foo(*a, **kwa):
795803
time.sleep(t)
@@ -1096,14 +1104,12 @@ def testSupport(self):
10961104
assert self.caldav.check_scheduling_support() == self.is_supported("scheduling")
10971105

10981106
def testSchedulingInfo(self):
1099-
self.skip_unless_support("scheduling")
1100-
## TODO: add scheduling.calendar-user-address-set sub-feature check
1107+
self.skip_unless_support("scheduling.calendar-user-address-set")
11011108
calendar_user_address_set = self.principal.calendar_user_address_set()
11021109
me_a_participant = self.principal.get_vcal_address()
11031110

11041111
def testSchedulingMailboxes(self):
1105-
self.skip_unless_support("scheduling")
1106-
## TODO: add scheduling.mailbox sub-feature check
1112+
self.skip_unless_support("scheduling.mailbox")
11071113
inbox = self.principal.schedule_inbox()
11081114
outbox = self.principal.schedule_outbox()
11091115

@@ -3676,3 +3682,13 @@ def testWithEnvironment(self):
36763682
(RepeatedFunctionalTestsBaseClass,),
36773683
{"server_params": _caldav_server},
36783684
)
3685+
3686+
# If the server has scheduling_users configured, also generate a
3687+
# TestSchedulingForServer* class so scheduling tests run per-server.
3688+
if "scheduling_users" in _caldav_server:
3689+
_sched_classname = "TestSchedulingForServer" + _servername
3690+
vars()[_sched_classname] = type(
3691+
_sched_classname,
3692+
(TestSchedulingBase,),
3693+
{"_users": _caldav_server["scheduling_users"]},
3694+
)

tests/test_servers/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ def get_server_params(self) -> dict[str, Any]:
201201
# Pass through SSL verification setting if configured
202202
if "ssl_verify_cert" in self.config:
203203
params["ssl_verify_cert"] = self.config["ssl_verify_cert"]
204+
# Pass through scheduling_users if configured (for TestScheduling generation)
205+
if "scheduling_users" in self.config:
206+
params["scheduling_users"] = self.config["scheduling_users"]
204207
# Check if server is already running (either started by us or externally)
205208
already_running = self._started or self.is_accessible()
206209
if already_running:

0 commit comments

Comments
 (0)