Skip to content

Commit c58c743

Browse files
committed
add tests
1 parent e40e167 commit c58c743

2 files changed

Lines changed: 126 additions & 2 deletions

File tree

src/design/plone/contenttypes/restapi/services/scadenziario/post.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,17 @@ def _event_start_matches_own_rule(accessor):
5151
event_start = getattr(accessor, "start", None)
5252
if not recrule or not event_start:
5353
return True
54+
# dateutil refuses a tz-aware dtstart together with a tz-naive UNTIL
55+
# (as recurrence_sequence_ical produces one): mirror that function's
56+
# own workaround of stripping the tzinfo before parsing the rule.
57+
naive_start = event_start.replace(tzinfo=None)
5458
try:
5559
rset = rrule.rrulestr(
56-
recrule, dtstart=event_start, forceset=True, ignoretz=True
60+
recrule, dtstart=naive_start, forceset=True, ignoretz=True
5761
)
5862
except (ValueError, TypeError):
5963
return True
60-
return next(iter(rset), None) == event_start
64+
return next(iter(rset), None) == naive_start
6165

6266

6367
class BaseService(Service):

src/design/plone/contenttypes/tests/test_service_scadenziario.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from datetime import datetime
33
from datetime import timedelta
4+
from dateutil.rrule import rrulestr
45
from design.plone.contenttypes.testing import (
56
DESIGN_PLONE_CONTENTTYPES_API_FUNCTIONAL_TESTING,
67
)
@@ -91,6 +92,77 @@ def test_return_future_events_if_query_is_end_after_today(self):
9192
# results are in asc order
9293
self.assertEqual(response["items"], sorted(response["items"]))
9394

95+
def test_recurring_event_excludes_start_date_not_matching_byday_rule(self):
96+
# an event spanning multiple weeks
97+
# (e.g. from Thursday the 2nd to Friday the 17th) with a
98+
# recurrence limited to Mondays and Fridays. Since the event
99+
# itself starts on a Thursday, that start date does not satisfy
100+
# the BYDAY rule and must not be listed: only the actual
101+
# Monday/Friday occurrence dates should appear.
102+
# See https://github.com/plone/plone.event RFC5545 DTSTART handling:
103+
# DTSTART is always part of the recurrence set even if it doesn't
104+
# match the rule, but that's not what we want to expose here.
105+
# fixed reference date (a Thursday), so the test doesn't depend on
106+
# the day it happens to run.
107+
start = datetime(2024, 4, 4, 9, 0)
108+
end = start + timedelta(hours=1)
109+
# spans multiple weeks, like the reported "from the 2nd to the
110+
# 17th" case (15 days, i.e. more than two full weeks).
111+
until = start + timedelta(days=15)
112+
recurrence = "RRULE:FREQ=WEEKLY;BYDAY=MO,FR;UNTIL={}".format(
113+
until.strftime("%Y%m%dT235959")
114+
)
115+
116+
api.content.create(
117+
container=self.portal,
118+
type="Event",
119+
title="Recurring event starting on a non-matching weekday",
120+
start=start,
121+
end=end,
122+
recurrence=recurrence,
123+
)
124+
commit()
125+
126+
# what the RRULE alone actually produces, ignoring the forced
127+
# DTSTART inclusion: this is what @scadenziario should return.
128+
expected_days = sorted(
129+
{
130+
occurrence.strftime("%Y/%m/%d")
131+
for occurrence in rrulestr(
132+
recurrence, dtstart=start, forceset=True, ignoretz=True
133+
)
134+
}
135+
)
136+
start_day = start.strftime("%Y/%m/%d")
137+
# sanity checks on the fixture itself: multiple weeks, several
138+
# occurrences, and the (non-matching) start day genuinely absent.
139+
self.assertGreaterEqual(len(expected_days), 5)
140+
self.assertNotIn(start_day, expected_days)
141+
142+
response = self.api_session.post(
143+
f"{self.portal_url}/@scadenziario",
144+
json={
145+
"query": [
146+
{
147+
"i": "portal_type",
148+
"o": "plone.app.querystring.operation.selection.any",
149+
"v": ["Event"],
150+
},
151+
{
152+
"i": "path",
153+
"o": "plone.app.querystring.operation.string.relativePath",
154+
"v": "./",
155+
},
156+
],
157+
"sort_on": "start",
158+
"sort_order": "ascending",
159+
"b_size": 100,
160+
},
161+
).json()
162+
163+
self.assertEqual(response["items"], expected_days)
164+
self.assertNotIn(start_day, response["items"])
165+
94166

95167
class ScadenziarioDayTest(unittest.TestCase):
96168
layer = DESIGN_PLONE_CONTENTTYPES_API_FUNCTIONAL_TESTING
@@ -234,3 +306,51 @@ def test_recurring_event_uses_occurrence_dates_not_master_span(self):
234306
for item in day_response["items"].get(day_after_first_occurrence, [])
235307
]
236308
self.assertNotIn("Recurring event", titles)
309+
310+
def test_recurring_event_excludes_start_date_not_matching_byday_rule(self):
311+
# same bug and fixture as in ScadenziarioTest (event spanning
312+
# multiple weeks, e.g. from Thursday the 2nd to Friday the 17th,
313+
# recurring only on Mondays and Fridays), but checked against
314+
# @scadenziario-day: it must not be found on its own (non-matching)
315+
# Thursday start date, only on the actual Monday/Friday occurrences.
316+
# fixed reference date (a Thursday), so the test doesn't depend on
317+
# the day it happens to run.
318+
start = datetime(2024, 4, 4, 9, 0)
319+
end = start + timedelta(hours=1)
320+
# spans multiple weeks, like the reported "from the 2nd to the
321+
# 17th" case (15 days, i.e. more than two full weeks).
322+
until = start + timedelta(days=15)
323+
recurrence = "RRULE:FREQ=WEEKLY;BYDAY=MO,FR;UNTIL={}".format(
324+
until.strftime("%Y%m%dT235959")
325+
)
326+
327+
api.content.create(
328+
container=self.portal,
329+
type="Event",
330+
title="Recurring event starting on a non-matching weekday",
331+
start=start,
332+
end=end,
333+
recurrence=recurrence,
334+
)
335+
commit()
336+
337+
occurrence_days = {
338+
occurrence.strftime("%Y/%m/%d")
339+
for occurrence in rrulestr(
340+
recurrence, dtstart=start, forceset=True, ignoretz=True
341+
)
342+
}
343+
start_day = start.strftime("%Y/%m/%d")
344+
self.assertGreaterEqual(len(occurrence_days), 5)
345+
self.assertNotIn(start_day, occurrence_days)
346+
347+
for occurrence_day in occurrence_days:
348+
day_response = self.query_day(datetime.strptime(occurrence_day, "%Y/%m/%d"))
349+
titles = [
350+
item["title"] for item in day_response["items"].get(occurrence_day, [])
351+
]
352+
self.assertIn("Recurring event starting on a non-matching weekday", titles)
353+
354+
day_response = self.query_day(start)
355+
titles = [item["title"] for item in day_response["items"].get(start_day, [])]
356+
self.assertNotIn("Recurring event starting on a non-matching weekday", titles)

0 commit comments

Comments
 (0)