class RequestTest(unittest.TestCase): def setUp(self): self.client = Client() @istest def it_raises_resource_not_found(self): resp = mock_response(None, status_code=404) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ResourceNotFound): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_authentication_error_unauthorized(self): resp = mock_response(None, status_code=401) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.AuthenticationError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_authentication_error_forbidden(self): resp = mock_response(None, status_code=403) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.AuthenticationError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_server_error(self): resp = mock_response(None, status_code=500) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ServerError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_bad_gateway_error(self): resp = mock_response(None, status_code=502) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.BadGatewayError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_service_unavailable_error(self): resp = mock_response(None, status_code=503) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ServiceUnavailableError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_an_unexpected_typed_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'hopper', 'message': 'The first compiler.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp try: self.client.get('/users', {}) self.fail('UnexpectedError not raised.') except (UnexpectedError) as err: ok_("The error of type 'hopper' is not recognized" in err.message) # noqa eq_(err.context['http_code'], 200) eq_(err.context['application_error_code'], 'hopper') @istest def it_raises_an_unexpected_untyped_error(self): payload = { 'type': 'error.list', 'errors': [ { 'message': 'UNIVAC' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp try: self.client.get('/users', {}) self.fail('UnexpectedError not raised.') except (UnexpectedError) as err: ok_("An unexpected error occured." in err.message) eq_(err.context['application_error_code'], None) @istest def it_raises_a_bad_request_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': None, 'message': 'email is required' } ] } for code in ['missing_parameter', 'parameter_invalid', 'bad_request']: payload['errors'][0]['type'] = code content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.BadRequestError): self.client.get('/users', {}) @istest def it_raises_an_authentication_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'unauthorized', 'message': 'Your name\'s not down.' } ] } for code in ['unauthorized', 'forbidden']: payload['errors'][0]['type'] = code content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.AuthenticationError): self.client.get('/users', {}) @istest def it_raises_resource_not_found_by_type(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'not_found', 'message': 'Waaaaally?' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ResourceNotFound): self.client.get('/users', {}) @istest def it_raises_rate_limit_exceeded(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'rate_limit_exceeded', 'message': 'Fair use please.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.RateLimitExceeded): self.client.get('/users', {}) @istest def it_raises_a_service_unavailable_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'service_unavailable', 'message': 'Zzzzz.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ServiceUnavailableError): self.client.get('/users', {}) @istest def it_raises_a_multiple_matching_users_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'conflict', 'message': 'Two many cooks.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.MultipleMatchingUsersError): self.client.get('/users', {}) @istest def it_handles_no_error_type(self): payload = { 'errors': [ { 'code': 'unique_user_constraint', 'message': 'User already exists.' } ], 'request_id': '00000000-0000-0000-0000-000000000000', 'type': 'error.list' } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.MultipleMatchingUsersError): self.client.get('/users', {}) payload = { 'errors': [ { 'code': 'parameter_not_found', 'message': 'missing data parameter' } ], 'request_id': None, 'type': 'error.list' } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.BadRequestError): self.client.get('/users', {}) @istest def it_handles_empty_responses(self): resp = mock_response('', status_code=202) with patch('requests.request') as mock_method: mock_method.return_value = resp request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) resp = mock_response(' ', status_code=202) with patch('requests.request') as mock_method: mock_method.return_value = resp request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_handles_no_encoding(self): resp = mock_response( ' ', status_code=200, encoding=None, headers=None) resp.apparent_encoding = 'utf-8' with patch('requests.request') as mock_method: mock_method.return_value = resp request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_needs_encoding_or_apparent_encoding(self): payload = '{}' if not hasattr(payload, 'decode'): # python 3 payload = payload.encode('utf-8') resp = mock_response( payload, status_code=200, encoding=None, headers=None) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(TypeError): request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_allows_the_timeout_to_be_changed(self): from intercom.request import Request eq_(10, Request.timeout) Request.timeout = 3 eq_(3, Request.timeout)
class UserTest(unittest.TestCase): def setUp(self): self.client = Client() @istest def it_to_dict_itself(self): created_at = datetime.utcnow() user = User( email="*****@*****.**", user_id="12345", created_at=created_at, name="Jim Bob") as_dict = user.to_dict() eq_(as_dict["email"], "*****@*****.**") eq_(as_dict["user_id"], "12345") eq_(as_dict["created_at"], calendar.timegm(created_at.utctimetuple())) eq_(as_dict["name"], "Jim Bob") @istest def it_presents_created_at_and_last_impression_at_as_datetime(self): now = datetime.utcnow() now_ts = calendar.timegm(now.utctimetuple()) user = User.from_api( {'created_at': now_ts, 'last_impression_at': now_ts}) self.assertIsInstance(user.created_at, datetime) eq_(now.strftime('%c'), user.created_at.strftime('%c')) self.assertIsInstance(user.last_impression_at, datetime) eq_(now.strftime('%c'), user.last_impression_at.strftime('%c')) @istest def it_throws_an_attribute_error_on_trying_to_access_an_attribute_that_has_not_been_set(self): # noqa with assert_raises(AttributeError): user = User() user.foo_property @istest def it_presents_a_complete_user_record_correctly(self): user = User.from_api(get_user()) eq_('id-from-customers-app', user.user_id) eq_('*****@*****.**', user.email) eq_('Joe Schmoe', user.name) eq_('the-app-id', user.app_id) eq_(123, user.session_count) eq_(1401970114, calendar.timegm(user.created_at.utctimetuple())) eq_(1393613864, calendar.timegm(user.remote_created_at.utctimetuple())) eq_(1401970114, calendar.timegm(user.updated_at.utctimetuple())) Avatar = define_lightweight_class('avatar', 'Avatar') # noqa Company = define_lightweight_class('company', 'Company') # noqa SocialProfile = define_lightweight_class('social_profile', 'SocialProfile') # noqa LocationData = define_lightweight_class('locaion_data', 'LocationData') # noqa self.assertIsInstance(user.avatar.__class__, Avatar.__class__) img_url = 'https://graph.facebook.com/1/picture?width=24&height=24' eq_(img_url, user.avatar.image_url) self.assertIsInstance(user.companies, list) eq_(1, len(user.companies)) self.assertIsInstance(user.companies[0].__class__, Company.__class__) eq_('123', user.companies[0].company_id) eq_('bbbbbbbbbbbbbbbbbbbbbbbb', user.companies[0].id) eq_('the-app-id', user.companies[0].app_id) eq_('Company 1', user.companies[0].name) eq_(1390936440, calendar.timegm( user.companies[0].remote_created_at.utctimetuple())) eq_(1401970114, calendar.timegm( user.companies[0].created_at.utctimetuple())) eq_(1401970114, calendar.timegm( user.companies[0].updated_at.utctimetuple())) eq_(1401970113, calendar.timegm( user.companies[0].last_request_at.utctimetuple())) eq_(0, user.companies[0].monthly_spend) eq_(0, user.companies[0].session_count) eq_(1, user.companies[0].user_count) eq_([], user.companies[0].tag_ids) self.assertIsInstance(user.custom_attributes, FlatStore) eq_('b', user.custom_attributes["a"]) eq_(2, user.custom_attributes["b"]) eq_(4, len(user.social_profiles)) twitter_account = user.social_profiles[0] self.assertIsInstance(twitter_account.__class__, SocialProfile.__class__) eq_('twitter', twitter_account.name) eq_('abc', twitter_account.username) eq_('http://twitter.com/abc', twitter_account.url) self.assertIsInstance(user.location_data.__class__, LocationData.__class__) eq_('Dublin', user.location_data.city_name) eq_('EU', user.location_data.continent_code) eq_('Ireland', user.location_data.country_name) eq_('90', user.location_data.latitude) eq_('10', user.location_data.longitude) eq_('IRL', user.location_data.country_code) ok_(user.unsubscribed_from_emails) eq_("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", user.user_agent_data) # noqa @istest def it_allows_update_last_request_at(self): payload = { 'user_id': '1224242', 'update_last_request_at': True, 'custom_attributes': {} } with patch.object(Client, 'post', return_value=payload) as mock_method: self.client.users.create( user_id='1224242', update_last_request_at=True) mock_method.assert_called_once_with( '/users/', {'update_last_request_at': True, 'user_id': '1224242'}) @istest def it_allows_easy_setting_of_custom_data(self): now = datetime.utcnow() now_ts = calendar.timegm(now.utctimetuple()) user = User() user.custom_attributes["mad"] = 123 user.custom_attributes["other"] = now_ts user.custom_attributes["thing"] = "yay" attrs = {"mad": 123, "other": now_ts, "thing": "yay"} eq_(user.to_dict()["custom_attributes"], attrs) @istest def it_allows_easy_setting_of_multiple_companies(self): user = User() companies = [ {"name": "Intercom", "company_id": "6"}, {"name": "Test", "company_id": "9"}, ] user.companies = companies eq_(user.to_dict()["companies"], companies) @istest def it_rejects_nested_data_structures_in_custom_attributes(self): user = User() with assert_raises(ValueError): user.custom_attributes["thing"] = [1] with assert_raises(ValueError): user.custom_attributes["thing"] = {1: 2} with assert_raises(ValueError): user.custom_attributes = {1: {2: 3}} user = User.from_api(get_user()) with assert_raises(ValueError): user.custom_attributes["thing"] = [1] @istest def it_fetches_a_user(self): with patch.object(Client, 'get', return_value=get_user()) as mock_method: # noqa user = self.client.users.find(email='*****@*****.**') eq_(user.email, '*****@*****.**') eq_(user.name, 'Joe Schmoe') mock_method.assert_called_once_with( '/users', {'email': '*****@*****.**'}) # noqa @istest def it_gets_users_by_tag(self): with patch.object(Client, 'get', return_value=page_of_users(False)): users = self.client.users.by_tag(124) for user in users: ok_(hasattr(user, 'avatar')) @istest def it_saves_a_user_always_sends_custom_attributes(self): body = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'custom_attributes': {} } with patch.object(Client, 'post', return_value=body) as mock_method: user = User(email="*****@*****.**", user_id="i-1224242") self.client.users.save(user) eq_(user.email, '*****@*****.**') eq_(user.custom_attributes, {}) mock_method.assert_called_once_with( '/users', {'email': "*****@*****.**", 'user_id': "i-1224242", 'custom_attributes': {}}) @istest def it_saves_a_user_with_a_company(self): body = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'companies': [{ 'company_id': 6, 'name': 'Intercom' }] } with patch.object(Client, 'post', return_value=body) as mock_method: user = User( email="*****@*****.**", user_id="i-1224242", company={'company_id': 6, 'name': 'Intercom'}) self.client.users.save(user) eq_(user.email, '*****@*****.**') eq_(len(user.companies), 1) mock_method.assert_called_once_with( '/users', {'email': "*****@*****.**", 'user_id': "i-1224242", 'company': {'company_id': 6, 'name': 'Intercom'}, 'custom_attributes': {}}) @istest def it_saves_a_user_with_companies(self): body = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'companies': [{ 'company_id': 6, 'name': 'Intercom' }] } with patch.object(Client, 'post', return_value=body) as mock_method: user = User( email="*****@*****.**", user_id="i-1224242", companies=[{'company_id': 6, 'name': 'Intercom'}]) self.client.users.save(user) eq_(user.email, '*****@*****.**') eq_(len(user.companies), 1) mock_method.assert_called_once_with( '/users', {'email': "*****@*****.**", 'user_id': "i-1224242", 'companies': [{'company_id': 6, 'name': 'Intercom'}], 'custom_attributes': {}}) @istest def it_can_save_a_user_with_a_none_email(self): user = User( email=None, user_id="i-1224242", companies=[{'company_id': 6, 'name': 'Intercom'}]) body = { 'custom_attributes': {}, 'email': None, 'user_id': 'i-1224242', 'companies': [{ 'company_id': 6, 'name': 'Intercom' }] } with patch.object(Client, 'post', return_value=body) as mock_method: self.client.users.save(user) ok_(user.email is None) eq_(user.user_id, 'i-1224242') mock_method.assert_called_once_with( '/users', {'email': None, 'user_id': "i-1224242", 'companies': [{'company_id': 6, 'name': 'Intercom'}], 'custom_attributes': {}}) @istest def it_deletes_a_user(self): user = User(id="1") with patch.object(Client, 'delete', return_value={}) as mock_method: user = self.client.users.delete(user) eq_(user.id, "1") mock_method.assert_called_once_with('/users/1', {}) @istest def it_can_use_user_create_for_convenience(self): payload = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'custom_attributes': {} } with patch.object(Client, 'post', return_value=payload) as mock_method: # noqa user = self.client.users.create(email="*****@*****.**", user_id="i-1224242") # noqa eq_(payload, user.to_dict()) mock_method.assert_called_once_with( '/users/', {'email': "*****@*****.**", 'user_id': "i-1224242"}) # noqa @istest def it_updates_the_user_with_attributes_set_by_the_server(self): payload = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'custom_attributes': {}, 'session_count': 4 } with patch.object(Client, 'post', return_value=payload) as mock_method: # noqa user = self.client.users.create(email="*****@*****.**", user_id="i-1224242") # noqa eq_(payload, user.to_dict()) mock_method.assert_called_once_with( '/users/', {'email': "*****@*****.**", 'user_id': "i-1224242"}) # noqa @istest def it_allows_setting_dates_to_none_without_converting_them_to_0(self): payload = { 'email': '*****@*****.**', 'custom_attributes': {}, 'remote_created_at': None } with patch.object(Client, 'post', return_value=payload) as mock_method: user = self.client.users.create(email="*****@*****.**", remote_created_at=None) # noqa ok_(user.remote_created_at is None) mock_method.assert_called_once_with('/users/', {'email': "*****@*****.**", 'remote_created_at': None}) # noqa @istest def it_gets_sets_rw_keys(self): created_at = datetime.utcnow() payload = { 'email': '*****@*****.**', 'user_id': 'abc123', 'name': 'Bob Smith', 'last_seen_ip': '1.2.3.4', 'last_seen_user_agent': 'ie6', 'created_at': calendar.timegm(created_at.utctimetuple()) } user = User(**payload) expected_keys = ['custom_attributes'] expected_keys.extend(list(payload.keys())) eq_(sorted(expected_keys), sorted(user.to_dict().keys())) for key in list(payload.keys()): eq_(payload[key], user.to_dict()[key]) @istest def it_will_allow_extra_attributes_in_response_from_api(self): user = User.from_api({'new_param': 'some value'}) eq_('some value', user.new_param) @istest def it_returns_a_collectionproxy_for_all_without_making_any_requests(self): with mock.patch('intercom.request.Request.send_request_to_path', new_callable=mock.NonCallableMock): # noqa res = self.client.users.all() self.assertIsInstance(res, CollectionProxy) @istest def it_raises_a_multiple_matching_users_error_when_receiving_a_conflict(self): # noqa payload = { 'type': 'error.list', 'errors': [ { 'code': 'conflict', 'message': 'Multiple existing users match this email address - must be more specific using user_id' # noqa } ] } # create bytes content content = json.dumps(payload).encode('utf-8') # create mock response resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(MultipleMatchingUsersError): self.client.get('/users', {}) @istest def it_handles_accented_characters(self): # create a user dict with a name that contains accented characters payload = get_user(name='Jóe Schmö') # create bytes content content = json.dumps(payload).encode('utf-8') # create mock response resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp user = self.client.users.find(email='*****@*****.**') try: # Python 2 eq_(unicode('Jóe Schmö', 'utf-8'), user.name) except NameError: # Python 3 eq_('Jóe Schmö', user.name)
class UserTest(unittest.TestCase): def setUp(self): self.client = Client() @istest def it_to_dict_itself(self): created_at = datetime.utcnow() user = User(email="*****@*****.**", user_id="12345", created_at=created_at, name="Jim Bob") as_dict = user.to_dict() eq_(as_dict["email"], "*****@*****.**") eq_(as_dict["user_id"], "12345") eq_(as_dict["created_at"], calendar.timegm(created_at.utctimetuple())) eq_(as_dict["name"], "Jim Bob") @istest def it_presents_created_at_and_last_impression_at_as_datetime(self): now = datetime.utcnow() now_ts = calendar.timegm(now.utctimetuple()) user = User.from_api({ 'created_at': now_ts, 'last_impression_at': now_ts }) self.assertIsInstance(user.created_at, datetime) eq_(now.strftime('%c'), user.created_at.strftime('%c')) self.assertIsInstance(user.last_impression_at, datetime) eq_(now.strftime('%c'), user.last_impression_at.strftime('%c')) @istest def it_throws_an_attribute_error_on_trying_to_access_an_attribute_that_has_not_been_set( self): # noqa with assert_raises(AttributeError): user = User() user.foo_property @istest def it_presents_a_complete_user_record_correctly(self): user = User.from_api(get_user()) eq_('id-from-customers-app', user.user_id) eq_('*****@*****.**', user.email) eq_('Joe Schmoe', user.name) eq_('the-app-id', user.app_id) eq_(123, user.session_count) eq_(1401970114, calendar.timegm(user.created_at.utctimetuple())) eq_(1393613864, calendar.timegm(user.remote_created_at.utctimetuple())) eq_(1401970114, calendar.timegm(user.updated_at.utctimetuple())) Avatar = define_lightweight_class('avatar', 'Avatar') # noqa Company = define_lightweight_class('company', 'Company') # noqa SocialProfile = define_lightweight_class('social_profile', 'SocialProfile') # noqa LocationData = define_lightweight_class('locaion_data', 'LocationData') # noqa self.assertIsInstance(user.avatar.__class__, Avatar.__class__) img_url = 'https://graph.facebook.com/1/picture?width=24&height=24' eq_(img_url, user.avatar.image_url) self.assertIsInstance(user.companies, list) eq_(1, len(user.companies)) self.assertIsInstance(user.companies[0].__class__, Company.__class__) eq_('123', user.companies[0].company_id) eq_('bbbbbbbbbbbbbbbbbbbbbbbb', user.companies[0].id) eq_('the-app-id', user.companies[0].app_id) eq_('Company 1', user.companies[0].name) eq_( 1390936440, calendar.timegm( user.companies[0].remote_created_at.utctimetuple())) eq_(1401970114, calendar.timegm(user.companies[0].created_at.utctimetuple())) eq_(1401970114, calendar.timegm(user.companies[0].updated_at.utctimetuple())) eq_(1401970113, calendar.timegm(user.companies[0].last_request_at.utctimetuple())) eq_(0, user.companies[0].monthly_spend) eq_(0, user.companies[0].session_count) eq_(1, user.companies[0].user_count) eq_([], user.companies[0].tag_ids) self.assertIsInstance(user.custom_attributes, FlatStore) eq_('b', user.custom_attributes["a"]) eq_(2, user.custom_attributes["b"]) eq_(4, len(user.social_profiles)) twitter_account = user.social_profiles[0] self.assertIsInstance(twitter_account.__class__, SocialProfile.__class__) eq_('twitter', twitter_account.name) eq_('abc', twitter_account.username) eq_('http://twitter.com/abc', twitter_account.url) self.assertIsInstance(user.location_data.__class__, LocationData.__class__) eq_('Dublin', user.location_data.city_name) eq_('EU', user.location_data.continent_code) eq_('Ireland', user.location_data.country_name) eq_('90', user.location_data.latitude) eq_('10', user.location_data.longitude) eq_('IRL', user.location_data.country_code) ok_(user.unsubscribed_from_emails) eq_("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", user.user_agent_data) # noqa @istest def it_allows_update_last_request_at(self): payload = { 'user_id': '1224242', 'update_last_request_at': True, 'custom_attributes': {} } with patch.object(Client, 'post', return_value=payload) as mock_method: self.client.users.create(user_id='1224242', update_last_request_at=True) mock_method.assert_called_once_with('/users/', { 'update_last_request_at': True, 'user_id': '1224242' }) @istest def it_allows_easy_setting_of_custom_data(self): now = datetime.utcnow() now_ts = calendar.timegm(now.utctimetuple()) user = User() user.custom_attributes["mad"] = 123 user.custom_attributes["other"] = now_ts user.custom_attributes["thing"] = "yay" attrs = {"mad": 123, "other": now_ts, "thing": "yay"} eq_(user.to_dict()["custom_attributes"], attrs) @istest def it_allows_easy_setting_of_multiple_companies(self): user = User() companies = [ { "name": "Intercom", "company_id": "6" }, { "name": "Test", "company_id": "9" }, ] user.companies = companies eq_(user.to_dict()["companies"], companies) @istest def it_rejects_nested_data_structures_in_custom_attributes(self): user = User() with assert_raises(ValueError): user.custom_attributes["thing"] = [1] with assert_raises(ValueError): user.custom_attributes["thing"] = {1: 2} with assert_raises(ValueError): user.custom_attributes = {1: {2: 3}} user = User.from_api(get_user()) with assert_raises(ValueError): user.custom_attributes["thing"] = [1] @istest def it_fetches_a_user(self): with patch.object(Client, 'get', return_value=get_user()) as mock_method: # noqa user = self.client.users.find(email='*****@*****.**') eq_(user.email, '*****@*****.**') eq_(user.name, 'Joe Schmoe') mock_method.assert_called_once_with( '/users', {'email': '*****@*****.**'}) # noqa @istest def it_gets_users_by_tag(self): with patch.object(Client, 'get', return_value=page_of_users(False)): users = self.client.users.by_tag(124) for user in users: ok_(hasattr(user, 'avatar')) @istest def it_saves_a_user_always_sends_custom_attributes(self): body = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'custom_attributes': {} } with patch.object(Client, 'post', return_value=body) as mock_method: user = User(email="*****@*****.**", user_id="i-1224242") self.client.users.save(user) eq_(user.email, '*****@*****.**') eq_(user.custom_attributes, {}) mock_method.assert_called_once_with( '/users', { 'email': "*****@*****.**", 'user_id': "i-1224242", 'custom_attributes': {} }) @istest def it_saves_a_user_with_a_company(self): body = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'companies': [{ 'company_id': 6, 'name': 'Intercom' }] } with patch.object(Client, 'post', return_value=body) as mock_method: user = User(email="*****@*****.**", user_id="i-1224242", company={ 'company_id': 6, 'name': 'Intercom' }) self.client.users.save(user) eq_(user.email, '*****@*****.**') eq_(len(user.companies), 1) mock_method.assert_called_once_with( '/users', { 'email': "*****@*****.**", 'user_id': "i-1224242", 'company': { 'company_id': 6, 'name': 'Intercom' }, 'custom_attributes': {} }) @istest def it_saves_a_user_with_companies(self): body = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'companies': [{ 'company_id': 6, 'name': 'Intercom' }] } with patch.object(Client, 'post', return_value=body) as mock_method: user = User(email="*****@*****.**", user_id="i-1224242", companies=[{ 'company_id': 6, 'name': 'Intercom' }]) self.client.users.save(user) eq_(user.email, '*****@*****.**') eq_(len(user.companies), 1) mock_method.assert_called_once_with( '/users', { 'email': "*****@*****.**", 'user_id': "i-1224242", 'companies': [{ 'company_id': 6, 'name': 'Intercom' }], 'custom_attributes': {} }) @istest def it_can_save_a_user_with_a_none_email(self): user = User(email=None, user_id="i-1224242", companies=[{ 'company_id': 6, 'name': 'Intercom' }]) body = { 'custom_attributes': {}, 'email': None, 'user_id': 'i-1224242', 'companies': [{ 'company_id': 6, 'name': 'Intercom' }] } with patch.object(Client, 'post', return_value=body) as mock_method: self.client.users.save(user) ok_(user.email is None) eq_(user.user_id, 'i-1224242') mock_method.assert_called_once_with( '/users', { 'email': None, 'user_id': "i-1224242", 'companies': [{ 'company_id': 6, 'name': 'Intercom' }], 'custom_attributes': {} }) @istest def it_deletes_a_user(self): user = User(id="1") with patch.object(Client, 'delete', return_value={}) as mock_method: user = self.client.users.delete(user) eq_(user.id, "1") mock_method.assert_called_once_with('/users/1', {}) @istest def it_can_use_user_create_for_convenience(self): payload = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'custom_attributes': {} } with patch.object(Client, 'post', return_value=payload) as mock_method: # noqa user = self.client.users.create(email="*****@*****.**", user_id="i-1224242") # noqa eq_(payload, user.to_dict()) mock_method.assert_called_once_with('/users/', { 'email': "*****@*****.**", 'user_id': "i-1224242" }) # noqa @istest def it_updates_the_user_with_attributes_set_by_the_server(self): payload = { 'email': '*****@*****.**', 'user_id': 'i-1224242', 'custom_attributes': {}, 'session_count': 4 } with patch.object(Client, 'post', return_value=payload) as mock_method: # noqa user = self.client.users.create(email="*****@*****.**", user_id="i-1224242") # noqa eq_(payload, user.to_dict()) mock_method.assert_called_once_with('/users/', { 'email': "*****@*****.**", 'user_id': "i-1224242" }) # noqa @istest def it_allows_setting_dates_to_none_without_converting_them_to_0(self): payload = { 'email': '*****@*****.**', 'custom_attributes': {}, 'remote_created_at': None } with patch.object(Client, 'post', return_value=payload) as mock_method: user = self.client.users.create(email="*****@*****.**", remote_created_at=None) # noqa ok_(user.remote_created_at is None) mock_method.assert_called_once_with('/users/', { 'email': "*****@*****.**", 'remote_created_at': None }) # noqa @istest def it_gets_sets_rw_keys(self): created_at = datetime.utcnow() payload = { 'email': '*****@*****.**', 'user_id': 'abc123', 'name': 'Bob Smith', 'last_seen_ip': '1.2.3.4', 'last_seen_user_agent': 'ie6', 'created_at': calendar.timegm(created_at.utctimetuple()) } user = User(**payload) expected_keys = ['custom_attributes'] expected_keys.extend(list(payload.keys())) eq_(sorted(expected_keys), sorted(user.to_dict().keys())) for key in list(payload.keys()): eq_(payload[key], user.to_dict()[key]) @istest def it_will_allow_extra_attributes_in_response_from_api(self): user = User.from_api({'new_param': 'some value'}) eq_('some value', user.new_param) @istest def it_returns_a_collectionproxy_for_all_without_making_any_requests(self): with mock.patch('intercom.request.Request.send_request_to_path', new_callable=mock.NonCallableMock): # noqa res = self.client.users.all() self.assertIsInstance(res, CollectionProxy) @istest def it_raises_a_multiple_matching_users_error_when_receiving_a_conflict( self): # noqa payload = { 'type': 'error.list', 'errors': [{ 'code': 'conflict', 'message': 'Multiple existing users match this email address - must be more specific using user_id' # noqa }] } # create bytes content content = json.dumps(payload).encode('utf-8') # create mock response resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(MultipleMatchingUsersError): self.client.get('/users', {}) @istest def it_handles_accented_characters(self): # create a user dict with a name that contains accented characters payload = get_user(name='Jóe Schmö') # create bytes content content = json.dumps(payload).encode('utf-8') # create mock response resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp user = self.client.users.find(email='*****@*****.**') try: # Python 2 eq_(unicode('Jóe Schmö', 'utf-8'), user.name) except NameError: # Python 3 eq_('Jóe Schmö', user.name)
class RequestTest(unittest.TestCase): def setUp(self): self.client = Client() @istest def it_raises_resource_not_found(self): resp = mock_response(None, status_code=404) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ResourceNotFound): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_authentication_error_unauthorized(self): resp = mock_response(None, status_code=401) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.AuthenticationError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_authentication_error_forbidden(self): resp = mock_response(None, status_code=403) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.AuthenticationError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_server_error(self): resp = mock_response(None, status_code=500) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ServerError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_bad_gateway_error(self): resp = mock_response(None, status_code=502) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.BadGatewayError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_service_unavailable_error(self): resp = mock_response(None, status_code=503) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ServiceUnavailableError): request = Request('GET', 'notes') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_raises_an_unexpected_typed_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'hopper', 'message': 'The first compiler.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp try: self.client.get('/users', {}) self.fail('UnexpectedError not raised.') except (UnexpectedError) as err: ok_("The error of type 'hopper' is not recognized" in err.message) # noqa eq_(err.context['http_code'], 200) eq_(err.context['application_error_code'], 'hopper') @istest def it_raises_an_unexpected_untyped_error(self): payload = { 'type': 'error.list', 'errors': [ { 'message': 'UNIVAC' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp try: self.client.get('/users', {}) self.fail('UnexpectedError not raised.') except (UnexpectedError) as err: ok_("An unexpected error occured." in err.message) eq_(err.context['application_error_code'], None) @istest def it_raises_a_bad_request_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': None, 'message': 'email is required' } ] } for code in ['missing_parameter', 'parameter_invalid', 'bad_request']: payload['errors'][0]['type'] = code content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.BadRequestError): self.client.get('/users', {}) @istest def it_raises_an_authentication_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'unauthorized', 'message': 'Your name\'s not down.' } ] } for code in ['unauthorized', 'forbidden']: payload['errors'][0]['type'] = code content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.AuthenticationError): self.client.get('/users', {}) @istest def it_raises_resource_not_found_by_type(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'not_found', 'message': 'Waaaaally?' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ResourceNotFound): self.client.get('/users', {}) @istest def it_raises_rate_limit_exceeded(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'rate_limit_exceeded', 'message': 'Fair use please.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.RateLimitExceeded): self.client.get('/users', {}) @istest def it_raises_a_service_unavailable_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'service_unavailable', 'message': 'Zzzzz.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.ServiceUnavailableError): self.client.get('/users', {}) @istest def it_raises_a_multiple_matching_users_error(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'conflict', 'message': 'Two many cooks.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.MultipleMatchingUsersError): self.client.get('/users', {}) @istest def it_raises_token_unauthorized(self): payload = { 'type': 'error.list', 'errors': [ { 'type': 'token_unauthorized', 'message': 'The PAT is not authorized for this action.' } ] } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.TokenUnauthorizedError): self.client.get('/users', {}) @istest def it_handles_no_error_type(self): payload = { 'errors': [ { 'code': 'unique_user_constraint', 'message': 'User already exists.' } ], 'request_id': '00000000-0000-0000-0000-000000000000', 'type': 'error.list' } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.MultipleMatchingUsersError): self.client.get('/users', {}) payload = { 'errors': [ { 'code': 'parameter_not_found', 'message': 'missing data parameter' } ], 'request_id': None, 'type': 'error.list' } content = json.dumps(payload).encode('utf-8') resp = mock_response(content) with patch('requests.sessions.Session.request') as mock_method: mock_method.return_value = resp with assert_raises(intercom.BadRequestError): self.client.get('/users', {}) @istest def it_handles_empty_responses(self): resp = mock_response('', status_code=202) with patch('requests.request') as mock_method: mock_method.return_value = resp request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) resp = mock_response(' ', status_code=202) with patch('requests.request') as mock_method: mock_method.return_value = resp request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_handles_no_encoding(self): resp = mock_response( ' ', status_code=200, encoding=None, headers=None) resp.apparent_encoding = 'utf-8' with patch('requests.request') as mock_method: mock_method.return_value = resp request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_needs_encoding_or_apparent_encoding(self): payload = '{}' if not hasattr(payload, 'decode'): # python 3 payload = payload.encode('utf-8') resp = mock_response( payload, status_code=200, encoding=None, headers=None) with patch('requests.request') as mock_method: mock_method.return_value = resp with assert_raises(TypeError): request = Request('GET', 'events') request.send_request_to_path('', ('x', 'y'), resp) @istest def it_allows_the_timeout_to_be_changed(self): from intercom.request import Request try: eq_(90, Request.timeout) Request.timeout = 3 eq_(3, Request.timeout) finally: Request.timeout = 90 @istest def it_allows_the_timeout_to_be_configured(self): import os from intercom.request import configure_timeout # check the default eq_(90, configure_timeout()) # override the default os.environ['INTERCOM_REQUEST_TIMEOUT'] = '20' eq_(20, configure_timeout()) # ignore bad timeouts, reset to default 90 os.environ['INTERCOM_REQUEST_TIMEOUT'] = 'abc' eq_(90, configure_timeout())