def test_garbage_input(self): # Test that we can survive garbage input for common field types tz = EWSTimeZone.timezone('Europe/Copenhagen') account = namedtuple('Account', ['default_timezone'])(default_timezone=tz) payload = b'''\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:Foo>THIS_IS_GARBAGE</t:Foo> </t:Item> </Envelope>''' elem = to_xml(payload).find('{%s}Item' % TNS) for field_cls in (Base64Field, BooleanField, IntegerField, DateField, DateTimeField, DecimalField): field = field_cls('foo', field_uri='item:Foo', is_required=True, default='DUMMY') self.assertEqual(field.from_xml(elem=elem, account=account), None) # Test MS timezones payload = b'''\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:Foo Id="THIS_IS_GARBAGE"></t:Foo> </t:Item> </Envelope>''' elem = to_xml(payload).find('{%s}Item' % TNS) field = TimeZoneField('foo', field_uri='item:Foo', default='DUMMY') self.assertEqual(field.from_xml(elem=elem, account=account), None)
def test_garbage_input(self): # Test that we can survive garbage input for common field types tz = zoneinfo.ZoneInfo("Europe/Copenhagen") account = namedtuple("Account", ["default_timezone"])(default_timezone=tz) payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:Foo>THIS_IS_GARBAGE</t:Foo> </t:Item> </Envelope>""" elem = to_xml(payload).find(f"{{{TNS}}}Item") for field_cls in (Base64Field, BooleanField, IntegerField, DateField, DateTimeField, DecimalField): field = field_cls("foo", field_uri="item:Foo", is_required=True, default="DUMMY") self.assertEqual(field.from_xml(elem=elem, account=account), None) # Test MS timezones payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:Foo Id="THIS_IS_GARBAGE"></t:Foo> </t:Item> </Envelope>""" elem = to_xml(payload).find(f"{{{TNS}}}Item") field = TimeZoneField("foo", field_uri="item:Foo", default="DUMMY") self.assertEqual(field.from_xml(elem=elem, account=account), None)
def test_to_xml_failure_3(self, m): # Not all lxml versions throw ParseError on the same XML, so we have to mock with self.assertRaises(ParseError) as e: to_xml(b"<t:Foo><t:Bar>Baz</t:Bar></t:Foo>") self.assertEqual( e.exception.args[0], "This is not XML: b'<t:Foo><t:Bar>Baz</t:Bar></t:Foo>'")
def test_naive_datetime(self): # Test that we can survive naive datetimes on a datetime field tz = zoneinfo.ZoneInfo("Europe/Copenhagen") utc = zoneinfo.ZoneInfo("UTC") account = namedtuple("Account", ["default_timezone"])(default_timezone=tz) default_value = datetime.datetime(2017, 1, 2, 3, 4, tzinfo=tz) field = DateTimeField("foo", field_uri="item:DateTimeSent", default=default_value) # TZ-aware datetime string payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:DateTimeSent>2017-06-21T18:40:02Z</t:DateTimeSent> </t:Item> </Envelope>""" elem = to_xml(payload).find(f"{{{TNS}}}Item") self.assertEqual(field.from_xml(elem=elem, account=account), datetime.datetime(2017, 6, 21, 18, 40, 2, tzinfo=utc)) # Naive datetime string is localized to tz of the account payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:DateTimeSent>2017-06-21T18:40:02</t:DateTimeSent> </t:Item> </Envelope>""" elem = to_xml(payload).find(f"{{{TNS}}}Item") self.assertEqual(field.from_xml(elem=elem, account=account), datetime.datetime(2017, 6, 21, 18, 40, 2, tzinfo=tz)) # Garbage string returns None payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:DateTimeSent>THIS_IS_GARBAGE</t:DateTimeSent> </t:Item> </Envelope>""" elem = to_xml(payload).find(f"{{{TNS}}}Item") self.assertEqual(field.from_xml(elem=elem, account=account), None) # Element not found returns default value payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> </t:Item> </Envelope>""" elem = to_xml(payload).find(f"{{{TNS}}}Item") self.assertEqual(field.from_xml(elem=elem, account=account), default_value)
def test_naive_datetime(self): # Test that we can survive naive datetimes on a datetime field tz = EWSTimeZone.timezone('Europe/Copenhagen') account = namedtuple('Account', ['default_timezone'])(default_timezone=tz) default_value = tz.localize(EWSDateTime(2017, 1, 2, 3, 4)) field = DateTimeField('foo', field_uri='item:DateTimeSent', default=default_value) # TZ-aware datetime string payload = b'''\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:DateTimeSent>2017-06-21T18:40:02Z</t:DateTimeSent> </t:Item> </Envelope>''' elem = to_xml(payload).find('{%s}Item' % TNS) self.assertEqual(field.from_xml(elem=elem, account=account), UTC.localize(EWSDateTime(2017, 6, 21, 18, 40, 2))) # Naive datetime string is localized to tz of the account payload = b'''\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:DateTimeSent>2017-06-21T18:40:02</t:DateTimeSent> </t:Item> </Envelope>''' elem = to_xml(payload).find('{%s}Item' % TNS) self.assertEqual(field.from_xml(elem=elem, account=account), tz.localize(EWSDateTime(2017, 6, 21, 18, 40, 2))) # Garbage string returns None payload = b'''\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> <t:DateTimeSent>THIS_IS_GARBAGE</t:DateTimeSent> </t:Item> </Envelope>''' elem = to_xml(payload).find('{%s}Item' % TNS) self.assertEqual(field.from_xml(elem=elem, account=account), None) # Element not found returns default value payload = b'''\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Item> </t:Item> </Envelope>''' elem = to_xml(payload).find('{%s}Item' % TNS) self.assertEqual(field.from_xml(elem=elem, account=account), default_value)
def test_to_xml(self): to_xml(b'<?xml version="1.0" encoding="UTF-8"?><foo></foo>') to_xml(BOM_UTF8+b'<?xml version="1.0" encoding="UTF-8"?><foo></foo>') to_xml(BOM_UTF8+b'<?xml version="1.0" encoding="UTF-8"?><foo>&broken</foo>') with self.assertRaises(ParseError): to_xml(b'foo') try: to_xml(b'<t:Foo><t:Bar>Baz</t:Bar></t:Foo>') except ParseError as e: # Not all lxml versions throw an error here, so we can't use assertRaises self.assertIn('Offending text: [...]<t:Foo><t:Bar>Baz</t[...]', e.args[0])
def test_to_xml(self): to_xml(b'<?xml version="1.0" encoding="UTF-8"?><foo></foo>') to_xml(BOM_UTF8 + b'<?xml version="1.0" encoding="UTF-8"?><foo></foo>') to_xml(BOM_UTF8 + b'<?xml version="1.0" encoding="UTF-8"?><foo>&broken</foo>') with self.assertRaises(ParseError): to_xml(b"foo")
def test_internet_message_headers(self): # Message headers are read-only, and an integration test is difficult because we can't reliably AND quickly # generate emails that pass through some relay server that adds headers. Create a unit test instead. payload = b"""\ <?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:InternetMessageHeaders> <t:InternetMessageHeader HeaderName="Received">from foo by bar</t:InternetMessageHeader> <t:InternetMessageHeader HeaderName="DKIM-Signature">Hello from DKIM</t:InternetMessageHeader> <t:InternetMessageHeader HeaderName="MIME-Version">1.0</t:InternetMessageHeader> <t:InternetMessageHeader HeaderName="X-Mailer">Contoso Mail</t:InternetMessageHeader> <t:InternetMessageHeader HeaderName="Return-Path">[email protected]</t:InternetMessageHeader> </t:InternetMessageHeaders> </Envelope>""" headers_elem = to_xml(payload).find(f"{{{TNS}}}InternetMessageHeaders") headers = {} for elem in headers_elem.findall(f"{{{TNS}}}InternetMessageHeader"): header = MessageHeader.from_xml(elem=elem, account=None) headers[header.name] = header.value self.assertDictEqual( headers, { "Received": "from foo by bar", "DKIM-Signature": "Hello from DKIM", "MIME-Version": "1.0", "X-Mailer": "Contoso Mail", "Return-Path": "*****@*****.**", }, )
def test_from_response(self, m): # Test fallback to suggested api_version value when there is a version mismatch and response version is fishy version = Version.from_soap_header( 'Exchange2007', to_xml(b'''\ <s:Header> <h:ServerVersionInfo MajorBuildNumber="845" MajorVersion="15" MinorBuildNumber="22" MinorVersion="1" Version="V2016_10_10" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"/> </s:Header>''')) self.assertEqual(version.api_version, EXCHANGE_2007.api_version()) self.assertEqual(version.api_version, 'Exchange2007') self.assertEqual(version.build, Build(15, 1, 845, 22)) # Test that override the suggested version if the response version is not fishy version = Version.from_soap_header( 'Exchange2013', to_xml(b'''\ <s:Header> <h:ServerVersionInfo MajorBuildNumber="845" MajorVersion="15" MinorBuildNumber="22" MinorVersion="1" Version="HELLO_FROM_EXCHANGELIB" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"/> </s:Header>''')) self.assertEqual(version.api_version, 'HELLO_FROM_EXCHANGELIB') # Test that we override the suggested version with the version deduced from the build number if a version is not # present in the response version = Version.from_soap_header( 'Exchange2013', to_xml(b'''\ <s:Header> <h:ServerVersionInfo MajorBuildNumber="845" MajorVersion="15" MinorBuildNumber="22" MinorVersion="1" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"/> </s:Header>''')) self.assertEqual(version.api_version, 'Exchange2016') # Test that we use the version deduced from the build number when a version is not present in the response and # there was no suggested version. version = Version.from_soap_header( None, to_xml(b'''\ <s:Header> <h:ServerVersionInfo MajorBuildNumber="845" MajorVersion="15" MinorBuildNumber="22" MinorVersion="1" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"/> </s:Header>''')) self.assertEqual(version.api_version, 'Exchange2016') # Test various parse failures with self.assertRaises(TransportError) as e: Version.from_soap_header( 'Exchange2013', to_xml(b'''\ <s:Header> <foo/> </s:Header>''')) self.assertIn('No ServerVersionInfo in header', e.exception.args[0]) with self.assertRaises(TransportError) as e: Version.from_soap_header( 'Exchange2013', to_xml(b'''\ <s:Header> <h:ServerVersionInfo MajorBuildNumber="845" MajorVersion="15" Version="V2016_10_10" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"/> </s:Header>''')) self.assertIn('Bad ServerVersionInfo in response', e.exception.args[0])
def test_to_xml_failure_2(self, m): # Not all lxml versions throw ParseError on the same XML, so we have to mock with self.assertRaises(ParseError) as e: to_xml(b"<t:Foo><t:Bar>Baz</t:Bar></t:Foo>") self.assertIn("XXX", e.exception.args[0])