Changeset View
Changeset View
Standalone View
Standalone View
tests/unit/test-011-itip.py
Show First 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | |||||
END:VEVENT | END:VEVENT | ||||
END:VCALENDAR | END:VCALENDAR | ||||
""" | """ | ||||
itip_google_multipart = """MIME-Version: 1.0 | itip_google_multipart = """MIME-Version: 1.0 | ||||
Message-ID: <001a11c2ad84243e0604f3246bae@google.com> | Message-ID: <001a11c2ad84243e0604f3246bae@google.com> | ||||
Date: Mon, 24 Feb 2014 10:27:28 +0000 | Date: Mon, 24 Feb 2014 10:27:28 +0000 | ||||
Subject: =?ISO-8859-1?Q?Invitation=3A_iTip_from_Apple_=40_Mon_Feb_24=2C_2014_12pm_?= | Subject: =?ISO-8859-1?Q?Invitation=3A_iTip_from_Apple_=40_Mon_Feb_24=2C_2014_12pm_?= | ||||
=?ISO-8859-1?Q?=2D_1pm_=28Tom_=26_T=E4m=29?= | =?ISO-8859-1?Q?=2D_1pm_=28Tom_=26_T=E4m=29?= | ||||
From: "john.doe" <john.doe@gmail.com> | From: "john.doe" <john.doe@gmail.com> | ||||
To: <john.sample@example.org> | To: <john.sample@example.org> | ||||
Content-Type: multipart/mixed; boundary=001a11c2ad84243df004f3246bad | Content-Type: multipart/mixed; boundary=001a11c2ad84243df004f3246bad | ||||
--001a11c2ad84243df004f3246bad | --001a11c2ad84243df004f3246bad | ||||
Content-Type: multipart/alternative; boundary=001a11c2ad84243dec04f3246bab | Content-Type: multipart/alternative; boundary=001a11c2ad84243dec04f3246bab | ||||
--001a11c2ad84243dec04f3246bab | --001a11c2ad84243dec04f3246bab | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | |||||
Message plain text goes here... | Message plain text goes here... | ||||
""" | """ | ||||
conf = pykolab.getConf() | conf = pykolab.getConf() | ||||
if not hasattr(conf, 'defaults'): | if not hasattr(conf, 'defaults'): | ||||
conf.finalize_conf() | conf.finalize_conf() | ||||
class TestITip(unittest.TestCase): | class TestITip(unittest.TestCase): | ||||
def setUp(self): | def setUp(self): | ||||
# intercept calls to smtplib.SMTP.sendmail() | # intercept calls to smtplib.SMTP.sendmail() | ||||
import smtplib | import smtplib | ||||
self.patch(smtplib.SMTP, "__init__", self._mock_smtp_init) | self.patch(smtplib.SMTP, "__init__", self._mock_smtp_init) | ||||
self.patch(smtplib.SMTP, "quit", self._mock_nop) | self.patch(smtplib.SMTP, "quit", self._mock_nop) | ||||
self.patch(smtplib.SMTP, "sendmail", self._mock_smtp_sendmail) | self.patch(smtplib.SMTP, "sendmail", self._mock_smtp_sendmail) | ||||
self.smtplog = []; | self.smtplog = [] | ||||
def _mock_nop(self, domain=None): | def _mock_nop(self, domain=None): | ||||
pass | pass | ||||
def _mock_smtp_init(self, host=None, port=None, local_hostname=None, timeout=0): | def _mock_smtp_init(self, host=None, port=None, local_hostname=None, timeout=0): | ||||
pass | pass | ||||
def _mock_smtp_sendmail(self, from_addr, to_addr, message, mail_options=None, rcpt_options=None): | def _mock_smtp_sendmail(self, from_addr, to_addr, message, mail_options=None, rcpt_options=None): | ||||
self.smtplog.append((from_addr, to_addr, message)) | self.smtplog.append((from_addr, to_addr, message)) | ||||
def test_001_itip_events_from_message(self): | def test_001_itip_events_from_message(self): | ||||
itips1 = itip.events_from_message(message_from_string(itip_multipart)) | itips1 = itip.events_from_message(message_from_string(itip_multipart)) | ||||
self.assertEqual(len(itips1), 1, "Multipart iTip message with text/calendar") | self.assertEqual(len(itips1), 1, "Multipart iTip message with text/calendar") | ||||
self.assertEqual(itips1[0]['method'], "REQUEST", "iTip request method property") | self.assertEqual(itips1[0]['method'], "REQUEST", "iTip request method property") | ||||
itips2 = itip.events_from_message(message_from_string(itip_non_multipart)) | itips2 = itip.events_from_message(message_from_string(itip_non_multipart)) | ||||
self.assertEqual(len(itips2), 1, "Detect non-multipart iTip messages") | self.assertEqual(len(itips2), 1, "Detect non-multipart iTip messages") | ||||
Show All 17 Lines | def test_001_itip_events_from_message(self): | ||||
# iTips with unicode data | # iTips with unicode data | ||||
itips8 = itip.events_from_message(message_from_string(itip_unicode)) | itips8 = itip.events_from_message(message_from_string(itip_unicode)) | ||||
self.assertEqual(len(itips8), 1) | self.assertEqual(len(itips8), 1) | ||||
xml = itips8[0]['xml'] | xml = itips8[0]['xml'] | ||||
self.assertEqual(xml.get_summary(), "Testing Ümlauts") | self.assertEqual(xml.get_summary(), "Testing Ümlauts") | ||||
self.assertEqual(xml.get_location(), "Rue the Genève") | self.assertEqual(xml.get_location(), "Rue the Genève") | ||||
def test_002_check_date_conflict(self): | def test_002_check_date_conflict(self): | ||||
astart = datetime.datetime(2014,7,13, 10,0,0) | astart = datetime.datetime(2014, 7, 13, 10, 0, 0) | ||||
aend = astart + datetime.timedelta(hours=2) | aend = astart + datetime.timedelta(hours=2) | ||||
bstart = datetime.datetime(2014,7,13, 10,0,0) | bstart = datetime.datetime(2014, 7, 13, 10, 0, 0) | ||||
bend = astart + datetime.timedelta(hours=1) | bend = astart + datetime.timedelta(hours=1) | ||||
self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | ||||
bstart = datetime.datetime(2014,7,13, 11,0,0) | bstart = datetime.datetime(2014, 7, 13, 11, 0, 0) | ||||
bend = astart + datetime.timedelta(minutes=30) | bend = astart + datetime.timedelta(minutes=30) | ||||
self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | ||||
bend = astart + datetime.timedelta(hours=2) | bend = astart + datetime.timedelta(hours=2) | ||||
self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | ||||
bstart = datetime.datetime(2014,7,13, 12,0,0) | bstart = datetime.datetime(2014, 7, 13, 12, 0, 0) | ||||
bend = astart + datetime.timedelta(hours=1) | bend = astart + datetime.timedelta(hours=1) | ||||
self.assertFalse(itip.check_date_conflict(astart, aend, bstart, bend)) | self.assertFalse(itip.check_date_conflict(astart, aend, bstart, bend)) | ||||
self.assertFalse(itip.check_date_conflict(bstart, bend, astart, aend)) | self.assertFalse(itip.check_date_conflict(bstart, bend, astart, aend)) | ||||
bstart = datetime.datetime(2014,6,13, 10,0,0) | bstart = datetime.datetime(2014, 6, 13, 10, 0, 0) | ||||
bend = datetime.datetime(2014,6,14, 12,0,0) | bend = datetime.datetime(2014, 6, 14, 12, 0, 0) | ||||
self.assertFalse(itip.check_date_conflict(astart, aend, bstart, bend)) | self.assertFalse(itip.check_date_conflict(astart, aend, bstart, bend)) | ||||
bstart = datetime.datetime(2014,7,10, 12,0,0) | bstart = datetime.datetime(2014, 7, 10, 12, 0, 0) | ||||
bend = datetime.datetime(2014,7,14, 14,0,0) | bend = datetime.datetime(2014, 7, 14, 14, 0, 0) | ||||
self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) | ||||
def test_002_check_event_conflict(self): | def test_002_check_event_conflict(self): | ||||
itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | ||||
event = Event() | event = Event() | ||||
event.set_start(datetime.datetime(2012,7,13, 9,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_start(datetime.datetime(2012, 7, 13, 9, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
event.set_end(datetime.datetime(2012,7,13, 10,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_end(datetime.datetime(2012, 7, 13, 10, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates") | self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates") | ||||
event.set_uid(itip_event['uid']) | event.set_uid(itip_event['uid']) | ||||
self.assertFalse(itip.check_event_conflict(event, itip_event), "No conflict for same UID") | self.assertFalse(itip.check_event_conflict(event, itip_event), "No conflict for same UID") | ||||
allday = Event() | allday = Event() | ||||
allday.set_start(datetime.date(2012,7,13)) | allday.set_start(datetime.date(2012, 7, 13)) | ||||
allday.set_end(datetime.date(2012,7,13)) | allday.set_end(datetime.date(2012, 7, 13)) | ||||
self.assertTrue(itip.check_event_conflict(allday, itip_event), "Conflicting allday event") | self.assertTrue(itip.check_event_conflict(allday, itip_event), "Conflicting allday event") | ||||
allday.set_transparency(True) | allday.set_transparency(True) | ||||
self.assertFalse(itip.check_event_conflict(allday, itip_event), "No conflict if event is set to transparent") | self.assertFalse(itip.check_event_conflict(allday, itip_event), "No conflict if event is set to transparent") | ||||
event2 = Event() | event2 = Event() | ||||
event2.set_start(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("US/Central"))) | event2.set_start(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("US/Central"))) | ||||
event2.set_end(datetime.datetime(2012,7,13, 11,0,0, tzinfo=pytz.timezone("US/Central"))) | event2.set_end(datetime.datetime(2012, 7, 13, 11, 0, 0, tzinfo=pytz.timezone("US/Central"))) | ||||
self.assertFalse(itip.check_event_conflict(event, itip_event), "No conflict with timezone shift") | self.assertFalse(itip.check_event_conflict(event, itip_event), "No conflict with timezone shift") | ||||
rrule = kolabformat.RecurrenceRule() | rrule = kolabformat.RecurrenceRule() | ||||
rrule.setFrequency(kolabformat.RecurrenceRule.Weekly) | rrule.setFrequency(kolabformat.RecurrenceRule.Weekly) | ||||
rrule.setCount(10) | rrule.setCount(10) | ||||
event3 = Event() | event3 = Event() | ||||
event3.set_recurrence(rrule); | event3.set_recurrence(rrule) | ||||
event3.set_start(datetime.datetime(2012,6,29, 9,30,0, tzinfo=pytz.utc)) | event3.set_start(datetime.datetime(2012, 6, 29, 9, 30, 0, tzinfo=pytz.utc)) | ||||
event3.set_end(datetime.datetime(2012,6,29, 10,30,0, tzinfo=pytz.utc)) | event3.set_end(datetime.datetime(2012, 6, 29, 10, 30, 0, tzinfo=pytz.utc)) | ||||
self.assertTrue(itip.check_event_conflict(event3, itip_event), "Conflict in (3rd) recurring event instance") | self.assertTrue(itip.check_event_conflict(event3, itip_event), "Conflict in (3rd) recurring event instance") | ||||
itip_event = itip.events_from_message(message_from_string(itip_recurring))[0] | itip_event = itip.events_from_message(message_from_string(itip_recurring))[0] | ||||
self.assertTrue(itip.check_event_conflict(event3, itip_event), "Conflict in two recurring events") | self.assertTrue(itip.check_event_conflict(event3, itip_event), "Conflict in two recurring events") | ||||
event4 = Event() | event4 = Event() | ||||
event4.set_recurrence(rrule); | event4.set_recurrence(rrule) | ||||
event4.set_start(datetime.datetime(2012,7,1, 9,30,0, tzinfo=pytz.utc)) | event4.set_start(datetime.datetime(2012, 7, 1, 9, 30, 0, tzinfo=pytz.utc)) | ||||
event4.set_end(datetime.datetime(2012,7,1, 10,30,0, tzinfo=pytz.utc)) | event4.set_end(datetime.datetime(2012, 7, 1, 10, 30, 0, tzinfo=pytz.utc)) | ||||
self.assertFalse(itip.check_event_conflict(event4, itip_event), "No conflict in two recurring events") | self.assertFalse(itip.check_event_conflict(event4, itip_event), "No conflict in two recurring events") | ||||
itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | ||||
rrule.setFrequency(kolabformat.RecurrenceRule.Daily) | rrule.setFrequency(kolabformat.RecurrenceRule.Daily) | ||||
rrule.setCount(10) | rrule.setCount(10) | ||||
event5 = Event() | event5 = Event() | ||||
event5.set_recurrence(rrule); | event5.set_recurrence(rrule) | ||||
event5.set_start(datetime.datetime(2012,7,9, 10,0,0, tzinfo=pytz.timezone("Europe/London"))) | event5.set_start(datetime.datetime(2012, 7, 9, 10, 0, 0, tzinfo=pytz.timezone("Europe/London"))) | ||||
event5.set_end(datetime.datetime(2012,7,9, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) | event5.set_end(datetime.datetime(2012, 7, 9, 11, 0, 0, tzinfo=pytz.timezone("Europe/London"))) | ||||
event_xml = str(event5) | event_xml = str(event5) | ||||
exception = Event(from_string=event_xml) | exception = Event(from_string=event_xml) | ||||
exception.set_start(datetime.datetime(2012,7,13, 14,0,0, tzinfo=pytz.timezone("Europe/London"))) | exception.set_start(datetime.datetime(2012, 7, 13, 14, 0, 0, tzinfo=pytz.timezone("Europe/London"))) | ||||
exception.set_end(datetime.datetime(2012,7,13, 16,0,0, tzinfo=pytz.timezone("Europe/London"))) | exception.set_end(datetime.datetime(2012, 7, 13, 16, 0, 0, tzinfo=pytz.timezone("Europe/London"))) | ||||
exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False) | exception.set_recurrence_id(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("Europe/London")), False) | ||||
event5.add_exception(exception) | event5.add_exception(exception) | ||||
self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with exception date") | self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with exception date") | ||||
exception = Event(from_string=event_xml) | exception = Event(from_string=event_xml) | ||||
exception.set_start(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London"))) | exception.set_start(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("Europe/London"))) | ||||
exception.set_end(datetime.datetime(2012,7,13, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) | exception.set_end(datetime.datetime(2012, 7, 13, 11, 0, 0, tzinfo=pytz.timezone("Europe/London"))) | ||||
exception.set_status('CANCELLED') | exception.set_status('CANCELLED') | ||||
exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False) | exception.set_recurrence_id(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("Europe/London")), False) | ||||
event5.add_exception(exception) | event5.add_exception(exception) | ||||
self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with cancelled exception") | self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with cancelled exception") | ||||
def test_002_check_event_conflict_single(self): | def test_002_check_event_conflict_single(self): | ||||
itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | ||||
event = Event() | event = Event() | ||||
event.set_start(datetime.datetime(2012,7,10, 9,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_start(datetime.datetime(2012, 7, 10, 9, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
event.set_end(datetime.datetime(2012,7,10, 10,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_end(datetime.datetime(2012, 7, 10, 10, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
event.set_recurrence_id(event.get_start()) | event.set_recurrence_id(event.get_start()) | ||||
dtstart = datetime.datetime(2012,7,13, 9,30,0, tzinfo=itip_event['start'].tzinfo) | dtstart = datetime.datetime(2012, 7, 13, 9, 30, 0, tzinfo=itip_event['start'].tzinfo) | ||||
second = Event(from_string=str(event)) | second = Event(from_string=str(event)) | ||||
second.set_start(dtstart) | second.set_start(dtstart) | ||||
second.set_end(dtstart + datetime.timedelta(hours=1)) | second.set_end(dtstart + datetime.timedelta(hours=1)) | ||||
second.set_recurrence_id(dtstart) | second.set_recurrence_id(dtstart) | ||||
event.add_exception(second) | event.add_exception(second) | ||||
self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") | self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") | ||||
itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] | ||||
dtstart = datetime.datetime(2012,7,15, 10,0,0, tzinfo=itip_event['start'].tzinfo) | dtstart = datetime.datetime(2012, 7, 15, 10, 0, 0, tzinfo=itip_event['start'].tzinfo) | ||||
second = Event(from_string=str(itip_event['xml'])) | second = Event(from_string=str(itip_event['xml'])) | ||||
second.set_start(dtstart + datetime.timedelta(hours=1)) | second.set_start(dtstart + datetime.timedelta(hours=1)) | ||||
second.set_end(dtstart + datetime.timedelta(hours=2)) | second.set_end(dtstart + datetime.timedelta(hours=2)) | ||||
second.set_recurrence_id(dtstart) | second.set_recurrence_id(dtstart) | ||||
second.set_transparency(True) | second.set_transparency(True) | ||||
itip_event['xml'].add_exception(second) | itip_event['xml'].add_exception(second) | ||||
self.assertEqual(len(itip_event['xml'].get_exceptions()), 1) | self.assertEqual(len(itip_event['xml'].get_exceptions()), 1) | ||||
event = Event() | event = Event() | ||||
event.set_start(datetime.datetime(2012,7,11, 9,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_start(datetime.datetime(2012, 7, 11, 9, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
event.set_end(datetime.datetime(2012,7,11, 10,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_end(datetime.datetime(2012, 7, 11, 10, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (no)") | self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (no)") | ||||
event = Event() | event = Event() | ||||
event.set_start(datetime.datetime(2012,7,15, 11,0,0, tzinfo=itip_event['start'].tzinfo)) | event.set_start(datetime.datetime(2012, 7, 15, 11, 0, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
event.set_end(datetime.datetime(2012,7,15, 11,30,0, tzinfo=itip_event['start'].tzinfo)) | event.set_end(datetime.datetime(2012, 7, 15, 11, 30, 0, tzinfo=itip_event['start'].tzinfo)) | ||||
self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") | self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") | ||||
def test_003_send_reply(self): | def test_003_send_reply(self): | ||||
itip_events = itip.events_from_message(message_from_string(itip_non_multipart)) | itip_events = itip.events_from_message(message_from_string(itip_non_multipart)) | ||||
itip.send_reply("resource-collection-car@example.org", itip_events, "SUMMARY=%(summary)s; STATUS=%(status)s; NAME=%(name)s;") | itip.send_reply("resource-collection-car@example.org", itip_events, "SUMMARY=%(summary)s; STATUS=%(status)s; NAME=%(name)s;") | ||||
self.assertEqual(len(self.smtplog), 1) | self.assertEqual(len(self.smtplog), 1) | ||||
self.assertEqual(self.smtplog[0][0], 'resource-collection-car@example.org', "From attendee") | self.assertEqual(self.smtplog[0][0], 'resource-collection-car@example.org', "From attendee") | ||||
self.assertEqual(self.smtplog[0][1], 'john.doe@example.org', "To organizer") | self.assertEqual(self.smtplog[0][1], 'john.doe@example.org', "To organizer") | ||||
_accepted = participant_status_label('ACCEPTED') | _accepted = participant_status_label('ACCEPTED') | ||||
message = message_from_string(self.smtplog[0][2]) | message = message_from_string(self.smtplog[0][2]) | ||||
self.assertEqual(message.get('Subject'), _("Invitation for %(summary)s was %(status)s") % { 'summary':'test', 'status':_accepted }) | self.assertEqual(message.get('Subject'), _("Invitation for %(summary)s was %(status)s") % {'summary': 'test', 'status': _accepted}) | ||||
text = str(message.get_payload(0)); | text = str(message.get_payload(0)) | ||||
self.assertIn('SUMMARY=3Dtest', text) | self.assertIn('SUMMARY=3Dtest', text) | ||||
self.assertIn('STATUS=3D' + _accepted, text) | self.assertIn('STATUS=3D' + _accepted, text) | ||||
def test_004_send_reply_unicode(self): | def test_004_send_reply_unicode(self): | ||||
itip_events = itip.events_from_message(message_from_string(itip_non_multipart.replace('SUMMARY:test', "SUMMARY:With äöü"))) | itip_events = itip.events_from_message(message_from_string(itip_non_multipart.replace('SUMMARY:test', "SUMMARY:With äöü"))) | ||||
itip.send_reply("resource-collection-car@example.org", itip_events, "SUMMARY=%(summary)s; STATUS=%(status)s; NAME=%(name)s;") | itip.send_reply("resource-collection-car@example.org", itip_events, "SUMMARY=%(summary)s; STATUS=%(status)s; NAME=%(name)s;") | ||||
self.assertEqual(len(self.smtplog), 1) | self.assertEqual(len(self.smtplog), 1) | ||||
self.assertIn("Subject: =?utf-8?q?Invitation_for_With_=C3=A4=C3=B6=C3=BC_was_Accepted?=", self.smtplog[0][2]) | self.assertIn("Subject: =?utf-8?q?Invitation_for_With_=C3=A4=C3=B6=C3=BC_was_Accepted?=", self.smtplog[0][2]) | ||||
self.assertIn('SUMMARY=3DWith =C3=A4=C3=B6=C3=BC', self.smtplog[0][2]) | self.assertIn('SUMMARY=3DWith =C3=A4=C3=B6=C3=BC', self.smtplog[0][2]) |