@@ -118,7 +118,7 @@ def assert_ical_response_is_valid(test_inst, response, expected_event_summaries=
118118 expected_event_uids = None , expected_event_count = None ):
119119 """Validate an HTTP response containing iCal data
120120
121- Based on RFC2445 , but not exhaustive by any means. Assumes a single iCalendar object. Checks that
121+ Based on RFC5545 , but not exhaustive by any means. Assumes a single iCalendar object. Checks that
122122 expected_event_summaries/_uids are found, but other events are allowed to be present. Specify the
123123 expected_event_count if you want to reject additional events. If any of these are None,
124124 the check for that property is skipped.
@@ -132,18 +132,43 @@ def assert_ical_response_is_valid(test_inst, response, expected_event_summaries=
132132 test_inst .assertContains (response , 'VERSION' , count = 1 )
133133
134134 # Validate event objects
135+ event_count = 0
136+ uids_found = set ()
137+ summaries_found = set ()
138+ got_begin = False
139+ cur_event_props = set ()
140+ for line_num , line in enumerate (response .content .decode ().split ("\n " )):
141+ line = line .rstrip ()
142+ if line == 'BEGIN:VEVENT' :
143+ test_inst .assertFalse (got_begin , f"Nested BEGIN:VEVENT found on line { line_num + 1 } " )
144+ got_begin = True
145+ elif line == 'END:VEVENT' :
146+ test_inst .assertTrue (got_begin , f"Unexpected END:VEVENT on line { line_num + 1 } " )
147+ test_inst .assertIn ("uid" , cur_event_props , f"Found END:VEVENT without UID on line { line_num + 1 } " )
148+ got_begin = False
149+ cur_event_props .clear ()
150+ event_count += 1
151+ elif got_begin :
152+ # properties in an event
153+ if line .startswith ("UID:" ):
154+ # mandatory, not more than once
155+ test_inst .assertNotIn ("uid" , cur_event_props , f"Two UID properties in single event on line { line_num + 1 } " )
156+ cur_event_props .add ("uid" )
157+ uids_found .add (line .split (":" , 1 )[1 ])
158+ elif line .startswith ("SUMMARY:" ):
159+ # optional, not more than once
160+ test_inst .assertNotIn ("summary" , cur_event_props , f"Two SUMMARY properties in single event on line { line_num + 1 } " )
161+ cur_event_props .add ("summary" )
162+ summaries_found .add (line .split (":" , 1 )[1 ])
163+
135164 if expected_event_summaries is not None :
136- for summary in expected_event_summaries :
137- test_inst .assertContains (response , 'SUMMARY:' + summary )
165+ test_inst .assertCountEqual (summaries_found , set (expected_event_summaries ))
138166
139167 if expected_event_uids is not None :
140- for uid in expected_event_uids :
141- test_inst .assertContains (response , 'UID:' + uid )
168+ test_inst .assertCountEqual (uids_found , set (expected_event_uids ))
142169
143170 if expected_event_count is not None :
144- test_inst .assertContains (response , 'BEGIN:VEVENT' , count = expected_event_count )
145- test_inst .assertContains (response , 'END:VEVENT' , count = expected_event_count )
146- test_inst .assertContains (response , 'UID' , count = expected_event_count )
171+ test_inst .assertEqual (event_count , expected_event_count )
147172
148173 # make sure no doubled colons after timestamp properties
149174 test_inst .assertNotContains (response , 'DTSTART::' )
0 commit comments