def test_versioned_choice(self): field = ChoiceField('foo', field_uri='bar', choices={ Choice('c1'), Choice('c2', supported_from=EXCHANGE_2010) }) with self.assertRaises(ValueError): field.clean('XXX') # Value must be a valid choice field.clean('c2', version=None) with self.assertRaises(InvalidChoiceForVersion): field.clean('c2', version=Version(EXCHANGE_2007)) field.clean('c2', version=Version(EXCHANGE_2010)) field.clean('c2', version=Version(EXCHANGE_2013))
def test_q(self): version = Version(build=EXCHANGE_2007) account = mock_account(version=version, protocol=mock_protocol(version=version, service_endpoint='example.com')) root = Root(account=account) tz = zoneinfo.ZoneInfo('Europe/Copenhagen') start = datetime.datetime(2020, 9, 26, 8, 0, 0, tzinfo=tz) end = datetime.datetime(2020, 9, 26, 11, 0, 0, tzinfo=tz) result = '''\ <m:Restriction xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> <t:And xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Or> <t:Contains ContainmentMode="Substring" ContainmentComparison="Exact"> <t:FieldURI FieldURI="item:Categories"/> <t:Constant Value="FOO"/> </t:Contains> <t:Contains ContainmentMode="Substring" ContainmentComparison="Exact"> <t:FieldURI FieldURI="item:Categories"/> <t:Constant Value="BAR"/> </t:Contains> </t:Or> <t:IsGreaterThan> <t:FieldURI FieldURI="calendar:End"/> <t:FieldURIOrConstant> <t:Constant Value="2020-09-26T08:00:00+02:00"/> </t:FieldURIOrConstant> </t:IsGreaterThan> <t:IsLessThan> <t:FieldURI FieldURI="calendar:Start"/> <t:FieldURIOrConstant> <t:Constant Value="2020-09-26T11:00:00+02:00"/> </t:FieldURIOrConstant> </t:IsLessThan> </t:And> </m:Restriction>''' q = Q(Q(categories__contains='FOO') | Q(categories__contains='BAR'), start__lt=end, end__gt=start) r = Restriction(q, folders=[Calendar(root=root)], applies_to=Restriction.ITEMS) self.assertEqual(str(r), ''.join(s.lstrip() for s in result.split('\n'))) # Test empty Q q = Q() self.assertEqual(q.to_xml(folders=[Calendar()], version=version, applies_to=Restriction.ITEMS), None) with self.assertRaises(ValueError): Restriction(q, folders=[Calendar(root=root)], applies_to=Restriction.ITEMS) # Test validation with self.assertRaises(ValueError): Q(datetime_created__range=(1,)) # Must have exactly 2 args with self.assertRaises(ValueError): Q(datetime_created__range=(1, 2, 3)) # Must have exactly 2 args with self.assertRaises(TypeError): Q(datetime_created=Build(15, 1)).clean(version=Version(build=EXCHANGE_2007)) # Must be serializable with self.assertRaises(ValueError): # Must be a timezone-aware datetime Q(datetime_created=datetime.datetime(2017, 1, 1)).clean(version=Version(build=EXCHANGE_2007)) with self.assertRaises(ValueError): Q(categories__contains=[[1, 2], [3, 4]]).clean(version=Version(build=EXCHANGE_2007)) # Must be single value
def test_versioned_choice(self): field = ChoiceField( "foo", field_uri="bar", choices={Choice("c1"), Choice("c2", supported_from=EXCHANGE_2010)}) with self.assertRaises(ValueError): field.clean("XXX") # Value must be a valid choice field.clean("c2", version=None) with self.assertRaises(InvalidChoiceForVersion): field.clean("c2", version=Version(EXCHANGE_2007)) field.clean("c2", version=Version(EXCHANGE_2010)) field.clean("c2", version=Version(EXCHANGE_2013))
def test_find_folders_compat(self): account = self.get_account() coll = FolderCollection(account=account, folders=[account.root]) account.version = Version(EXCHANGE_2007) # Need to set it after the last auto-config of version with self.assertRaises(NotImplementedError) as e: list(coll.find_folders(offset=1)) self.assertEqual(e.exception.args[0], "'offset' is only supported for Exchange 2010 servers and later")
def test_invalid_field(self): test_field = Item.get_field_by_fieldname(fieldname='text_body') self.assertIsInstance(test_field, TextField) self.assertEqual(test_field.name, 'text_body') with self.assertRaises(InvalidField): Item.get_field_by_fieldname(fieldname='xxx') Item.validate_field(field=test_field, version=Version(build=EXCHANGE_2013)) with self.assertRaises(InvalidFieldForVersion) as e: Item.validate_field(field=test_field, version=Version(build=EXCHANGE_2010)) self.assertEqual( e.exception.args[0], "Field 'text_body' is not supported on server version Build=14.0.0.0, API=Exchange2010, Fullname=Microsoft " "Exchange Server 2010 (supported from: 15.0.0.0, deprecated from: None)" )
def test_decrease_poolsize(self): # Test increasing and decreasing the pool size max_connections = 3 protocol = Protocol(config=Configuration( service_endpoint='https://example.com/Foo.asmx', credentials=Credentials(get_random_string(8), get_random_string( 8)), auth_type=NTLM, version=Version(Build(15, 1)), retry_policy=FailFast(), max_connections=max_connections, )) self.assertEqual(protocol._session_pool.qsize(), 0) self.assertEqual(protocol.session_pool_size, 0) protocol.increase_poolsize() protocol.increase_poolsize() protocol.increase_poolsize() with self.assertRaises(SessionPoolMaxSizeReached): protocol.increase_poolsize() self.assertEqual(protocol._session_pool.qsize(), max_connections) self.assertEqual(protocol.session_pool_size, max_connections) protocol.decrease_poolsize() protocol.decrease_poolsize() with self.assertRaises(SessionPoolMinSizeReached): protocol.decrease_poolsize() self.assertEqual(protocol._session_pool.qsize(), 1)
def test_protocol_instance_caching(self, m): # Verify that we get the same Protocol instance for the same combination of (endpoint, credentials) user, password = get_random_string(8), get_random_string(8) config = Configuration(service_endpoint='https://example.com/Foo.asmx', credentials=Credentials(user, password), auth_type=NTLM, version=Version(Build(15, 1)), retry_policy=FailFast()) # Test CachingProtocol.__getitem__ with self.assertRaises(KeyError): _ = Protocol[config] base_p = Protocol(config=config) self.assertEqual(base_p, Protocol[config][0]) # Make sure we always return the same item when creating a Protocol with the same endpoint and creds for _ in range(10): p = Protocol(config=config) self.assertEqual(base_p, p) self.assertEqual(id(base_p), id(p)) self.assertEqual(hash(base_p), hash(p)) self.assertEqual(id(base_p._session_pool), id(p._session_pool)) # Test CachingProtocol.__delitem__ del Protocol[config] with self.assertRaises(KeyError): _ = Protocol[config] # Make sure we get a fresh instance after we cleared the cache p = Protocol(config=config) self.assertNotEqual(base_p, p) Protocol.clear_cache()
def test_invalid_version_args(self): with self.assertRaises(TypeError) as e: Version(build="XXX") self.assertEqual( e.exception.args[0], "'build' 'XXX' must be of type <class 'exchangelib.version.Build'>" ) with self.assertRaises(TypeError) as e: Version(build="XXX", api_version="XXX") self.assertEqual( e.exception.args[0], "'build' 'XXX' must be of type <class 'exchangelib.version.Build'>" ) with self.assertRaises(TypeError) as e: Version(build=Build(15, 1, 2, 3), api_version=999) self.assertEqual(e.exception.args[0], "'api_version' 999 must be of type <class 'str'>")
def test_session(self, m): protocol = Protocol(config=Configuration( service_endpoint='https://example.com/Foo.asmx', credentials=Credentials(get_random_string(8), get_random_string(8)), auth_type=NTLM, version=Version(Build(15, 1)), retry_policy=FailFast() )) session = protocol.create_session() new_session = protocol.renew_session(session) self.assertNotEqual(id(session), id(new_session))
def test_protocol_instance_caching(self, m): # Verify that we get the same Protocol instance for the same combination of (endpoint, credentials) user, password = get_random_string(8), get_random_string(8) base_p = Protocol(config=Configuration( service_endpoint='https://example.com/Foo.asmx', credentials=Credentials(user, password), auth_type=NTLM, version=Version(Build(15, 1)), retry_policy=FailFast() )) for _ in range(10): p = Protocol(config=Configuration( service_endpoint='https://example.com/Foo.asmx', credentials=Credentials(user, password), auth_type=NTLM, version=Version(Build(15, 1)), retry_policy=FailFast() )) self.assertEqual(base_p, p) self.assertEqual(id(base_p), id(p)) self.assertEqual(hash(base_p), hash(p)) self.assertEqual(id(base_p._session_pool), id(p._session_pool)) Protocol.clear_cache()
def test_hardcode_all(self, m): # Test that we can hardcode everything without having a working server. This is useful if neither tasting or # guessing missing values works. Configuration( server="example.com", credentials=Credentials(get_random_string(8), get_random_string(8)), auth_type=NTLM, version=Version(build=Build(15, 1, 2, 3), api_version="foo"), )
def test_send_pre_2013(self): # Test < Exchange 2013 fallback for attachments and send-only mode item = self.get_test_item() item.account = self.get_account() item.folder = item.account.inbox item.attach( FileAttachment(name="file_attachment", content=b"file_attachment")) item.account.version = Version(EXCHANGE_2010_SP2) item.send(save_copy=False) self.assertIsNone(item.id) self.assertIsNone(item.changekey)
def test_magic(self): config = Configuration( server="example.com", credentials=Credentials(get_random_string(8), get_random_string(8)), auth_type=NTLM, version=Version(build=Build(15, 1, 2, 3), api_version="foo"), ) # Just test that these work str(config) repr(config)
def test_pickle(self): # Test that we can pickle, repr and str Protocols o = Protocol(config=Configuration( service_endpoint='https://example.com/Foo.asmx', credentials=Credentials(get_random_string(8), get_random_string(8)), auth_type=NTLM, version=Version(Build(15, 1)), retry_policy=FailFast() )) pickled_o = pickle.dumps(o) unpickled_o = pickle.loads(pickled_o) self.assertIsInstance(unpickled_o, type(o)) self.assertEqual(repr(o), repr(unpickled_o)) self.assertEqual(str(o), str(unpickled_o))
def test_single_field_indexed_element(self): # A SingleFieldIndexedElement must have only one field defined class TestField(SingleFieldIndexedElement): a = CharField() b = CharField() with self.assertRaises(ValueError) as e: TestField.value_field(version=Version(EXCHANGE_2013)) self.assertEqual( e.exception.args[0], "Class <class 'tests.test_field.FieldTest.test_single_field_indexed_element.<locals>.TestField'> " "must have only one value field (found ('a', 'b'))", )
def get_test_protocol(**kwargs): return Protocol(config=Configuration( server=kwargs.get("server"), service_endpoint=kwargs.get( "service_endpoint", f"https://{get_random_hostname()}/Foo.asmx"), credentials=kwargs.get( "credentials", Credentials(get_random_string(8), get_random_string(8))), auth_type=kwargs.get("auth_type", NTLM), version=kwargs.get("version", Version(Build(15, 1))), retry_policy=kwargs.get("retry_policy", FailFast()), max_connections=kwargs.get("max_connections"), ))
def test_close(self): # Don't use example.com here - it does not resolve or answer on all ISPs proc = psutil.Process() ip_addresses = { info[4][0] for info in socket.getaddrinfo('httpbin.org', 80, socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_IP) } def conn_count(): return len( [p for p in proc.connections() if p.raddr[0] in ip_addresses]) self.assertGreater(len(ip_addresses), 0) protocol = Protocol(config=Configuration( service_endpoint='http://httpbin.org', credentials=Credentials(get_random_string(8), get_random_string( 8)), auth_type=NOAUTH, version=Version(Build(15, 1)), retry_policy=FailFast(), max_connections=3)) # Merely getting a session should not create conections session = protocol.get_session() self.assertEqual(conn_count(), 0) # Open one URL - we have 1 connection session.get('http://httpbin.org') self.assertEqual(conn_count(), 1) # Open the same URL - we should still have 1 connection session.get('http://httpbin.org') self.assertEqual(conn_count(), 1) # Open some more connections s2 = protocol.get_session() s2.get('http://httpbin.org') s3 = protocol.get_session() s3.get('http://httpbin.org') self.assertEqual(conn_count(), 3) # Releasing the sessions does not close the connections protocol.release_session(session) protocol.release_session(s2) protocol.release_session(s3) self.assertEqual(conn_count(), 3) # But closing explicitly does protocol.close() self.assertEqual(conn_count(), 0)
def test_q_inversion(self): version = Version(build=EXCHANGE_2007) account = mock_account(version=version, protocol=mock_protocol( version=version, service_endpoint='example.com')) root = Root(account=account) self.assertEqual((~Q(foo=5)).op, Q.NE) self.assertEqual((~Q(foo__not=5)).op, Q.EQ) self.assertEqual((~Q(foo__lt=5)).op, Q.GTE) self.assertEqual((~Q(foo__lte=5)).op, Q.GT) self.assertEqual((~Q(foo__gt=5)).op, Q.LTE) self.assertEqual((~Q(foo__gte=5)).op, Q.LT) # Test not not Q on a non-leaf self.assertEqual(Q(foo__contains=('bar', 'baz')).conn_type, Q.AND) self.assertEqual((~Q(foo__contains=('bar', 'baz'))).conn_type, Q.NOT) self.assertEqual((~~Q(foo__contains=('bar', 'baz'))).conn_type, Q.AND) self.assertEqual(Q(foo__contains=('bar', 'baz')), ~~Q(foo__contains=('bar', 'baz'))) # Test generated XML of 'Not' statement when there is only one child. Skip 't:And' between 't:Not' and 't:Or'. result = '''\ <m:Restriction xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> <t:Not xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Or> <t:IsEqualTo> <t:FieldURI FieldURI="item:Subject"/> <t:FieldURIOrConstant> <t:Constant Value="bar"/> </t:FieldURIOrConstant> </t:IsEqualTo> <t:IsEqualTo> <t:FieldURI FieldURI="item:Subject"/> <t:FieldURIOrConstant> <t:Constant Value="baz"/> </t:FieldURIOrConstant> </t:IsEqualTo> </t:Or> </t:Not> </m:Restriction>''' q = ~(Q(subject='bar') | Q(subject='baz')) self.assertEqual( xml_to_str( q.to_xml(folders=[Calendar(root=root)], version=version, applies_to=Restriction.ITEMS)), ''.join(s.lstrip() for s in result.split('\n')))
def test_autodiscover_from_account(self, m): # Test that autodiscovery via account creation works # Mock the default endpoint that we test in step 1 of autodiscovery m.post(self.dummy_ad_endpoint, status_code=200, content=self.dummy_ad_response) self.assertEqual(len(autodiscover_cache), 0) account = Account( primary_smtp_address=self.account.primary_smtp_address, config=Configuration( credentials=self.account.protocol.credentials, retry_policy=self.retry_policy, version=Version(build=EXCHANGE_2013), ), autodiscover=True, locale="da_DK", ) self.assertEqual(account.primary_smtp_address, self.account.primary_smtp_address) self.assertEqual(account.protocol.service_endpoint.lower(), self.dummy_ews_endpoint.lower()) # Make sure cache is full self.assertEqual(len(autodiscover_cache), 1) self.assertTrue((account.domain, self.account.protocol.credentials, True) in autodiscover_cache) # Test that autodiscover works with a full cache account = Account( primary_smtp_address=self.account.primary_smtp_address, config=Configuration( credentials=self.account.protocol.credentials, retry_policy=self.retry_policy, ), autodiscover=True, locale="da_DK", ) self.assertEqual(account.primary_smtp_address, self.account.primary_smtp_address) # Test cache manipulation key = (account.domain, self.account.protocol.credentials, True) self.assertTrue(key in autodiscover_cache) del autodiscover_cache[key] self.assertFalse(key in autodiscover_cache)
def test_versioned_field(self): field = TextField('foo', field_uri='bar', supported_from=EXCHANGE_2010) with self.assertRaises(InvalidFieldForVersion): field.clean('baz', version=Version(EXCHANGE_2007)) field.clean('baz', version=Version(EXCHANGE_2010)) field.clean('baz', version=Version(EXCHANGE_2013))
def test_comparison(self): self.assertEqual(Version(Build(15, 1, 2, 3)), Version(Build(15, 1, 2, 3))) self.assertNotEqual(Version(Build(15, 1, 2, 3)), Version(Build(15, 1))) self.assertNotEqual(Version(Build(15, 1, 2, 3), api_version="XXX"), Version(None, api_version="XXX")) self.assertNotEqual(Version(None, api_version="XXX"), Version(Build(15, 1, 2), api_version="XXX")) self.assertEqual(Version(Build(15, 1, 2, 3), "XXX"), Version(Build(15, 1, 2, 3), "XXX")) self.assertNotEqual(Version(Build(15, 1, 2, 3), "XXX"), Version(Build(15, 1, 2, 3), "YYY")) self.assertNotEqual(Version(Build(15, 1, 2, 3), "XXX"), Version(Build(99, 88), "XXX")) self.assertNotEqual(Version(Build(15, 1, 2, 3), "XXX"), Version(Build(99, 88), "YYY"))
def test_version_guess(self, m): protocol = self.get_test_protocol() # Test that we can get the version even on error responses m.post( protocol.service_endpoint, status_code=200, content=b"""\ <?xml version='1.0' encoding='utf-8'?> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header> <h:ServerVersionInfo xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" MajorVersion="15" MinorVersion="1" MajorBuildNumber="2345" MinorBuildNumber="6789" Version="V2017_07_11"/> </s:Header> <s:Body> <m:ResolveNamesResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <m:ResponseMessages> <m:ResolveNamesResponseMessage ResponseClass="Error"> <m:MessageText>Multiple results were found.</m:MessageText> <m:ResponseCode>ErrorNameResolutionMultipleResults</m:ResponseCode> <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey> </m:ResolveNamesResponseMessage> </m:ResponseMessages> </m:ResolveNamesResponse> </s:Body> </s:Envelope>""", ) Version.guess(protocol) self.assertEqual(protocol.version.build, Build(15, 1, 2345, 6789)) # Test exception when there are no version headers m.post( protocol.service_endpoint, status_code=200, content=b"""\ <?xml version='1.0' encoding='utf-8'?> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header> </s:Header> <s:Body> <m:ResolveNamesResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <m:ResponseMessages> <m:ResolveNamesResponseMessage ResponseClass="Error"> <m:MessageText>.</m:MessageText> <m:ResponseCode>ErrorNameResolutionMultipleResults</m:ResponseCode> <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey> </m:ResolveNamesResponseMessage> </m:ResponseMessages> </m:ResolveNamesResponse> </s:Body> </s:Envelope>""", ) with self.assertRaises(TransportError) as e: Version.guess(protocol) self.assertEqual( e.exception.args[0], "No valid version headers found in response (ErrorNameResolutionMultipleResults('.'))" )
def test_resolvenames(self): with self.assertRaises(ValueError) as e: self.account.protocol.resolve_names(names=[], search_scope="XXX") self.assertEqual( e.exception.args[0], f"'search_scope' 'XXX' must be one of {sorted(SEARCH_SCOPE_CHOICES)}" ) with self.assertRaises(ValueError) as e: self.account.protocol.resolve_names(names=[], shape="XXX") self.assertEqual( e.exception.args[0], "'contact_data_shape' 'XXX' must be one of ['AllProperties', 'Default', 'IdOnly']" ) with self.assertRaises(ValueError) as e: ResolveNames(protocol=self.account.protocol, chunk_size=500).call(unresolved_entries=None) self.assertEqual( e.exception.args[0], "Chunk size 500 is too high. ResolveNames supports returning at most 100 candidates for a lookup", ) tmp = self.account.protocol.version self.account.protocol.config.version = Version(EXCHANGE_2010_SP1) with self.assertRaises(NotImplementedError) as e: self.account.protocol.resolve_names(names=["*****@*****.**"], shape="IdOnly") self.account.protocol.config.version = tmp self.assertEqual( e.exception.args[0], "'contact_data_shape' is only supported for Exchange 2010 SP2 servers and later" ) self.assertGreaterEqual( self.account.protocol.resolve_names(names=["*****@*****.**"]), []) self.assertGreaterEqual( self.account.protocol.resolve_names( names=["*****@*****.**"], search_scope="ActiveDirectoryContacts"), []) self.assertGreaterEqual( self.account.protocol.resolve_names(names=["*****@*****.**"], shape="AllProperties"), []) self.assertGreaterEqual( self.account.protocol.resolve_names( names=["*****@*****.**"], parent_folders=[self.account.contacts]), []) self.assertEqual( self.account.protocol.resolve_names( names=[self.account.primary_smtp_address]), [Mailbox(email_address=self.account.primary_smtp_address)], ) # Test something that's not an email self.assertEqual( self.account.protocol.resolve_names(names=["foo\\bar"]), [ErrorNameResolutionNoResults("No results were found.")], ) # Test return_full_contact_data mailbox, contact = self.account.protocol.resolve_names( names=[self.account.primary_smtp_address], return_full_contact_data=True)[0] self.assertEqual( mailbox, Mailbox(email_address=self.account.primary_smtp_address)) self.assertListEqual( [ e.email.replace("SMTP:", "") for e in contact.email_addresses if e.label == "EmailAddress1" ], [self.account.primary_smtp_address], )
def test_default_api_version(self): # Test that a version gets a reasonable api_version value if we don't set one explicitly version = Version(build=Build(15, 1, 2, 3)) self.assertEqual(version.api_version, 'Exchange2016')
def test_delegate(self): # The test server does not have any delegate info. Test that account.delegates works, and mock to test parsing # of a non-empty response. self.assertGreaterEqual( len(self.account.delegates), 0 ) self.assertGreaterEqual( len(list(GetDelegate(account=self.account).call(user_ids=['*****@*****.**'], include_permissions=True))), 0 ) xml = b'''\ <?xml version="1.0" encoding="utf-8"?> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <m:GetDelegateResponse ResponseClass="Success" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> <m:ResponseCode>NoError</m:ResponseCode> <m:ResponseMessages> <m:DelegateUserResponseMessageType ResponseClass="Success"> <m:ResponseCode>NoError</m:ResponseCode> <m:DelegateUser> <t:UserId> <t:SID>SOME_SID</t:SID> <t:PrimarySmtpAddress>[email protected]</t:PrimarySmtpAddress> <t:DisplayName>Foo Bar</t:DisplayName> </t:UserId> <t:DelegatePermissions> <t:CalendarFolderPermissionLevel>Author</t:CalendarFolderPermissionLevel> <t:InboxFolderPermissionLevel>Reviewer</t:ContactsFolderPermissionLevel> </t:DelegatePermissions> <t:ReceiveCopiesOfMeetingMessages>false</t:ReceiveCopiesOfMeetingMessages> <t:ViewPrivateItems>true</t:ViewPrivateItems> </m:DelegateUser> </m:DelegateUserResponseMessageType> </m:ResponseMessages> <m:DeliverMeetingRequests>DelegatesAndMe</m:DeliverMeetingRequests> </m:GetDelegateResponse> </s:Body> </s:Envelope>''' MockTZ = namedtuple('EWSTimeZone', ['ms_id']) MockAccount = namedtuple('Account', ['access_type', 'primary_smtp_address', 'default_timezone', 'protocol']) MockProtocol = namedtuple('Protocol', ['version']) p = MockProtocol(version=Version(build=EXCHANGE_2007_SP1)) a = MockAccount(DELEGATE, '*****@*****.**', MockTZ('XXX'), protocol=p) ws = GetDelegate(account=a) _, body = ws._get_soap_parts(response=MockResponse(xml)) res = ws._get_elements_in_response(response=ws._get_soap_messages(body=body)) delegates = [DelegateUser.from_xml(elem=elem, account=a) for elem in res] self.assertListEqual( delegates, [ DelegateUser( user_id=UserId(sid='SOME_SID', primary_smtp_address='*****@*****.**', display_name='Foo Bar'), delegate_permissions=DelegatePermissions( calendar_folder_permission_level='Author', inbox_folder_permission_level='Reviewer', contacts_folder_permission_level='None', notes_folder_permission_level='None', journal_folder_permission_level='None', tasks_folder_permission_level='None', ), receive_copies_of_meeting_messages=False, view_private_items=True, ) ] )
def test_q_failures(self): with self.assertRaises(ValueError): # Invalid value Q(foo=None).clean(version=Version(build=EXCHANGE_2007))
def test_delegate(self): # The test server does not have any delegate info. Test that account.delegates works, and mock to test parsing # of a non-empty response. self.assertGreaterEqual(len(self.account.delegates), 0) with self.assertRaises(ErrorInvalidUserSid): list(GetDelegate(account=self.account).call(user_ids=[UserId(sid="XXX")], include_permissions=True)) with self.assertRaises(ErrorDelegateNoUser): list(GetDelegate(account=self.account).call(user_ids=["*****@*****.**"], include_permissions=True)) with self.assertRaises(ErrorNotDelegate): list( GetDelegate(account=self.account).call( user_ids=[self.account.primary_smtp_address], include_permissions=True ) ) xml = b"""\ <?xml version="1.0" encoding="utf-8"?> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <m:GetDelegateResponse ResponseClass="Success" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> <m:ResponseCode>NoError</m:ResponseCode> <m:ResponseMessages> <m:DelegateUserResponseMessageType ResponseClass="Success"> <m:ResponseCode>NoError</m:ResponseCode> <m:DelegateUser> <t:UserId> <t:SID>SOME_SID</t:SID> <t:PrimarySmtpAddress>[email protected]</t:PrimarySmtpAddress> <t:DisplayName>Foo Bar</t:DisplayName> </t:UserId> <t:DelegatePermissions> <t:CalendarFolderPermissionLevel>Author</t:CalendarFolderPermissionLevel> <t:InboxFolderPermissionLevel>Reviewer</t:ContactsFolderPermissionLevel> </t:DelegatePermissions> <t:ReceiveCopiesOfMeetingMessages>false</t:ReceiveCopiesOfMeetingMessages> <t:ViewPrivateItems>true</t:ViewPrivateItems> </m:DelegateUser> </m:DelegateUserResponseMessageType> </m:ResponseMessages> <m:DeliverMeetingRequests>DelegatesAndMe</m:DeliverMeetingRequests> </m:GetDelegateResponse> </s:Body> </s:Envelope>""" MockTZ = namedtuple("EWSTimeZone", ["ms_id"]) MockAccount = namedtuple("Account", ["access_type", "primary_smtp_address", "default_timezone", "protocol"]) MockProtocol = namedtuple("Protocol", ["version"]) p = MockProtocol(version=Version(build=EXCHANGE_2007_SP1)) a = MockAccount(DELEGATE, "*****@*****.**", MockTZ("XXX"), protocol=p) ws = GetDelegate(account=a) delegates = list(ws.parse(xml)) self.assertListEqual( delegates, [ DelegateUser( user_id=UserId(sid="SOME_SID", primary_smtp_address="*****@*****.**", display_name="Foo Bar"), delegate_permissions=DelegatePermissions( calendar_folder_permission_level="Author", inbox_folder_permission_level="Reviewer", contacts_folder_permission_level="None", notes_folder_permission_level="None", journal_folder_permission_level="None", tasks_folder_permission_level="None", ), receive_copies_of_meeting_messages=False, view_private_items=True, ) ], )
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_versioned_field(self): field = TextField("foo", field_uri="bar", supported_from=EXCHANGE_2010) with self.assertRaises(InvalidFieldForVersion): field.clean("baz", version=Version(EXCHANGE_2007)) field.clean("baz", version=Version(EXCHANGE_2010)) field.clean("baz", version=Version(EXCHANGE_2013))