def test_insights_start_dates_adjust_if_inside_window(self): input_date = pendulum.today().subtract(months=1) expected_date = input_date.subtract(days=28) insights = AdsInsights(name='insights', account=None, stream_alias="insights", options={}, catalog_entry=self.fake_catalog_entry, state={ 'bookmarks': { 'insights': { 'date_start': input_date.to_date_string() } } }) params = list(itertools.islice(insights.job_params(), 5)) self.assertEqual(params[0]['time_ranges'], [{ 'since': expected_date.to_date_string(), 'until': expected_date.to_date_string() }]) expected_date = expected_date.add(days=4) self.assertEqual(params[4]['time_ranges'], [{ 'since': expected_date.to_date_string(), 'until': expected_date.to_date_string() }])
def test_insights_start_dates(self): insights = AdsInsights( name='insights', account=None, stream_alias="insights", options={}, annotated_schema=Schema.from_dict({ 'selected': True, 'properties': { 'something': { 'type': 'object' } } }), state={'bookmarks': { 'insights': { 'date_start': '2017-01-31' } }}) params = list(itertools.islice(insights.job_params(), 5)) self.assertEqual(params[0]['time_ranges'], [{ 'since': '2017-01-03', 'until': '2017-01-03' }]) self.assertEqual(params[4]['time_ranges'], [{ 'since': '2017-01-07', 'until': '2017-01-07' }])
def test_job_polling_retry(self): """AdInsights.api_get() polls the job status of an insights job we've requested that Facebook generate. This test makes a request with a mock response to raise a 400 status error that should be retried. We expect the tap to retry this request up to 5 times for each insights job attempted. """ mocked_api_get = Mock() mocked_api_get.side_effect = FacebookRequestError( message='Unsupported get request; Object does not exist', request_context={"":Mock()}, http_status=400, http_headers=Mock(), body={"error": {"error_subcode": 33}} ) # Create the mock and force the function to throw an error mocked_account = Mock() mocked_account.get_insights = Mock() mocked_account.get_insights.return_value.api_get = mocked_api_get # Initialize the object and call `sync()` ad_insights_object = AdsInsights('', mocked_account, '', '', {}, {}) with self.assertRaises(FacebookRequestError): ad_insights_object.run_job({}) # 5 is the max tries specified in the tap self.assertEquals(25, mocked_account.get_insights.return_value.api_get.call_count) self.assertEquals(5, mocked_account.get_insights.call_count )
def test_insights_job_params_stops(self): start_date = pendulum.today().subtract(days=2) insights = AdsInsights(name='insights', account=None, stream_alias="insights", options={}, catalog_entry=CatalogEntry(schema={ 'properties': { 'something': { 'type': 'object' } } }, metadata=[{ 'breadcrumb': ('properties', 'something'), 'metadata': { 'selected': True } }]), state={ 'bookmarks': { 'insights': { 'date_start': start_date.to_date_string() } } }) self.assertEqual(31, len(list(insights.job_params())))
def test_insights_start_dates(self): insights = AdsInsights( name='insights', account=None, stream_alias="insights", options={}, catalog_entry=CatalogEntry( schema={'properties': { 'something': { 'type': 'object' } }}, metadata=[{ 'breadcrumb': ('properties', 'something'), 'metadata': { 'selected': True } }]), state={'bookmarks': { 'insights': { 'date_start': '2017-01-31' } }}) params = list(itertools.islice(insights.job_params(), 5)) self.assertEqual(params[0]['time_ranges'], [{ 'since': '2017-01-03', 'until': '2017-01-03' }]) self.assertEqual(params[4]['time_ranges'], [{ 'since': '2017-01-07', 'until': '2017-01-07' }])
def test_insights_job_params_stops(self): start_date = pendulum.today().subtract(days=2) insights = AdsInsights(name='insights', account=None, stream_alias="insights", options={}, catalog_entry=self.fake_catalog_entry, state={ 'bookmarks': { 'insights': { 'date_start': start_date.to_date_string() } } }) self.assertEqual(31, len(list(insights.job_params())))
def test_retries_on_type_error(self): """`AdInsights.run_job()` calls a `facebook_business` method, `get_insights()`, to make a request to the API. We want to mock this to throw a `TypeError("string indices must be integers")` and assert that we retry this specific error. """ # Create the mock and force the function to throw an error mocked_account = Mock() mocked_account.get_insights = Mock() mocked_account.get_insights.side_effect = TypeError("string indices must be integers") # Initialize the object and call `sync()` ad_creative_object = AdsInsights('', mocked_account, '', '', {}, {}) with self.assertRaises(TypeError): ad_creative_object.run_job({}) # 5 is the max tries specified in the tap self.assertEquals(5, mocked_account.get_insights.call_count )
def test_insights_job_params_stops(self): start_date = tap_facebook.TODAY.subtract(days=2) insights = AdsInsights(name='insights', account=None, breakdowns=[], annotated_schema={ 'selected': True, 'properties': { 'something': { 'type': 'object' } } }, state=tap_facebook.State( start_date.to_date_string(), None)) self.assertEqual(3, len(list(insights.job_params())))
def test_run_job(self, mocked_sleep): """ AdsInsights.run_job calls a `facebook_business` method,`get_insights()`, to get a batch of insights. We mock this method to raise a `AttributeError` and expect the tap to retry this that function up to 5 times, which is the current hard coded `max_tries` value. """ # Mock get_insights function to throw AttributeError exception mocked_account = Mock() mocked_account.get_insights = Mock() mocked_account.get_insights.side_effect = AttributeError # Call run_job() function of Campaigns and verify AttributeError is raised ads_insights_object = AdsInsights('', mocked_account, '', '', '', {}) with self.assertRaises(AttributeError): ads_insights_object.run_job('test') # verify get_insights() is called 5 times as max 5 reties provided for function self.assertEquals(mocked_account.get_insights.call_count, 5)
def test_retries_on_bad_data(self): """`AdInsights.run_job()` calls a `facebook_business` method, `get_insights()`, to make a request to the API. We mock this method to raise a `FacebookBadObjectError` We expect the tap to retry this request up to 5 times, which is the current hard coded `max_tries` value. """ # Create the mock and force the function to throw an error mocked_account = Mock() mocked_account.get_insights = Mock() mocked_account.get_insights.side_effect = FacebookBadObjectError("Bad data to set object data") # Initialize the object and call `sync()` ad_creative_object = AdsInsights('', mocked_account, '', '', {}, {}) with self.assertRaises(FacebookBadObjectError): ad_creative_object.run_job({}) # 5 is the max tries specified in the tap self.assertEquals(5, mocked_account.get_insights.call_count )
def test_job_polling_retry_succeeds_eventually(self): """AdInsights.api_get() polls the job status of an insights job we've requested that Facebook generate. This test makes a request with a mock response to raise a 400 status error that should be retried. We expect the tap to retry this request up to 5 times for each insights job attempted. """ mocked_bad_response = FacebookRequestError( message='Unsupported get request; Object does not exist', request_context={"":Mock()}, http_status=400, http_headers=Mock(), body={"error": {"error_subcode": 33}} ) mocked_good_response = { "async_status": "Job Completed", "async_percent_completion": 100, "id": "2134" } mocked_api_get = Mock() mocked_api_get.side_effect = [ mocked_bad_response, mocked_bad_response, mocked_good_response ] # Create the mock and force the function to throw an error mocked_account = Mock() mocked_account.get_insights = Mock() mocked_account.get_insights.return_value.api_get = mocked_api_get # Initialize the object and call `sync()` ad_insights_object = AdsInsights('', mocked_account, '', '', {}, {}) ad_insights_object.run_job({}) self.assertEquals(3, mocked_account.get_insights.return_value.api_get.call_count) self.assertEquals(1, mocked_account.get_insights.call_count)
def test_insights_start_dates(self): insights = AdsInsights(name='insights', account=None, breakdowns=[], annotated_schema={ 'selected': True, 'properties': { 'something': { 'type': 'object' } } }, state=tap_facebook.State('2017-01-31', None)) params = list(itertools.islice(insights.job_params(), 5)) self.assertEqual(params[0]['time_ranges'], [{ 'since': '2017-01-03', 'until': '2017-01-31' }]) self.assertEqual(params[4]['time_ranges'], [{ 'since': '2017-01-07', 'until': '2017-02-04' }])
def test_insights_job_params_stops(self): start_date = tap_facebook.TODAY.subtract(days=2) insights = AdsInsights(name='insights', account=None, stream_alias="insights", options={}, annotated_schema=Schema.from_dict({ 'selected': True, 'properties': { 'something': { 'type': 'object' } } }), state={ 'bookmarks': { 'insights': { 'date_start': start_date.to_date_string() } } }) self.assertEqual(31, len(list(insights.job_params())))
def test_retries_and_good_response(self): """Facebook has a class called `FacebookResponse` and it is created from a `requests.Response`. Some `facebook_business` functions depend on calling `FacebookResponse.json()`, which sometimes returns a string instead of a dictionary. This leads to a `TypeError("string indices must be integers")` and we want to retry these. This test will return a "bad" API response the first time the function is called, then a "good" response that can be `json.loads()`. We check that the resulting object has our expected value in it. """ FacebookAdsApi.init(access_token='access_token') expected_value = {"foo": "bar"} account = AdAccount('abc_123') patcher = patch('requests.Session.request') mocked_request = patcher.start() mocked_bad_response = Response() mocked_bad_response._content = b'images' mocked_good_response = Response() # Convert our expected value into a JSON string, and then into bytes byte_string = json.dumps(expected_value).encode() mocked_good_response._content = byte_string mocked_request.side_effect = [ mocked_bad_response, mocked_good_response ] ad_creative_object = AdsInsights('', account, '', '', {}, {}) with self.assertRaises(TypeError): ad_creative_object.account.get_insights(params={}, is_async=True) actual_response = ad_creative_object.account.get_insights( params={}, is_async=True) self.assertDictEqual(expected_value, actual_response._json) # Clean up tests patcher.stop()