import re import pytest from icalendar import Calendar, Event, Parameters, vCalAddress @pytest.mark.parametrize( ("parameter", "expected"), [ # Simple parameter:value pair (Parameters(parameter1="Value1"), b"PARAMETER1=Value1"), # Parameter with list of values must be separated by comma (Parameters({"parameter1": ["Value1", "Value2"]}), b"PARAMETER1=Value1,Value2"), # Multiple parameters must be separated by a semicolon ( Parameters({"RSVP": "TRUE", "ROLE": "REQ-PARTICIPANT"}), b"ROLE=REQ-PARTICIPANT;RSVP=TRUE", ), # Parameter values containing ',;:' must be double quoted (Parameters({"ALTREP": "http://www.wiz.org"}), b'ALTREP="http://www.wiz.org"'), # list items must be quoted separately ( Parameters( {"MEMBER": ["MAILTO:projectA@host.com", "MAILTO:projectB@host.com"]} ), b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"', ), ( Parameters( { "parameter1": "Value1", "parameter2": ["Value2", "Value3"], "ALTREP": ["http://www.wiz.org", "value4"], } ), b'ALTREP="http://www.wiz.org","value4";PARAMETER1=Value1;PARAMETER2=Value2,Value3', ), # Including empty strings (Parameters({"PARAM": ""}), b"PARAM="), # We can also parse parameter strings ( Parameters( {"MEMBER": ["MAILTO:projectA@host.com", "MAILTO:projectB@host.com"]} ), b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"', ), # We can also parse parameter strings ( Parameters( { "PARAMETER1": "Value1", "ALTREP": ["http://www.wiz.org", "value4"], "PARAMETER2": ["Value2", "Value3"], } ), b'ALTREP="http://www.wiz.org","value4";PARAMETER1=Value1;PARAMETER2=Value2,Value3', ), ], ) def test_parameter_to_ical_is_inverse_of_from_ical(parameter, expected): assert parameter.to_ical() == expected assert Parameters.from_ical(expected.decode("utf-8")) == parameter def test_parse_parameter_string_without_quotes(): assert Parameters.from_ical("PARAM1=Value 1;PARA2=Value 2") == Parameters( {"PARAM1": "Value 1", "PARA2": "Value 2"} ) def test_parametr_is_case_insensitive(): parameter = Parameters(parameter1="Value1") assert parameter["parameter1"] == parameter["PARAMETER1"] == parameter["PaRaMeTer1"] def test_parameter_keys_are_uppercase(): parameter = Parameters(parameter1="Value1") assert list(parameter.keys()) == ["PARAMETER1"] @pytest.mark.parametrize( ("cn_param", "cn_quoted"), [ # not double-quoted ("Aramis", "Aramis"), # if a space is present - enclose in double quotes ("Aramis Alameda", '"Aramis Alameda"'), # a single quote in parameter value - double quote the value ("Aramis d'Alameda", '"Aramis d\'Alameda"'), ("Арамис д'Аламеда", '"Арамис д\'Аламеда"'), # Before, double quote is replaced with single quote # Since RFC 6868, we replace this with ^' ('Aramis d"Alameda', '"Aramis d^\'Alameda"'), ], ) def test_quoting(cn_param, cn_quoted): event = Event() attendee = vCalAddress("test@example.com") attendee.params["CN"] = cn_param event.add("ATTENDEE", attendee) assert f"ATTENDEE;CN={cn_quoted}:test@example.com" in event.to_ical().decode( "utf-8" ) def test_property_params(): """Property parameters with values containing a COLON character, a SEMICOLON character or a COMMA character MUST be placed in quoted text.""" cal_address = vCalAddress("mailto:john.doe@example.org") cal_address.params["CN"] = "Doe, John" ical = Calendar() ical.add("organizer", cal_address) ical_str = Calendar.to_ical(ical) exp_str = ( b"""BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":""" b"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n""" ) assert ical_str == exp_str # other way around: ensure the property parameters can be restored from # an icalendar string. ical2 = Calendar.from_ical(ical_str) assert ical2.get("ORGANIZER").params.get("CN") == "Doe, John" def test_parse_and_access_property_params(calendars): """Parse an ics string and access some property parameters then. This is a follow-up of a question received per email. """ event = calendars.property_params.walk("VEVENT")[0] attendee = event["attendee"][0] assert attendee.to_ical() == b"MAILTO:rembrand@xs4all.nl" assert attendee.params.to_ical() == b"CN=RembrandXS;PARTSTAT=NEEDS-ACTION;RSVP=TRUE" assert attendee.params["cn"] == "RembrandXS" def test_repr(): """Test correct class representation.""" it = Parameters(parameter1="Value1") assert re.match(r"Parameters\({u?'PARAMETER1': u?'Value1'}\)", str(it))