Skip to content

Commit 71f1167

Browse files
committed
Add test for separated events
This test is designed to check whether events are divided into several parts, such as in issue #17.
1 parent d1982fe commit 71f1167

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

tests/test_calendar_files.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import pytest
22
import os
3+
from collections import defaultdict
4+
from datetime import datetime, timedelta
35

46
import requests
57
from icalendar import Calendar
@@ -16,6 +18,19 @@ def find_ics_files(base_path='./'):
1618
ics_files.append(os.path.join(root, file))
1719
return ics_files
1820

21+
22+
def find_ferien_ics_files(base_path='./'):
23+
"""Find only ICS files in 'Ferien' directories."""
24+
ics_files = []
25+
for root, _, files in os.walk(base_path):
26+
path_parts = set(os.path.normpath(root).split(os.sep))
27+
if 'Ferien' in path_parts:
28+
for file in files:
29+
if file.endswith('.ics'):
30+
ics_files.append(os.path.join(root, file))
31+
return ics_files
32+
33+
1934
@pytest.fixture(params=find_ics_files())
2035
def parsed_calendar(request):
2136
path = request.param
@@ -26,6 +41,18 @@ def parsed_calendar(request):
2641
pytest.fail(f"Failed to parse ICS file {path}: {e}")
2742
return cal, path
2843

44+
45+
@pytest.fixture(params=find_ferien_ics_files())
46+
def parsed_ferien_calendar(request):
47+
path = request.param
48+
with open(path, 'rb') as f:
49+
try:
50+
cal = Calendar.from_ical(f.read())
51+
except Exception as e:
52+
pytest.fail(f"Failed to parse ICS file {path}: {e}")
53+
return cal, path
54+
55+
2956
def build_expected_calendar_name(ics_path: str) -> str:
3057
dirname = os.path.basename(os.path.dirname(ics_path))
3158
filename = os.path.splitext(os.path.basename(ics_path))[0]
@@ -46,6 +73,85 @@ def test_calendar_name_matches_filename_with_suffix(parsed_calendar):
4673
assert cal_name == expected_name, f"Expected calendar name '{expected_name}', but found '{cal_name}' (NAME) in {path}"
4774
assert x_wr_cal_name == expected_name, f"Expected calendar name '{expected_name}', but found '{x_wr_cal_name}' (X-WR-CALNAME) in {path}"
4875

76+
77+
def test_no_close_events_with_same_title_in_ferien_calendars(parsed_ferien_calendar):
78+
"""Test that events with the same title have more than 1 day between end of one and start of next in Ferien calendars."""
79+
cal, path = parsed_ferien_calendar
80+
81+
events_by_title = defaultdict(list)
82+
83+
for component in cal.walk():
84+
if component.name == "VEVENT":
85+
summary = component.get('SUMMARY')
86+
dtstart = component.get('DTSTART')
87+
dtend = component.get('DTEND')
88+
89+
if summary is not None and dtstart is not None:
90+
title = str(summary)
91+
92+
start_date = dtstart.dt
93+
if hasattr(start_date, 'date'):
94+
start_date = start_date.date()
95+
96+
if dtend is not None:
97+
end_date = dtend.dt
98+
if hasattr(end_date, 'date'):
99+
end_date = end_date.date()
100+
else:
101+
end_date = start_date
102+
103+
events_by_title[title].append({
104+
'start': start_date,
105+
'end': end_date
106+
})
107+
108+
close_events = []
109+
110+
for title, events in events_by_title.items():
111+
if len(events) > 1:
112+
sorted_events = sorted(events, key=lambda x: x['start'])
113+
114+
for i in range(len(sorted_events) - 1):
115+
event1 = sorted_events[i]
116+
event2 = sorted_events[i + 1]
117+
118+
end_date1 = event1['end']
119+
start_date2 = event2['start']
120+
121+
if hasattr(end_date1, 'toordinal') and hasattr(start_date2, 'toordinal'):
122+
days_diff = start_date2.toordinal() - end_date1.toordinal()
123+
else:
124+
if hasattr(end_date1, 'date'):
125+
end_date1 = end_date1.date()
126+
if hasattr(start_date2, 'date'):
127+
start_date2 = start_date2.date()
128+
days_diff = (start_date2 - end_date1).days
129+
130+
if 0 <= days_diff <= 1:
131+
close_events.append({
132+
'title': title,
133+
'event1_start': event1['start'],
134+
'event1_end': event1['end'],
135+
'event2_start': event2['start'],
136+
'event2_end': event2['end'],
137+
'gap_days': days_diff
138+
})
139+
140+
if close_events:
141+
error_messages = []
142+
for event in close_events:
143+
error_messages.append(
144+
f" '{event['title']}': Event 1 ({event['event1_start']} - {event['event1_end']}) "
145+
f"and Event 2 ({event['event2_start']} - {event['event2_end']}) "
146+
f"have only {event['gap_days']} days gap"
147+
)
148+
149+
error_info = "\n".join(error_messages)
150+
pytest.fail(
151+
f"Events with same title found with 1 day or less gap in Ferien calendar {path}:\n{error_info}"
152+
)
153+
154+
49155
@pytest.mark.parametrize("ics_path", find_ics_files())
50156
def test_icalendar_org_validator(ics_path):
51157
with open(ics_path, 'rb') as f:

0 commit comments

Comments
 (0)