Files
syn-chat-bot/.venv/lib/python3.9/site-packages/icalendar/tests/test_unit_cal.py
Hyungi Ahn c2257d3a86 fix: 포트 충돌 회피 — note_bridge 8098, intent_service 8099
Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:53:55 +09:00

490 lines
16 KiB
Python

import itertools
import re
from datetime import datetime, timedelta
import pytest
import icalendar
from icalendar import prop
from icalendar.cal import Calendar, Component, Event
from icalendar.prop import tzid_from_dt
def test_cal_Component(calendar_component):
"""A component is like a dictionary with extra methods and attributes."""
assert calendar_component
assert calendar_component.is_empty()
def test_nonempty_calendar_component(calendar_component):
"""Every key defines a property.A property can consist of either a
single item. This can be set with a single value...
"""
calendar_component["prodid"] = "-//max m//icalendar.mxm.dk/"
assert not calendar_component.is_empty()
assert calendar_component == Calendar({"PRODID": "-//max m//icalendar.mxm.dk/"})
# or with a list
calendar_component["ATTENDEE"] = ["Max M", "Rasmussen"]
assert calendar_component == Calendar(
{"ATTENDEE": ["Max M", "Rasmussen"], "PRODID": "-//max m//icalendar.mxm.dk/"}
)
def test_add_multiple_values(event_component):
"""add multiple values to a property.
If you use the add method you don't have to considder if a value is
a list or not.
"""
# add multiple values at once
event_component.add("attendee", ["test@test.com", "test2@test.com"])
# or add one per line
event_component.add("attendee", "maxm@mxm.dk")
event_component.add("attendee", "test@example.dk")
# add again multiple values at once to very concatenaton of lists
event_component.add("attendee", ["test3@test.com", "test4@test.com"])
assert event_component == Event(
{
"ATTENDEE": [
prop.vCalAddress("test@test.com"),
prop.vCalAddress("test2@test.com"),
prop.vCalAddress("maxm@mxm.dk"),
prop.vCalAddress("test@example.dk"),
prop.vCalAddress("test3@test.com"),
prop.vCalAddress("test4@test.com"),
]
}
)
def test_get_content_directly(c):
"""You can get the values back directly ..."""
c.add("prodid", "-//my product//")
assert c["prodid"] == prop.vText("-//my product//")
# ... or decoded to a python type
assert c.decoded("prodid") == b"-//my product//"
def test_get_default_value(c):
"""With default values for non existing properties"""
assert c.decoded("version", "No Version") == "No Version"
def test_default_list_example(c):
c.add("rdate", [datetime(2013, 3, 28), datetime(2013, 3, 27)])
assert isinstance(c.decoded("rdate"), prop.vDDDLists)
def test_render_component(calendar_component):
"""The component can render itself in the RFC 5545 format."""
calendar_component.add("attendee", "Max M")
assert (
calendar_component.to_ical()
== b"BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n"
)
def test_nested_component_event_ics(filled_event_component):
"""Check the ical string of the event component."""
assert filled_event_component.to_ical() == (
b"BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n"
+ b"DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r"
+ b"\nEND:VEVENT\r\n"
)
def test_nested_components(calendar_component, filled_event_component):
"""Components can be nested, so You can add a subcomponent. Eg a calendar
holds events."""
self.assertEqual(
calendar_component.subcomponents,
[
Event(
{
"DTEND": "20000102T000000",
"DTSTART": "20000101T000000",
"SUMMARY": "A brief history of time",
}
)
],
)
def test_walk_filled_calendar_component(calendar_component, filled_event_component):
"""We can walk over nested componentes with the walk method."""
assert [i.name for i in calendar_component.walk()] == ["VCALENDAR", "VEVENT"]
def test_filter_walk(calendar_component, filled_event_component):
"""We can also just walk over specific component types, by filtering
them on their name."""
assert [i.name for i in calendar_component.walk("VEVENT")] == ["VEVENT"]
assert [i["dtstart"] for i in calendar_component.walk("VEVENT")] == [
"20000101T000000"
]
def test_recursive_property_items(calendar_component, filled_event_component):
"""We can enumerate property items recursively with the property_items
method."""
calendar_component.add("attendee", "Max M")
assert calendar_component.property_items() == [
("BEGIN", b"VCALENDAR"),
("ATTENDEE", prop.vCalAddress("Max M")),
("BEGIN", b"VEVENT"),
("DTEND", "20000102T000000"),
("DTSTART", "20000101T000000"),
("SUMMARY", "A brief history of time"),
("END", b"VEVENT"),
("END", b"VCALENDAR"),
]
def test_flat_property_items(calendar_component, filled_event_component):
"""We can also enumerate property items just under the component."""
assert calendar_component.property_items(recursive=False) == [
("BEGIN", b"VCALENDAR"),
("ATTENDEE", prop.vCalAddress("Max M")),
("END", b"VCALENDAR"),
]
def test_flat_property_items(filled_event_component):
"""Flat enumeration on the event."""
assert filled_event_component.property_items(recursive=False) == [
("BEGIN", b"VEVENT"),
("DTEND", "20000102T000000"),
("DTSTART", "20000101T000000"),
("SUMMARY", "A brief history of time"),
("END", b"VEVENT"),
]
def test_indent():
"""Text fields which span multiple mulitple lines require proper indenting"""
c = Calendar()
c["description"] = "Paragraph one\n\nParagraph two"
assert c.to_ical() == (
b"BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two"
+ b"\r\nEND:VCALENDAR\r\n"
)
def test_INLINE_properties(calendar_with_resources):
"""INLINE properties have their values on one property line. Note the
double quoting of the value with a colon in it.
"""
assert calendar_with_resources == Calendar(
{"RESOURCES": 'Chair, Table, "Room: 42"'}
)
assert calendar_with_resources.to_ical() == (
b'BEGIN:VCALENDAR\r\nRESOURCES:Chair\\, Table\\, "Room: 42"\r\n'
+ b"END:VCALENDAR\r\n"
)
def test_get_inline(calendar_with_resources):
"""The inline values must be handled by the get_inline() and
set_inline() methods.
"""
assert calendar_with_resources.get_inline("resources", decode=0) == [
"Chair",
"Table",
"Room: 42",
]
def test_get_inline_decoded(calendar_with_resources):
"""These can also be decoded"""
assert calendar_with_resources.get_inline("resources", decode=1) == [
b"Chair",
b"Table",
b"Room: 42",
]
def test_set_inline(calendar_with_resources):
"""You can set them directly ..."""
calendar_with_resources.set_inline(
"resources", ["A", "List", "of", "some, recources"], encode=1
)
assert calendar_with_resources["resources"] == 'A,List,of,"some, recources"'
assert calendar_with_resources.get_inline("resources", decode=0) == [
"A",
"List",
"of",
"some, recources",
]
def test_inline_free_busy_inline(c):
c["freebusy"] = (
"19970308T160000Z/PT3H,19970308T200000Z/PT1H,"
+ "19970308T230000Z/19970309T000000Z"
)
assert c.get_inline("freebusy", decode=0) == [
"19970308T160000Z/PT3H",
"19970308T200000Z/PT1H",
"19970308T230000Z/19970309T000000Z",
]
freebusy = c.get_inline("freebusy", decode=1)
assert isinstance(freebusy[0][0], datetime)
assert isinstance(freebusy[0][1], timedelta)
def test_cal_Component_add(comp, tzp):
"""Test the for timezone correctness: dtstart should preserve it's
timezone, created, dtstamp and last-modified must be in UTC.
"""
comp.add("dtstart", tzp.localize(datetime(2010, 10, 10, 10, 0, 0), "Europe/Vienna"))
comp.add("created", datetime(2010, 10, 10, 12, 0, 0))
comp.add("dtstamp", tzp.localize(datetime(2010, 10, 10, 14, 0, 0), "Europe/Vienna"))
comp.add("last-modified", tzp.localize_utc(datetime(2010, 10, 10, 16, 0, 0)))
lines = comp.to_ical().splitlines()
assert b"DTSTART;TZID=Europe/Vienna:20101010T100000" in lines
assert b"CREATED:20101010T120000Z" in lines
assert b"DTSTAMP:20101010T120000Z" in lines
assert b"LAST-MODIFIED:20101010T160000Z" in lines
def test_cal_Component_add_no_reencode(comp):
"""Already encoded values should not be re-encoded."""
comp.add("ATTACH", "me")
comp.add("ATTACH", "you", encode=False)
binary = prop.vBinary("us")
comp.add("ATTACH", binary)
assert comp["ATTACH"] == ["me", "you", binary]
def test_cal_Component_add_property_parameter(comp):
"""Test the for timezone correctness: dtstart should preserve it's
timezone, crated, dtstamp and last-modified must be in UTC.
"""
comp.add("X-TEST-PROP", "tryout.", parameters={"prop1": "val1", "prop2": "val2"})
lines = comp.to_ical().splitlines()
assert b"X-TEST-PROP;PROP1=val1;PROP2=val2:tryout." in lines
comp_prop = pytest.mark.parametrize(
"component_name, property_name",
[
("VEVENT", "DTSTART"),
("VEVENT", "DTEND"),
("VEVENT", "RECURRENCE-ID"),
("VTODO", "DUE"),
],
)
@comp_prop
def test_cal_Component_from_ical(component_name, property_name, tzp):
"""Check for proper handling of TZID parameter of datetime properties"""
component_str = "BEGIN:" + component_name + "\n"
component_str += property_name + ";TZID=America/Denver:"
component_str += "20120404T073000\nEND:" + component_name
component = Component.from_ical(component_str)
assert tzid_from_dt(component[property_name].dt) == "America/Denver"
@comp_prop
def test_cal_Component_from_ical_2(component_name, property_name, tzp):
"""Check for proper handling of TZID parameter of datetime properties"""
component_str = "BEGIN:" + component_name + "\n"
component_str += property_name + ":"
component_str += "20120404T073000\nEND:" + component_name
component = Component.from_ical(component_str)
assert component[property_name].dt.tzinfo == None
def test_cal_Component_to_ical_property_order():
component_str = [
b"BEGIN:VEVENT",
b"DTSTART:19970714T170000Z",
b"DTEND:19970715T035959Z",
b"SUMMARY:Bastille Day Party",
b"END:VEVENT",
]
component = Component.from_ical(b"\r\n".join(component_str))
sorted_str = component.to_ical().splitlines()
assert sorted_str != component_str
assert set(sorted_str) == set(component_str)
preserved_str = component.to_ical(sorted=False).splitlines()
assert preserved_str == component_str
def test_cal_Component_to_ical_parameter_order():
component_str = [
b"BEGIN:VEVENT",
b"X-FOOBAR;C=one;A=two;B=three:helloworld.",
b"END:VEVENT",
]
component = Component.from_ical(b"\r\n".join(component_str))
sorted_str = component.to_ical().splitlines()
assert sorted_str[0] == component_str[0]
assert sorted_str[1] == b"X-FOOBAR;A=two;B=three;C=one:helloworld."
assert sorted_str[2] == component_str[2]
preserved_str = component.to_ical(sorted=False).splitlines()
assert preserved_str == component_str
@pytest.fixture
def repr_example(c):
class ReprExample:
component = c
component["key1"] = "value1"
calendar = Calendar()
calendar["key1"] = "value1"
event = Event()
event["key1"] = "value1"
nested = Component(key1="VALUE1")
nested.add_component(component)
nested.add_component(calendar)
return ReprExample
def test_repr_component(repr_example):
"""Test correct class representation."""
assert re.match(r"Component\({u?'KEY1': u?'value1'}\)", str(repr_example.component))
def test_repr_calendar(repr_example):
assert re.match(r"VCALENDAR\({u?'KEY1': u?'value1'}\)", str(repr_example.calendar))
def test_repr_event(repr_example):
assert re.match(r"VEVENT\({u?'KEY1': u?'value1'}\)", str(repr_example.event))
def test_nested_components(repr_example):
"""Representation of nested Components"""
repr_example.calendar.add_component(repr_example.event)
print(repr_example.nested)
assert re.match(
r"Component\({u?'KEY1': u?'VALUE1'}, "
r"Component\({u?'KEY1': u?'value1'}\), "
r"VCALENDAR\({u?'KEY1': u?'value1'}, "
r"VEVENT\({u?'KEY1': u?'value1'}\)\)\)",
str(repr_example.nested),
)
def test_component_factory_VEVENT(factory):
"""Check the events in the component factory"""
component = factory["VEVENT"]
event = component(dtstart="19700101")
assert event.to_ical() == b"BEGIN:VEVENT\r\nDTSTART:19700101\r\nEND:VEVENT\r\n"
def test_component_factory_VCALENDAR(factory):
"""Check the VCALENDAR in the factory."""
assert factory.get("VCALENDAR") == icalendar.cal.Calendar
def test_minimal_calendar_component_with_one_event():
"""Setting up a minimal calendar component looks like this"""
cal = Calendar()
# Some properties are required to be compliant
cal["prodid"] = "-//My calendar product//mxm.dk//"
cal["version"] = "2.0"
# We also need at least one subcomponent for a calendar to be compliant
event = Event()
event["summary"] = "Python meeting about calendaring"
event["uid"] = "42"
event.add("dtstart", datetime(2005, 4, 4, 8, 0, 0))
cal.add_component(event)
assert (
cal.subcomponents[0].to_ical()
== b"BEGIN:VEVENT\r\nSUMMARY:Python meeting about calendaring\r\n"
+ b"DTSTART:20050404T080000\r\nUID:42\r\n"
+ b"END:VEVENT\r\n"
)
def test_calendar_with_parsing_errors_includes_all_events(calendars):
"""Parsing a complete calendar from a string will silently ignore wrong
events but adding the error information to the component's 'errors'
attribute. The error in the following is the third EXDATE: it has an
empty DATE.
"""
event_descriptions = [
e["DESCRIPTION"].to_ical() for e in calendars.parsing_error.walk("VEVENT")
]
assert event_descriptions == [b"Perfectly OK event", b"Wrong event"]
def test_calendar_with_parsing_errors_has_an_error_in_one_event(calendars):
"""Parsing a complete calendar from a string will silently ignore wrong
events but adding the error information to the component's 'errors'
attribute. The error in the following is the third EXDATE: it has an
empty DATE.
"""
errors = [e.errors for e in calendars.parsing_error.walk("VEVENT")]
assert errors == [[], [("EXDATE", "Expected datetime, date, or time, got: ''")]]
def test_cal_strict_parsing(calendars):
"""If components are damaged, we raise an exception."""
with pytest.raises(ValueError):
calendars.parsing_error_in_UTC_offset
def test_cal_ignore_errors_parsing(calendars, vUTCOffset_ignore_exceptions):
"""If we diable the errors, we should be able to put the calendar back together."""
assert (
calendars.parsing_error_in_UTC_offset.to_ical()
== calendars.parsing_error_in_UTC_offset.raw_ics
)
@pytest.mark.parametrize(
("calendar", "other_calendar"),
itertools.product(
[
"issue_156_RDATE_with_PERIOD_TZID_khal",
"issue_156_RDATE_with_PERIOD_TZID_khal_2",
"issue_178_custom_component_contains_other",
"issue_178_custom_component_inside_other",
"issue_526_calendar_with_events",
"issue_526_calendar_with_different_events",
"issue_526_calendar_with_event_subset",
],
repeat=2,
),
)
def test_comparing_calendars(calendars, calendar, other_calendar, tzp):
are_calendars_equal = calendars[calendar] == calendars[other_calendar]
are_calendars_actually_equal = calendar == other_calendar
assert are_calendars_equal == are_calendars_actually_equal
@pytest.mark.parametrize(
("calendar", "shuffeled_calendar"),
[
(
"issue_526_calendar_with_events",
"issue_526_calendar_with_shuffeled_events",
),
],
)
def test_calendars_with_same_subcomponents_in_different_order_are_equal(
calendars, calendar, shuffeled_calendar
):
assert (
calendars[calendar].subcomponents != calendars[shuffeled_calendar].subcomponents
)
assert calendars[calendar] == calendars[shuffeled_calendar]