Skip to content

Commit 9e22ba7

Browse files
committed
black style. Last minute v2.2-preparations
1 parent ae8089b commit 9e22ba7

File tree

3 files changed

+78
-33
lines changed

3 files changed

+78
-33
lines changed

caldav/collection.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,8 @@ def objects_by_sync_token(
12471247
## Fetch ETags for all objects if not already present
12481248
## ETags are crucial for detecting changes in the fallback mechanism
12491249
if all_objects and (
1250-
not hasattr(all_objects[0], "props") or dav.GetEtag.tag not in all_objects[0].props
1250+
not hasattr(all_objects[0], "props")
1251+
or dav.GetEtag.tag not in all_objects[0].props
12511252
):
12521253
## Use PROPFIND to fetch ETags for all objects
12531254
try:
@@ -1275,7 +1276,11 @@ def objects_by_sync_token(
12751276
fake_sync_token = self._generate_fake_sync_token(all_objects)
12761277

12771278
## If a sync_token was provided, check if anything has changed
1278-
if sync_token and isinstance(sync_token, str) and sync_token.startswith("fake-"):
1279+
if (
1280+
sync_token
1281+
and isinstance(sync_token, str)
1282+
and sync_token.startswith("fake-")
1283+
):
12791284
## Compare the provided token with the new token
12801285
if sync_token == fake_sync_token:
12811286
## Nothing has changed, return empty collection

tests/test_caldav.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,12 @@ def testObjectBySyncToken(self):
13471347
sync_info = self.is_supported("sync-token", return_type=dict)
13481348
is_time_based = sync_info.get("behaviour") == "time-based"
13491349
## Consider the client-side fake sync-tokens and etag stuff to be fragile
1350-
is_fragile = sync_info.get("support") in ("fragile", "broken", "unsupported", "ungraceful")
1350+
is_fragile = sync_info.get("support") in (
1351+
"fragile",
1352+
"broken",
1353+
"unsupported",
1354+
"ungraceful",
1355+
)
13511356

13521357
if is_time_based:
13531358
time.sleep(1)
@@ -1466,7 +1471,12 @@ def testSync(self):
14661471
sync_info = self.is_supported("sync-token", return_type=dict)
14671472
is_time_based = sync_info.get("behaviour") == "time-based"
14681473
## Consider the client-side fake sync-tokens and etag stuff to be fragile
1469-
is_fragile = sync_info.get("support") in ("fragile", "broken", "unsupported", "ungraceful")
1474+
is_fragile = sync_info.get("support") in (
1475+
"fragile",
1476+
"broken",
1477+
"unsupported",
1478+
"ungraceful",
1479+
)
14701480

14711481
## Boiler plate ... make a calendar and add some content
14721482
c = self._fixCalendar()

tests/test_sync_token_fallback.py

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
These tests verify the behavior of the fake sync token implementation
55
used when servers don't support sync-collection REPORT.
66
"""
7+
from unittest.mock import MagicMock
8+
from unittest.mock import Mock
9+
from unittest.mock import patch
710

811
import pytest
9-
from unittest.mock import Mock, MagicMock, patch
10-
from caldav.collection import Calendar, SynchronizableCalendarObjectCollection
11-
from caldav.lib.url import URL
12+
13+
from caldav.collection import Calendar
14+
from caldav.collection import SynchronizableCalendarObjectCollection
1215
from caldav.elements import dav
16+
from caldav.lib.url import URL
1317

1418

1519
class TestSyncTokenFallback:
@@ -22,8 +26,7 @@ def setup_method(self):
2226
self.mock_client.features.is_supported = Mock(return_value={})
2327

2428
self.calendar = Calendar(
25-
client=self.mock_client,
26-
url=URL("http://example.com/calendar/")
29+
client=self.mock_client, url=URL("http://example.com/calendar/")
2730
)
2831

2932
def create_mock_object(self, url_str: str, etag: str = None, data: str = None):
@@ -110,10 +113,11 @@ def test_generate_fake_sync_token_cannot_detect_changes_without_etags(self) -> N
110113

111114
# BUG: Tokens will be the same because we're using URLs as fallback
112115
# and URLs don't change when content changes
113-
assert token_before == token_after, \
114-
"This test documents a KNOWN BUG: without ETags, modifications aren't detected"
116+
assert (
117+
token_before == token_after
118+
), "This test documents a KNOWN BUG: without ETags, modifications aren't detected"
115119

116-
@patch.object(Calendar, 'search')
120+
@patch.object(Calendar, "search")
117121
def test_fallback_returns_empty_when_nothing_changed(self, mock_search) -> None:
118122
"""Test that fallback returns empty list when sync token matches."""
119123
# Setup: search returns same objects with ETags
@@ -125,7 +129,9 @@ def test_fallback_returns_empty_when_nothing_changed(self, mock_search) -> None:
125129
self.mock_client.features.is_supported.return_value = {"support": "unsupported"}
126130

127131
# First call: get initial state
128-
result1 = self.calendar.objects_by_sync_token(sync_token=None, load_objects=False)
132+
result1 = self.calendar.objects_by_sync_token(
133+
sync_token=None, load_objects=False
134+
)
129135
initial_token = result1.sync_token
130136

131137
# Second call: with same token, should return empty
@@ -136,7 +142,7 @@ def test_fallback_returns_empty_when_nothing_changed(self, mock_search) -> None:
136142
assert len(list(result2)) == 0, "Should return empty when nothing changed"
137143
assert result2.sync_token == initial_token
138144

139-
@patch.object(Calendar, 'search')
145+
@patch.object(Calendar, "search")
140146
def test_fallback_returns_all_when_etag_changed(self, mock_search) -> None:
141147
"""Test that fallback returns all objects when ETags change."""
142148
# First call: return objects with initial ETags
@@ -146,11 +152,15 @@ def test_fallback_returns_all_when_etag_changed(self, mock_search) -> None:
146152

147153
self.mock_client.features.is_supported.return_value = {"support": "unsupported"}
148154

149-
result1 = self.calendar.objects_by_sync_token(sync_token=None, load_objects=False)
155+
result1 = self.calendar.objects_by_sync_token(
156+
sync_token=None, load_objects=False
157+
)
150158
initial_token = result1.sync_token
151159

152160
# Simulate modification: search now returns objects with changed ETags
153-
obj1_modified = self.create_mock_object("http://example.com/1.ics", etag="etag-1-new")
161+
obj1_modified = self.create_mock_object(
162+
"http://example.com/1.ics", etag="etag-1-new"
163+
)
154164
obj2_same = self.create_mock_object("http://example.com/2.ics", etag="etag-2")
155165
mock_search.return_value = [obj1_modified, obj2_same]
156166

@@ -159,13 +169,19 @@ def test_fallback_returns_all_when_etag_changed(self, mock_search) -> None:
159169
sync_token=initial_token, load_objects=False
160170
)
161171

162-
assert len(list(result2)) == 2, "Should return all objects when changes detected"
172+
assert (
173+
len(list(result2)) == 2
174+
), "Should return all objects when changes detected"
163175
assert result2.sync_token != initial_token
164176

165-
@pytest.mark.xfail(reason="Mock objects don't preserve props updates properly - integration test needed")
166-
@patch.object(Calendar, '_query_properties')
167-
@patch.object(Calendar, 'search')
168-
def test_fallback_fetches_etags_when_missing(self, mock_search, mock_query_props) -> None:
177+
@pytest.mark.xfail(
178+
reason="Mock objects don't preserve props updates properly - integration test needed"
179+
)
180+
@patch.object(Calendar, "_query_properties")
181+
@patch.object(Calendar, "search")
182+
def test_fallback_fetches_etags_when_missing(
183+
self, mock_search, mock_query_props
184+
) -> None:
169185
"""
170186
Test that fallback fetches ETags when search() doesn't return them.
171187
@@ -176,25 +192,33 @@ def test_fallback_fetches_etags_when_missing(self, mock_search, mock_query_props
176192
props updates properly. The actual functionality works in integration tests.
177193
"""
178194
# First call: return objects without ETags
179-
obj1 = self.create_mock_object("http://example.com/calendar/1.ics", data="DATA1")
180-
obj2 = self.create_mock_object("http://example.com/calendar/2.ics", data="DATA2")
195+
obj1 = self.create_mock_object(
196+
"http://example.com/calendar/1.ics", data="DATA1"
197+
)
198+
obj2 = self.create_mock_object(
199+
"http://example.com/calendar/2.ics", data="DATA2"
200+
)
181201
mock_search.return_value = [obj1, obj2]
182202

183203
# Mock PROPFIND response with ETags
184204
mock_response = Mock()
185205
mock_response.expand_simple_props.return_value = {
186206
"http://example.com/calendar/1.ics": {dav.GetEtag.tag: "etag-1"},
187-
"http://example.com/calendar/2.ics": {dav.GetEtag.tag: "etag-2"}
207+
"http://example.com/calendar/2.ics": {dav.GetEtag.tag: "etag-2"},
188208
}
189209
mock_query_props.return_value = mock_response
190210

191211
self.mock_client.features.is_supported.return_value = {"support": "unsupported"}
192212

193-
result1 = self.calendar.objects_by_sync_token(sync_token=None, load_objects=False)
213+
result1 = self.calendar.objects_by_sync_token(
214+
sync_token=None, load_objects=False
215+
)
194216
initial_token = result1.sync_token
195217

196218
# Verify PROPFIND was called to fetch ETags
197-
assert mock_query_props.call_count >= 1, "PROPFIND should be called to fetch ETags"
219+
assert (
220+
mock_query_props.call_count >= 1
221+
), "PROPFIND should be called to fetch ETags"
198222

199223
# Check that ETags were actually added to the first batch of objects
200224
# (This verifies the ETag fetching mechanism worked)
@@ -207,14 +231,16 @@ def test_fallback_fetches_etags_when_missing(self, mock_search, mock_query_props
207231
obj1_modified = self.create_mock_object(
208232
"http://example.com/calendar/1.ics", data="MODIFIED_DATA1"
209233
)
210-
obj2_same = self.create_mock_object("http://example.com/calendar/2.ics", data="DATA2")
234+
obj2_same = self.create_mock_object(
235+
"http://example.com/calendar/2.ics", data="DATA2"
236+
)
211237
mock_search.return_value = [obj1_modified, obj2_same]
212238

213239
# Mock PROPFIND to return different ETag for modified object
214240
mock_response2 = Mock()
215241
mock_response2.expand_simple_props.return_value = {
216242
"http://example.com/calendar/1.ics": {dav.GetEtag.tag: "etag-1-new"},
217-
"http://example.com/calendar/2.ics": {dav.GetEtag.tag: "etag-2"}
243+
"http://example.com/calendar/2.ics": {dav.GetEtag.tag: "etag-2"},
218244
}
219245
mock_query_props.return_value = mock_response2
220246

@@ -225,15 +251,19 @@ def test_fallback_fetches_etags_when_missing(self, mock_search, mock_query_props
225251

226252
# Debug: check if ETags were added to second batch
227253
if obj1_modified.props:
228-
print(f"DEBUG: obj1_modified props after second call: {obj1_modified.props}")
254+
print(
255+
f"DEBUG: obj1_modified props after second call: {obj1_modified.props}"
256+
)
229257
if obj2_same.props:
230258
print(f"DEBUG: obj2_same props after second call: {obj2_same.props}")
231259

232260
# Should return all objects because change was detected
233-
assert len(list(result2)) == 2, \
234-
"Should detect modification via ETags and return all objects"
235-
assert result2.sync_token != initial_token, \
236-
"Token should change when ETag changes"
261+
assert (
262+
len(list(result2)) == 2
263+
), "Should detect modification via ETags and return all objects"
264+
assert (
265+
result2.sync_token != initial_token
266+
), "Token should change when ETag changes"
237267

238268

239269
if __name__ == "__main__":

0 commit comments

Comments
 (0)