def test_api_invocation(self): """The constructor is called appropriately once per request.""" self.log.debug("Testing API invocation...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image", "text"), keyword="test", quantity=2, location=(4, 3, 2, "km"), interval=(1, 0), api=api, network=network ) api.assert_called_once_with( self.retriever.keychain[ self.retriever.keyring["twitter"] ]["consumer_key"], access_token=self.retriever.keychain[ self.retriever.keyring["twitter"] ]["access_token"] )
def test_rate_limit_low(self): """The rate limit should be obeyed during paging.""" self.log.debug("Testing rate low limit obedience...") api = self.injectors["api"]["low_limits"] network = self.injectors["network"]["low_limits"] twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", api=api, network=network, ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(1, api().search.call_count) self.assertEqual(1, network.call_count)
def test_rate_limit_retrieval(self): """The rate limit is retrieved once per request.""" self.log.debug("Testing rate limit retrieval...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image", "text"), keyword="test", quantity=2, location=(4, 3, 2, "km"), interval=(1, 0), api=api, network=network ) api().get_application_rate_limit_status.assert_called_once_with()
def test_rate_limit_failure(self): """Failure to retrieve a rate limit is fatal.""" self.log.debug("Testing rate limit failure...") api = self.injectors["api"]["malformed_limits"] network = self.injectors["network"]["malformed_limits"] with self.assertRaises(KeyError): twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", api=api, network=network, ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(0, api().search.call_count) self.assertEqual(0, network.call_count)
def test_TwythonError_relay(self): # pylint: disable=invalid-name """TwythonErrors are relayed correctly.""" self.log.debug("Testing TwythonError relay...") api = self.injectors["api"]["imitate"] network = self.injectors["network"]["imitate"] with self.assertRaises(TwythonError): twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", api=api, network=network ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(1, api().search.call_count) self.assertEqual(0, network.call_count)
def test_hard_failure(self): """Failing hard raises exceptions instead of returning empty.""" self.log.debug("Testing hard failure...") api = self.injectors["api"]["limited"] network = self.injectors["network"]["limited"] with self.assertRaises(OGReLimitError): twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", fail_hard=True, api=api, network=network ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(0, api().search.call_count) self.assertEqual(0, network.call_count)
def test_empty_response_with_hard_failure(self): # pylint: disable=invalid-name """No "statuses" key in fail_hard Twitter response causes an exception.""" self.log.debug("Testing empty response with hard failure...") api = self.injectors["api"]["complex"] network = self.injectors["network"]["complex"] with self.assertRaises(OGReError): twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", fail_hard=True, api=api, network=network ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(1, api().search.call_count) self.assertEqual(0, network.call_count)
def test_default_https(self): """HTTPS is used by default to retrieve images.""" self.log.debug("Testing HTTPS by default...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image", "text"), keyword="test", quantity=2, location=(4, 3, 2, "km"), interval=(1, 0), api=api, network=network ) network.assert_called_once_with( self.tweets["statuses"][0] ["entities"]["media"][0]["media_url_https"] )
def test_api_iteration_invocation(self): """The API is queried once per iteration.""" self.log.debug("Testing appropriate API use and HTTPS by default...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image", "text"), keyword="test", quantity=2, location=(4, 3, 2, "km"), interval=(1, 0), api=api, network=network ) api().search.assert_called_once_with( q="test", count=2, geocode="4.0,3.0,2.0km", since_id=-5405765689543753728, max_id=-5405765685349449728 )
def test_empty_response(self): """No "statuses" key in Twitter response causes a break.""" self.log.debug("Testing empty response...") api = self.injectors["api"]["complex"] network = self.injectors["network"]["complex"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", api=api, network=network ), [] ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(1, api().search.call_count) self.assertEqual(0, network.call_count)
def test_rate_limit_obedience(self): """The rate limit is obeyed appropriately.""" self.log.debug("Testing rate limit obedience...") api = self.injectors["api"]["limited"] network = self.injectors["network"]["limited"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", api=api, network=network ), [] ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(0, api().search.call_count) self.assertEqual(0, network.call_count)
def test_zero_quantity(self): """Requesting < 1 result returns empty without accessing the Twitter API.""" self.log.debug("Testing zero quantity...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], keyword="test", quantity=0, api=api, network=network ), [] ) self.assertEqual(0, api.call_count) self.assertEqual(0, api().get_application_rate_limit_status.call_count) self.assertEqual(0, api().search.call_count) self.assertEqual(0, network.call_count)
def test_fetch(self): """ Test the main entry point to OGRe. These tests should ensure that the retrieved results are packaged in a GeoJSON FeatureCollection object properly. They should also verify that empty requests fail fast (e.g. quantity < 1 or media == []). """ self.log.debug("Testing the main entry point to OGRe...") with self.assertRaises(ValueError): self.retriever.get(("invalid",),) with self.assertRaises(ValueError): self.retriever.get(("Twitter", "invalid"),) self.assertEqual( self.retriever.fetch(sources=()), { "type": "FeatureCollection", "features": [] } ) self.assertEqual( self.retriever.fetch(sources=("Twitter",), media=()), { "type": "FeatureCollection", "features": [] } ) self.assertEqual( self.retriever.fetch(sources=("Twitter",), quantity=0), { "type": "FeatureCollection", "features": [] } ) self.api.reset_mock() self.network.reset_mock() control = { "type": "FeatureCollection", "features": twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image", "text"), keyword="test", quantity=2, location=(0, 1, 2, "km"), interval=(3, 4), test=True, test_message="GeoJSON FeatureCollection Packaging (Control)", api=self.api, network=self.network ) } self.api.reset_mock() self.network.reset_mock() self.assertEqual( control, self.retriever.fetch( sources=("Twitter",), media=("image", "text"), keyword="test", quantity=2, location=(0, 1, 2, "km"), interval=(3, 4), test=True, test_message="GeoJSON FeatureCollection Packaging", api=self.api, network=self.network ) )
def test_fetch(self): """ Test the main entry point to OGRe. These tests should ensure that the retrieved results are packaged in a GeoJSON FeatureCollection object properly. They should also verify that empty requests fail fast (e.g. quantity < 1 or media == []). """ self.log.debug("Testing the main entry point to OGRe...") with self.assertRaises(ValueError): self.retriever.get(("invalid", ), ) with self.assertRaises(ValueError): self.retriever.get(("Twitter", "invalid"), ) self.assertEqual(self.retriever.fetch(sources=()), { "type": "FeatureCollection", "features": [] }) self.assertEqual(self.retriever.fetch(sources=("Twitter", ), media=()), { "type": "FeatureCollection", "features": [] }) self.assertEqual( self.retriever.fetch(sources=("Twitter", ), quantity=0), { "type": "FeatureCollection", "features": [] }) self.api.reset_mock() self.network.reset_mock() control = { "type": "FeatureCollection", "features": twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"]], media=("image", "text"), keyword="test", quantity=2, location=(0, 1, 2, "km"), interval=(3, 4), test=True, test_message="GeoJSON FeatureCollection Packaging (Control)", api=self.api, network=self.network) } self.api.reset_mock() self.network.reset_mock() self.assertEqual( control, self.retriever.fetch( sources=("Twitter", ), media=("image", "text"), keyword="test", quantity=2, location=(0, 1, 2, "km"), interval=(3, 4), test=True, test_message="GeoJSON FeatureCollection Packaging", api=self.api, network=self.network))
def test_return_format(self): """Results are packaged correctly.""" self.log.debug("Testing return format...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image", "text"), keyword="test", quantity=2, location=(0, 1, 2, "km"), interval=(3, 4), api=api, network=network ), [ { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][0] ["coordinates"]["coordinates"][0], self.tweets["statuses"][0] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "text": self.tweets["statuses"][0]["text"], "image": base64.b64encode("test_image".encode('utf-8')), "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][0]["id"] ) ).isoformat()+"Z" } }, { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][1] ["coordinates"]["coordinates"][0], self.tweets["statuses"][1] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "text": self.tweets["statuses"][1]["text"], "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][1]["id"] ) ).isoformat()+"Z" } } ] )
def test_strict_media_paging_and_duplication(self): # pylint: disable=invalid-name """ Setting "strict_media" kwarg to True returns only requested media. Parameters for paging are computed correctly. Paging is not used unless it is needed. Duplicates are not filtered. """ self.log.debug("Testing strict media, paging, and duplication...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image",), keyword="test", quantity=2, location=(0, 1, 2, "km"), interval=(3, 4), strict_media=True, api=api, network=network ), [ { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][0] ["coordinates"]["coordinates"][0], self.tweets["statuses"][0] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "image": base64.b64encode("test_image".encode('utf-8')), "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][0]["id"] ) ).isoformat()+"Z" } }, { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][0] ["coordinates"]["coordinates"][0], self.tweets["statuses"][0] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "image": base64.b64encode("test_image".encode('utf-8')), "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][0]["id"] ) ).isoformat()+"Z" } } ] ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(2, api().search.call_count) api().search.assert_has_any_call( q="test pic.twitter.com", count=2, geocode="0.0,0.1,2.0km", since_id=-5405765676960841728, max_id=-5405765672766537728 ) api().search.assert_has_any_call( q="test pic.twitter.com", count=1, geocode="0.0,0.1,2.0km", since_id=-5405765676960841728, max_id=445633721891164159 ) self.assertEqual(2, network.call_count)
def test_filtering_counting_and_HTTP(self): # pylint: disable=invalid-name """ "Text" media is returned when not requested. "Image" media is returned when requested. Remaining results are calculated correctly. Setting "secure" kwarg to False causes HTTP retrieval. """ self.log.debug("Testing filtering, counting, and HTTP on demand...") api = self.injectors["api"]["regular"] network = self.injectors["network"]["regular"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("image",), keyword="test", quantity=1, location=(0, 1, 2, "km"), interval=(3, 4), secure=False, api=api, network=network ), [ { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][0] ["coordinates"]["coordinates"][0], self.tweets["statuses"][0] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "text": self.tweets["statuses"][0]["text"], "image": base64.b64encode("test_image".encode('utf-8')), "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][0]["id"] ) ).isoformat()+"Z" } }, { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][1] ["coordinates"]["coordinates"][0], self.tweets["statuses"][1] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "text": self.tweets["statuses"][1]["text"], "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][1]["id"] ) ).isoformat()+"Z" } } ] ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(1, api().search.call_count) network.assert_called_once_with( self.tweets["statuses"][0]["entities"]["media"][0]["media_url"] )
def test_filtering_and_page_depletion(self): # pylint: disable=invalid-name """ Ungeotagged or untimestamped results are omitted. "Text" media is returned when requested. "Image" media is not returned unless requested. No remaining pages causes a break. """ self.log.debug("Testing filtering and page depletion...") api = self.injectors["api"]["deplete"] network = self.injectors["network"]["deplete"] self.assertEqual( twitter( keys=self.retriever.keychain[ self.retriever.keyring["twitter"] ], media=("text",), keyword="test", quantity=4, location=(0, 1, 2, "km"), interval=(3, 4), api=api, network=network ), [ { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][0]["coordinates"]["coordinates"][0], self.tweets["statuses"][0]["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "text": self.tweets["statuses"][0]["text"], "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][0]["id"] ) ).isoformat()+"Z" } }, { "geometry": { "type": "Point", "coordinates": [ self.tweets["statuses"][1] ["coordinates"]["coordinates"][0], self.tweets["statuses"][1] ["coordinates"]["coordinates"][1] ] }, "type": "Feature", "properties": { "source": "Twitter", "text": self.tweets["statuses"][1]["text"], "time": datetime.utcfromtimestamp( snowflake.snowflake2utc( self.tweets["statuses"][1]["id"] ) ).isoformat()+"Z" } } ] ) self.assertEqual(1, api.call_count) self.assertEqual(1, api().get_application_rate_limit_status.call_count) self.assertEqual(1, api().search.call_count) self.assertEqual(0, network.call_count)