Example #1
0
class TwythonAPITestCase(unittest.TestCase):
    def setUp(self):
        self.api = Twython('', '', '', '')

    def get_url(self, endpoint):
        """Convenience function for mapping from endpoint to URL"""
        return '%s/%s.json' % (self.api.api_url % self.api.api_version, endpoint)

    def register_response(self, method, url, body='{}', match_querystring=False,
                          status=200, adding_headers=None, stream=False,
                          content_type='application/json; charset=utf-8'):
        """Wrapper function for responses for simpler unit tests"""

        # responses uses BytesIO to hold the body so it needs to be in bytes
        if not is_py2:
            body = bytes(body, 'UTF-8')

        responses.add(method, url, body, match_querystring,
                      status, adding_headers, stream, content_type)

    @responses.activate
    def test_request_should_handle_full_endpoint(self):
        """Test that request() accepts a full URL for the endpoint argument"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        self.register_response(responses.GET, url)

        self.api.request(url)

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_handle_relative_endpoint(self):
        """Test that request() accepts a twitter endpoint name for the endpoint argument"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        self.register_response(responses.GET, url)

        self.api.request('search/tweets', version='1.1')

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_post_request_regardless_of_case(self):
        """Test that request() accepts the HTTP method name regardless of case"""
        url = 'https://api.twitter.com/1.1/statuses/update.json'
        self.register_response(responses.POST, url)

        self.api.request(url, method='POST')
        self.api.request(url, method='post')

        self.assertEqual(2, len(responses.calls))
        self.assertEqual('POST', responses.calls[0].request.method)
        self.assertEqual('POST', responses.calls[1].request.method)

    @responses.activate
    def test_request_should_throw_exception_with_invalid_http_method(self):
        """Test that request() throws an exception when an invalid HTTP method is passed"""
        # TODO(cash): should Twython catch the AttributeError and throw a TwythonError
        self.assertRaises(AttributeError, self.api.request, endpoint='search/tweets', method='INVALID')

    @responses.activate
    def test_request_should_encode_boolean_as_lowercase_string(self):
        """Test that request() encodes a boolean parameter as a lowercase string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'include_entities': True})
        self.api.request(endpoint, params={'include_entities': False})

        self.assertEqual(url + '?include_entities=true', responses.calls[0].request.url)
        self.assertEqual(url + '?include_entities=false', responses.calls[1].request.url)

    @responses.activate
    def test_request_should_handle_string_or_number_parameter(self):
        """Test that request() encodes a numeric or string parameter correctly"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'lang': 'es'})
        self.api.request(endpoint, params={'count': 50})

        self.assertEqual(url + '?lang=es', responses.calls[0].request.url)
        self.assertEqual(url + '?count=50', responses.calls[1].request.url)

    @responses.activate
    def test_request_should_encode_list_of_strings_as_string(self):
        """Test that request() encodes a list of strings as a comma-separated string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        location = ['37.781157', '-122.39872', '1mi']
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': location})

        # requests url encodes the parameters so , is %2C
        self.assertEqual(url + '?geocode=37.781157%2C-122.39872%2C1mi', responses.calls[0].request.url)

    @responses.activate
    def test_request_should_encode_numeric_list_as_string(self):
        """Test that request() encodes a list of numbers as a comma-separated string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        location = [37.781157, -122.39872, '1mi']
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': location})

        self.assertEqual(url + '?geocode=37.781157%2C-122.39872%2C1mi', responses.calls[0].request.url)

    @responses.activate
    def test_request_should_ignore_bad_parameter(self):
        """Test that request() ignores unexpected parameter types"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': self})

        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_handle_file_as_parameter(self):
        """Test that request() pulls a file out of params for requests lib"""
        endpoint = 'account/update_profile_image'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        mock_file = StringIO("Twython test image")
        self.api.request(endpoint, method='POST', params={'image': mock_file})

        self.assertIn(b'filename="image"', responses.calls[0].request.body)
        self.assertIn(b"Twython test image", responses.calls[0].request.body)

    @responses.activate
    def test_request_should_put_params_in_body_when_post(self):
        """Test that request() passes params as data when the request is a POST"""
        endpoint = 'statuses/update'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        self.api.request(endpoint, method='POST', params={'status': 'this is a test'})

        self.assertIn(b'status=this+is+a+test', responses.calls[0].request.body)
        self.assertNotIn('status=this+is+a+test', responses.calls[0].request.url)

    @responses.activate
    def test_get_uses_get_method(self):
        """Test Twython generic GET request works"""
        endpoint = 'account/verify_credentials'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.get(endpoint)

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_post_uses_post_method(self):
        """Test Twython generic POST request works"""
        endpoint = 'statuses/update'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        self.api.post(endpoint, params={'status': 'I love Twython!'})

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    def test_raise_twython_error_on_request_exception(self):
        """Test if TwythonError is raised by a RequestException"""
        with mock.patch.object(requests.Session, 'get') as get_mock:
            # mocking an ssl cert error
            get_mock.side_effect = requests.RequestException("hostname 'example.com' doesn't match ...")
            self.assertRaises(TwythonError, self.api.get, 'https://example.com')

    @responses.activate
    def test_request_should_get_convert_json_to_data(self):
        """Test that Twython converts JSON data to a Python object"""
        endpoint = 'statuses/show'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, body='{"id": 210462857140252672}')

        data = self.api.request(endpoint, params={'id': 210462857140252672})

        self.assertEqual({'id': 210462857140252672}, data)

    @responses.activate
    def test_request_should_raise_exception_with_invalid_json(self):
        """Test that Twython handles invalid JSON (though Twitter should not return it)"""
        endpoint = 'statuses/show'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, body='{"id: 210462857140252672}')

        self.assertRaises(TwythonError, self.api.request, endpoint, params={'id': 210462857140252672})

    @responses.activate
    def test_request_should_handle_401(self):
        """Test that Twython raises an auth error on 401 error"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, body='{"errors":[{"message":"Error"}]}', status=401)

        self.assertRaises(TwythonAuthError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_400_for_missing_auth_data(self):
        """Test that Twython raises an auth error on 400 error when no oauth data sent"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url,
                               body='{"errors":[{"message":"Bad Authentication data"}]}', status=400)

        self.assertRaises(TwythonAuthError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_400_that_is_not_auth_related(self):
        """Test that Twython raises a normal error on 400 error when unrelated to authorization"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url,
                               body='{"errors":[{"message":"Bad request"}]}', status=400)

        self.assertRaises(TwythonError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_rate_limit(self):
        """Test that Twython raises an rate limit error on 429"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url,
                               body='{"errors":[{"message":"Rate Limit"}]}', status=429)

        self.assertRaises(TwythonRateLimitError, self.api.request, endpoint)

    @responses.activate
    def test_get_lastfunction_header_should_return_header(self):
        """Test getting last specific header of the last API call works"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, adding_headers={'x-rate-limit-remaining': 37})

        self.api.get(endpoint)

        value = self.api.get_lastfunction_header('x-rate-limit-remaining')
        self.assertEqual(37, value)
        value2 = self.api.get_lastfunction_header('does-not-exist')
        self.assertIsNone(value2)
        value3 = self.api.get_lastfunction_header('not-there-either', 96)
        self.assertEqual(96, value3)

    def test_get_lastfunction_header_should_raise_error_when_no_previous_call(self):
        """Test attempting to get a header when no API call was made raises a TwythonError"""
        self.assertRaises(TwythonError, self.api.get_lastfunction_header, 'no-api-call-was-made')

    @responses.activate
    def test_sends_correct_accept_encoding_header(self):
        """Test that Twython accepts compressed data."""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.get(endpoint)

        self.assertEqual(b'gzip, deflate, compress', responses.calls[0].request.headers['Accept-Encoding'])

    # Static methods
    def test_construct_api_url(self):
        """Test constructing a Twitter API url works as we expect"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        constructed_url = self.api.construct_api_url(url, q='#twitter')
        self.assertEqual(constructed_url, 'https://api.twitter.com/1.1/search/tweets.json?q=%23twitter')

    def test_encode(self):
        """Test encoding UTF-8 works"""
        self.api.encode('Twython is awesome!')

    def test_html_for_tweet(self):
        """Test HTML for Tweet returns what we want"""
        tweet_text = self.api.html_for_tweet(test_tweet_object)
        self.assertEqual(test_tweet_html, tweet_text)

    def test_html_for_tweet_expanded_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object,
                                             use_expanded_url=True)
        # Make sure full url is in HTML
        self.assertTrue('http://google.com' in tweet_text)

    def test_html_for_tweet_short_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object, False)
        # Make sure HTML doesn't contain the display OR expanded url
        self.assertTrue('http://google.com' not in tweet_text)
        self.assertTrue('google.com' not in tweet_text)
Example #2
0
class TwythonAPITestCase(unittest.TestCase):
    def setUp(self):
        self.api = Twython('', '', '', '')

    def get_url(self, endpoint):
        """Convenience function for mapping from endpoint to URL"""
        return '%s/%s.json' % (self.api.api_url % self.api.api_version, endpoint)

    def register_response(self, method, url, body='{}', match_querystring=False,
            status=200, adding_headers=None, stream=False,
            content_type='application/json; charset=utf-8'):
        """Wrapper function for responses for simpler unit tests"""

        # responses uses BytesIO to hold the body so it needs to be in bytes
        if not is_py2:
            body = bytes(body, 'UTF-8')

        responses.add(method, url, body, match_querystring,
            status, adding_headers, stream, content_type)

    @responses.activate
    def test_request_should_handle_full_endpoint(self):
        """Test that request() accepts a full URL for the endpoint argument"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        self.register_response(responses.GET, url)

        self.api.request(url)

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_handle_relative_endpoint(self):
        """Test that request() accepts a twitter endpoint name for the endpoint argument"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        self.register_response(responses.GET, url)

        self.api.request('search/tweets', version='1.1')

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_post_request_regardless_of_case(self):
        """Test that request() accepts the HTTP method name regardless of case"""
        url = 'https://api.twitter.com/1.1/statuses/update.json'
        self.register_response(responses.POST, url)

        self.api.request(url, method='POST')
        self.api.request(url, method='post')

        self.assertEqual(2, len(responses.calls))
        self.assertEqual('POST', responses.calls[0].request.method)
        self.assertEqual('POST', responses.calls[1].request.method)

    @responses.activate
    def test_request_should_throw_exception_with_invalid_http_method(self):
        """Test that request() throws an exception when an invalid HTTP method is passed"""
        #TODO(cash): should Twython catch the AttributeError and throw a TwythonError
        self.assertRaises(AttributeError, self.api.request, endpoint='search/tweets', method='INVALID')

    @responses.activate
    def test_request_should_encode_boolean_as_lowercase_string(self):
        """Test that request() encodes a boolean parameter as a lowercase string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'include_entities': True})
        self.api.request(endpoint, params={'include_entities': False})

        self.assertEqual(url + '?include_entities=true', responses.calls[0].request.url)
        self.assertEqual(url + '?include_entities=false', responses.calls[1].request.url)

    @responses.activate
    def test_request_should_handle_string_or_number_parameter(self):
        """Test that request() encodes a numeric or string parameter correctly"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'lang': 'es'})
        self.api.request(endpoint, params={'count': 50})

        self.assertEqual(url + '?lang=es', responses.calls[0].request.url)
        self.assertEqual(url + '?count=50', responses.calls[1].request.url)

    @responses.activate
    def test_request_should_encode_list_of_strings_as_string(self):
        """Test that request() encodes a list of strings as a comma-separated string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        location = ['37.781157', '-122.39872', '1mi']
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': location})

        # requests url encodes the parameters so , is %2C
        self.assertEqual(url + '?geocode=37.781157%2C-122.39872%2C1mi', responses.calls[0].request.url)

    @responses.activate
    def test_request_should_encode_numeric_list_as_string(self):
        """Test that request() encodes a list of numbers as a comma-separated string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        location = [37.781157, -122.39872, '1mi']
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': location})

        self.assertEqual(url + '?geocode=37.781157%2C-122.39872%2C1mi', responses.calls[0].request.url)

    @responses.activate
    def test_request_should_ignore_bad_parameter(self):
        """Test that request() ignores unexpected parameter types"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': self})

        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_handle_file_as_parameter(self):
        """Test that request() pulls a file out of params for requests lib"""
        endpoint = 'account/update_profile_image'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        mock_file = StringIO("Twython test image")
        self.api.request(endpoint, method='POST', params={'image': mock_file})

        self.assertIn(b'filename="image"', responses.calls[0].request.body)
        self.assertIn(b"Twython test image", responses.calls[0].request.body)

    @responses.activate
    def test_request_should_put_params_in_body_when_post(self):
        """Test that request() passes params as data when the request is a POST"""
        endpoint = 'statuses/update'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        self.api.request(endpoint, method='POST', params={'status': 'this is a test'})

        self.assertIn(b'status=this+is+a+test', responses.calls[0].request.body)
        self.assertNotIn('status=this+is+a+test', responses.calls[0].request.url)

    @responses.activate
    def test_get_uses_get_method(self):
        """Test Twython generic GET request works"""
        endpoint = 'account/verify_credentials'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.get(endpoint)

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_post_uses_post_method(self):
        """Test Twython generic POST request works"""
        endpoint = 'statuses/update'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        self.api.post(endpoint, params={'status': 'I love Twython!'})

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    def test_raise_twython_error_on_request_exception(self):
        """Test if TwythonError is raised by a RequestException"""
        with mock.patch.object(requests.Session, 'get') as get_mock:
            # mocking an ssl cert error
            get_mock.side_effect = requests.RequestException("hostname 'example.com' doesn't match ...")
            self.assertRaises(TwythonError, self.api.get, 'https://example.com')

    @responses.activate
    def test_request_should_get_convert_json_to_data(self):
        """Test that Twython converts JSON data to a Python object"""
        endpoint = 'statuses/show'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, body='{"id": 210462857140252672}')

        data = self.api.request(endpoint, params={'id': 210462857140252672})

        self.assertEqual({'id': 210462857140252672}, data)

    @responses.activate
    def test_request_should_raise_exception_with_invalid_json(self):
        """Test that Twython handles invalid JSON (though Twitter should not return it)"""
        endpoint = 'statuses/show'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, body='{"id: 210462857140252672}')

        self.assertRaises(TwythonError, self.api.request, endpoint, params={'id': 210462857140252672})

    @responses.activate
    def test_request_should_handle_401(self):
        """Test that Twython raises an auth error on 401 error"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, body='{"errors":[{"message":"Error"}]}', status=401)

        self.assertRaises(TwythonAuthError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_400_for_missing_auth_data(self):
        """Test that Twython raises an auth error on 400 error when no oauth data sent"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url,
                               body='{"errors":[{"message":"Bad Authentication data"}]}', status=400)

        self.assertRaises(TwythonAuthError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_400_that_is_not_auth_related(self):
        """Test that Twython raises a normal error on 400 error when unrelated to authorization"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url,
                               body='{"errors":[{"message":"Bad request"}]}', status=400)

        self.assertRaises(TwythonError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_rate_limit(self):
        """Test that Twython raises an rate limit error on 429"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url,
                               body='{"errors":[{"message":"Rate Limit"}]}', status=429)

        self.assertRaises(TwythonRateLimitError, self.api.request, endpoint)

    @responses.activate
    def test_get_lastfunction_header_should_return_header(self):
        """Test getting last specific header of the last API call works"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url, adding_headers={'x-rate-limit-remaining': 37})

        self.api.get(endpoint)

        value = self.api.get_lastfunction_header('x-rate-limit-remaining')
        self.assertEqual(37, value)
        value2 = self.api.get_lastfunction_header('does-not-exist')
        self.assertIsNone(value2)
        value3 = self.api.get_lastfunction_header('not-there-either', 96)
        self.assertEqual(96, value3)

    def test_get_lastfunction_header_should_raise_error_when_no_previous_call(self):
        """Test attempting to get a header when no API call was made raises a TwythonError"""
        self.assertRaises(TwythonError, self.api.get_lastfunction_header, 'no-api-call-was-made')

    # Static methods
    def test_construct_api_url(self):
        """Test constructing a Twitter API url works as we expect"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        constructed_url = self.api.construct_api_url(url, q='#twitter')
        self.assertEqual(constructed_url, 'https://api.twitter.com/1.1/search/tweets.json?q=%23twitter')

    def test_encode(self):
        """Test encoding UTF-8 works"""
        self.api.encode('Twython is awesome!')

    def test_html_for_tweet(self):
        """Test HTML for Tweet returns what we want"""
        tweet_text = self.api.html_for_tweet(test_tweet_object)
        self.assertEqual(test_tweet_html, tweet_text)

    def test_html_for_tweet_expanded_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object,
                                             use_expanded_url=True)
        # Make sure full url is in HTML
        self.assertTrue('http://google.com' in tweet_text)

    def test_html_for_tweet_short_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object, False)
        # Make sure HTML doesn't contain the display OR expanded url
        self.assertTrue(not 'http://google.com' in tweet_text)
        self.assertTrue(not 'google.com' in tweet_text)
Example #3
0
class TwythonAPITestCase(unittest.TestCase):
    def setUp(self):
        self.api = Twython('', '', '', '')

    def get_url(self, endpoint):
        """Convenience function for mapping from endpoint to URL"""
        return '%s/%s.json' % (self.api.api_url % self.api.api_version,
                               endpoint)

    def register_response(self,
                          method,
                          url,
                          body='{}',
                          match_querystring=False,
                          status=200,
                          adding_headers=None,
                          stream=False,
                          content_type='application/json; charset=utf-8'):
        """Wrapper function for responses for simpler unit tests"""

        # responses uses BytesIO to hold the body so it needs to be in bytes
        if not is_py2:
            body = bytes(body, 'UTF-8')

        responses.add(method, url, body, match_querystring, status,
                      adding_headers, stream, content_type)

    @responses.activate
    def test_request_should_handle_full_endpoint(self):
        """Test that request() accepts a full URL for the endpoint argument"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        self.register_response(responses.GET, url)

        self.api.request(url)

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_handle_relative_endpoint(self):
        """Test that request() accepts a twitter endpoint name for the endpoint argument"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        self.register_response(responses.GET, url)

        self.api.request('search/tweets', version='1.1')

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_post_request_regardless_of_case(self):
        """Test that request() accepts the HTTP method name regardless of case"""
        url = 'https://api.twitter.com/1.1/statuses/update.json'
        self.register_response(responses.POST, url)

        self.api.request(url, method='POST')
        self.api.request(url, method='post')

        self.assertEqual(2, len(responses.calls))
        self.assertEqual('POST', responses.calls[0].request.method)
        self.assertEqual('POST', responses.calls[1].request.method)

    @responses.activate
    def test_request_should_throw_exception_with_invalid_http_method(self):
        """Test that request() throws an exception when an invalid HTTP method is passed"""
        # TODO(cash): should Twython catch the AttributeError and throw a TwythonError
        self.assertRaises(AttributeError,
                          self.api.request,
                          endpoint='search/tweets',
                          method='INVALID')

    @responses.activate
    def test_request_should_encode_boolean_as_lowercase_string(self):
        """Test that request() encodes a boolean parameter as a lowercase string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'include_entities': True})
        self.api.request(endpoint, params={'include_entities': False})

        self.assertEqual(url + '?include_entities=true',
                         responses.calls[0].request.url)
        self.assertEqual(url + '?include_entities=false',
                         responses.calls[1].request.url)

    @responses.activate
    def test_request_should_handle_string_or_number_parameter(self):
        """Test that request() encodes a numeric or string parameter correctly"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'lang': 'es'})
        self.api.request(endpoint, params={'count': 50})

        self.assertEqual(url + '?lang=es', responses.calls[0].request.url)
        self.assertEqual(url + '?count=50', responses.calls[1].request.url)

    @responses.activate
    def test_request_should_encode_list_of_strings_as_string(self):
        """Test that request() encodes a list of strings as a comma-separated string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        location = ['37.781157', '-122.39872', '1mi']
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': location})

        # requests url encodes the parameters so , is %2C
        self.assertEqual(url + '?geocode=37.781157%2C-122.39872%2C1mi',
                         responses.calls[0].request.url)

    @responses.activate
    def test_request_should_encode_numeric_list_as_string(self):
        """Test that request() encodes a list of numbers as a comma-separated string"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        location = [37.781157, -122.39872, '1mi']
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': location})

        self.assertEqual(url + '?geocode=37.781157%2C-122.39872%2C1mi',
                         responses.calls[0].request.url)

    @responses.activate
    def test_request_should_ignore_bad_parameter(self):
        """Test that request() ignores unexpected parameter types"""
        endpoint = 'search/tweets'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.request(endpoint, params={'geocode': self})

        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_request_should_handle_file_as_parameter(self):
        """Test that request() pulls a file out of params for requests lib"""
        endpoint = 'account/update_profile_image'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        mock_file = StringIO("Twython test image")
        self.api.request(endpoint, method='POST', params={'image': mock_file})

        self.assertIn(b'filename="image"', responses.calls[0].request.body)
        self.assertIn(b"Twython test image", responses.calls[0].request.body)

    @responses.activate
    def test_request_should_put_params_in_body_when_post(self):
        """Test that request() passes params as data when the request is a POST"""
        endpoint = 'statuses/update'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        self.api.request(endpoint,
                         method='POST',
                         params={'status': 'this is a test'})

        self.assertIn(b'status=this+is+a+test',
                      responses.calls[0].request.body)
        self.assertNotIn('status=this+is+a+test',
                         responses.calls[0].request.url)

    @responses.activate
    def test_get_uses_get_method(self):
        """Test Twython generic GET request works"""
        endpoint = 'account/verify_credentials'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.get(endpoint)

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    @responses.activate
    def test_post_uses_post_method(self):
        """Test Twython generic POST request works"""
        endpoint = 'statuses/update'
        url = self.get_url(endpoint)
        self.register_response(responses.POST, url)

        self.api.post(endpoint, params={'status': 'I love Twython!'})

        self.assertEqual(1, len(responses.calls))
        self.assertEqual(url, responses.calls[0].request.url)

    def test_raise_twython_error_on_request_exception(self):
        """Test if TwythonError is raised by a RequestException"""
        with mock.patch.object(requests.Session, 'get') as get_mock:
            # mocking an ssl cert error
            get_mock.side_effect = requests.RequestException(
                "hostname 'example.com' doesn't match ...")
            self.assertRaises(TwythonError, self.api.get,
                              'https://example.com')

    @responses.activate
    def test_request_should_get_convert_json_to_data(self):
        """Test that Twython converts JSON data to a Python object"""
        endpoint = 'statuses/show'
        url = self.get_url(endpoint)
        self.register_response(responses.GET,
                               url,
                               body='{"id": 210462857140252672}')

        data = self.api.request(endpoint, params={'id': 210462857140252672})

        self.assertEqual({'id': 210462857140252672}, data)

    @responses.activate
    def test_request_should_raise_exception_with_invalid_json(self):
        """Test that Twython handles invalid JSON (though Twitter should not return it)"""
        endpoint = 'statuses/show'
        url = self.get_url(endpoint)
        self.register_response(responses.GET,
                               url,
                               body='{"id: 210462857140252672}')

        self.assertRaises(TwythonError,
                          self.api.request,
                          endpoint,
                          params={'id': 210462857140252672})

    @responses.activate
    def test_request_should_handle_401(self):
        """Test that Twython raises an auth error on 401 error"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET,
                               url,
                               body='{"errors":[{"message":"Error"}]}',
                               status=401)

        self.assertRaises(TwythonAuthError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_400_for_missing_auth_data(self):
        """Test that Twython raises an auth error on 400 error when no oauth data sent"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(
            responses.GET,
            url,
            body='{"errors":[{"message":"Bad Authentication data"}]}',
            status=400)

        self.assertRaises(TwythonAuthError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_400_that_is_not_auth_related(self):
        """Test that Twython raises a normal error on 400 error when unrelated to authorization"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET,
                               url,
                               body='{"errors":[{"message":"Bad request"}]}',
                               status=400)

        self.assertRaises(TwythonError, self.api.request, endpoint)

    @responses.activate
    def test_request_should_handle_rate_limit(self):
        """Test that Twython raises an rate limit error on 429"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET,
                               url,
                               body='{"errors":[{"message":"Rate Limit"}]}',
                               status=429)

        self.assertRaises(TwythonRateLimitError, self.api.request, endpoint)

    @responses.activate
    def test_get_lastfunction_header_should_return_header(self):
        """Test getting last specific header of the last API call works"""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET,
                               url,
                               adding_headers={'x-rate-limit-remaining': '37'})

        self.api.get(endpoint)

        value = self.api.get_lastfunction_header('x-rate-limit-remaining')
        self.assertEqual('37', value)
        value2 = self.api.get_lastfunction_header('does-not-exist')
        self.assertIsNone(value2)
        value3 = self.api.get_lastfunction_header('not-there-either', '96')
        self.assertEqual('96', value3)

    def test_get_lastfunction_header_should_raise_error_when_no_previous_call(
            self):
        """Test attempting to get a header when no API call was made raises a TwythonError"""
        self.assertRaises(TwythonError, self.api.get_lastfunction_header,
                          'no-api-call-was-made')

    @responses.activate
    def test_sends_correct_accept_encoding_header(self):
        """Test that Twython accepts compressed data."""
        endpoint = 'statuses/home_timeline'
        url = self.get_url(endpoint)
        self.register_response(responses.GET, url)

        self.api.get(endpoint)

        self.assertEqual(b'gzip, deflate',
                         responses.calls[0].request.headers['Accept-Encoding'])

    # Static methods
    def test_construct_api_url(self):
        """Test constructing a Twitter API url works as we expect"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        constructed_url = self.api.construct_api_url(url, q='#twitter')
        self.assertEqual(
            constructed_url,
            'https://api.twitter.com/1.1/search/tweets.json?q=%23twitter')

    def test_encode(self):
        """Test encoding UTF-8 works"""
        self.api.encode('Twython is awesome!')

    def test_html_for_tweet(self):
        """Test HTML for Tweet returns what we want"""
        tweet_text = self.api.html_for_tweet(test_tweet_object)
        self.assertEqual(test_tweet_html, tweet_text)

    def test_html_for_tweet_reply(self):
        """Test HTML for Tweet links the replied-to username."""
        tweet_text = self.api.html_for_tweet(test_tweet_reply)
        self.assertEqual(
            tweet_text,
            u'<span class="twython-tweet-prefix"><a href="https://twitter.com/philgyford" class="twython-mention">@philgyford</a> </span>Here’s a test tweet that goes on as much as possible and includes an image. Hi to my fans in testland!<span class="twython-tweet-suffix"> https://t.co/tzhyk2QWSr</span>'
        )

    def test_html_for_tweet_expanded_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object,
                                             use_expanded_url=True)
        # Make sure full url is in HTML
        self.assertTrue('http://google.com' in tweet_text)

    def test_html_for_tweet_short_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object, False)
        # Make sure HTML doesn't contain the display OR expanded url
        self.assertTrue('http://google.com' not in tweet_text)
        self.assertTrue('google.com' not in tweet_text)

    def test_html_for_tweet_identical_urls(self):
        """If the 'url's for different url entities are identical, they should link correctly."""
        tweet_text = self.api.html_for_tweet(test_tweet_identical_urls)
        self.assertEqual(
            tweet_text,
            u'Use Cases, Trials and Making 5G a Reality <a href="https://t.co/W0uArTMk9N" class="twython-url">buff.ly/2sEhrgO</a> #5G #innovation via @5GWorldSeries <a href="https://t.co/W0uArTMk9N" class="twython-url">buff.ly/2sEhrgO</a>'
        )

    def test_html_for_tweet_symbols(self):
        tweet_text = self.api.html_for_tweet(test_tweet_symbols_object)
        # Should only link symbols listed in entities:
        self.assertTrue(
            '<a href="https://twitter.com/search?q=%24AAPL" class="twython-symbol">$AAPL</a>'
            in tweet_text)
        self.assertTrue(
            '<a href="https://twitter.com/search?q=%24ANOTHER" class="twython-symbol">$ANOTHER</a>'
            not in tweet_text)

    def test_html_for_tweet_no_symbols(self):
        """Should still work if tweet object has no symbols list"""
        tweet = test_tweet_symbols_object
        # Save a copy:
        symbols = tweet['entities']['symbols']
        del tweet['entities']['symbols']
        tweet_text = self.api.html_for_tweet(tweet)
        self.assertTrue('symbols: $AAPL and' in tweet_text)
        self.assertTrue('and $ANOTHER and $A.' in tweet_text)
        # Put the symbols back:
        test_tweet_symbols_object['entities']['symbols'] = symbols

    def test_html_for_tweet_compatmode(self):
        tweet_text = self.api.html_for_tweet(test_tweet_compat_object)
        # link to compat web status link
        self.assertTrue(
            u'<a href="https://t.co/SRmsuks2ru" class="twython-url">twitter.com/i/web/status/7…</a>'
            in tweet_text)

    def test_html_for_tweet_extendedmode(self):
        tweet_text = self.api.html_for_tweet(test_tweet_extended_object)
        # full tweet rendered with suffix
        self.assertEqual(test_tweet_extended_html, tweet_text)

    def test_cursor_requires_twython_function(self):
        """Test that cursor() raises when called without a Twython function"""
        def init_and_iterate_cursor(*args, **kwargs):
            cursor = self.api.cursor(*args, **kwargs)
            return next(cursor)

        non_function = object()
        non_twython_function = lambda x: x

        self.assertRaises(TypeError, init_and_iterate_cursor, non_function)
        self.assertRaises(TwythonError, init_and_iterate_cursor,
                          non_twython_function)
Example #4
0
class TwythonAPITestCase(unittest.TestCase):
    def setUp(self):

        client_args = {
            'headers': {
                'User-Agent': '__twython__ Test'
            },
            'allow_redirects': False
        }

        oauth2_client_args = {
            'headers': {}  # This is so we can hit coverage that Twython sets User-Agent for us if none is supplied
        }

        self.api = Twython(app_key, app_secret,
                           oauth_token, oauth_token_secret,
                           client_args=client_args)

        self.oauth2_api = Twython(app_key, access_token=access_token,
                                  client_args=oauth2_client_args)

    def test_construct_api_url(self):
        """Test constructing a Twitter API url works as we expect"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        constructed_url = self.api.construct_api_url(url, q='#twitter')
        self.assertEqual(constructed_url, 'https://api.twitter.com/1.1/search/tweets.json?q=%23twitter')

    def test_get(self):
        """Test Twython generic GET request works"""
        self.api.get('account/verify_credentials')

    def test_post(self):
        """Test Twython generic POST request works, with a full url and
        with just an endpoint"""
        update_url = 'https://api.twitter.com/1.1/statuses/update.json'
        status = self.api.post(update_url, params={'status': 'I love Twython!'})
        self.api.post('statuses/destroy/%s' % status['id_str'])

    def test_get_lastfunction_header(self):
        """Test getting last specific header of the last API call works"""
        self.api.get('statuses/home_timeline')
        self.api.get_lastfunction_header('x-rate-limit-remaining')

    def test_get_lastfunction_header_not_present(self):
        """Test getting specific header that does not exist from the last call returns None"""
        self.api.get('statuses/home_timeline')
        header = self.api.get_lastfunction_header('does-not-exist')
        self.assertEqual(header, None)

    def test_get_lastfunction_header_no_last_api_call(self):
        """Test attempting to get a header when no API call was made raises a TwythonError"""
        self.assertRaises(TwythonError, self.api.get_lastfunction_header,
                          'no-api-call-was-made')

    def test_search_gen(self):
        """Test looping through the generator results works, at least once that is"""
        search = self.api.search_gen('twitter', count=1)
        counter = 0
        while counter < 2:
            counter += 1
            result = next(search)
            new_id_str = int(result['id_str'])
            if counter == 1:
                prev_id_str = new_id_str
                time.sleep(1)  # Give time for another tweet to come into search
            if counter == 2:
                self.assertTrue(new_id_str > prev_id_str)

    def test_encode(self):
        """Test encoding UTF-8 works"""
        self.api.encode('Twython is awesome!')

    def test_html_for_tweet(self):
        """Test HTML for Tweet returns what we want"""
        tweet_text = self.api.html_for_tweet(test_tweet_object)
        self.assertEqual(test_tweet_html, tweet_text)

    def test_html_for_tweet_expanded_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object,
                                             use_expanded_url=True)
        # Make sure full url is in HTML
        self.assertTrue('http://google.com' in tweet_text)

    def test_html_for_tweet_short_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object, False)
        # Make sure HTML doesn't contain the display OR exapanded url
        self.assertTrue(not 'http://google.com' in tweet_text)
        self.assertTrue(not 'google.com' in tweet_text)

    def test_raise_error_on_bad_ssl_cert(self):
        """Test TwythonError is raised by a RequestException when an actual HTTP happens"""
        self.assertRaises(TwythonError, self.api.get, 'https://example.com')

    # Timelines
    def test_get_mentions_timeline(self):
        """Test returning mentions timeline for authenticated user succeeds"""
        self.api.get_mentions_timeline()

    def test_get_user_timeline(self):
        """Test returning timeline for authenticated user and random user
        succeeds"""
        self.api.get_user_timeline()  # Authenticated User Timeline
        self.api.get_user_timeline(screen_name='twitter')  # Random User Timeline

    def test_get_protected_user_timeline_following(self):
        """Test returning a protected user timeline who you are following
        succeeds"""
        self.api.get_user_timeline(screen_name=protected_twitter_1)

    def test_get_protected_user_timeline_not_following(self):
        """Test returning a protected user timeline who you are not following
        fails and raise a TwythonAuthError"""
        self.assertRaises(TwythonAuthError, self.api.get_user_timeline,
                          screen_name=protected_twitter_2)

    def test_retweeted_of_me(self):
        """Test that getting recent tweets by authenticated user that have
        been retweeted by others succeeds"""
        self.api.retweeted_of_me()

    def test_get_home_timeline(self):
        """Test returning home timeline for authenticated user succeeds"""
        self.api.get_home_timeline()

    # Tweets
    def test_get_retweets(self):
        """Test getting retweets of a specific tweet succeeds"""
        self.api.get_retweets(id=test_tweet_id)

    def test_show_status(self):
        """Test returning a single status details succeeds"""
        self.api.show_status(id=test_tweet_id)

    def test_update_and_destroy_status(self):
        """Test updating and deleting a status succeeds"""
        status = self.api.update_status(status='Test post just to get deleted :(')
        self.api.destroy_status(id=status['id_str'])

    def test_get_oembed_tweet(self):
        """Test getting info to embed tweet on Third Party site succeeds"""
        self.api.get_oembed_tweet(id='99530515043983360')

    def test_get_retweeters_ids(self):
        """Test getting ids for people who retweeted a tweet succeeds"""
        self.api.get_retweeters_ids(id='99530515043983360')

    # Search
    def test_search(self):
        """Test searching tweets succeeds"""
        self.api.search(q='twitter')

    # Direct Messages
    def test_get_direct_messages(self):
        """Test getting the authenticated users direct messages succeeds"""
        self.api.get_direct_messages()

    def test_get_sent_messages(self):
        """Test getting the authenticated users direct messages they've
        sent succeeds"""
        self.api.get_sent_messages()

    def test_send_get_and_destroy_direct_message(self):
        """Test sending, getting, then destory a direct message succeeds"""
        message = self.api.send_direct_message(screen_name=protected_twitter_1,
                                               text='Hey d00d! %s' % int(time.time()))

        self.api.get_direct_message(id=message['id_str'])
        self.api.destroy_direct_message(id=message['id_str'])

    def test_send_direct_message_to_non_follower(self):
        """Test sending a direct message to someone who doesn't follow you
        fails"""
        self.assertRaises(TwythonError, self.api.send_direct_message,
                          screen_name=protected_twitter_2, text='Yo, man!')

    # Friends & Followers
    def test_get_user_ids_of_blocked_retweets(self):
        """Test that collection of user_ids that the authenticated user does
        not want to receive retweets from succeeds"""
        self.api.get_user_ids_of_blocked_retweets(stringify_ids=True)

    def test_get_friends_ids(self):
        """Test returning ids of users the authenticated user and then a random
        user is following succeeds"""
        self.api.get_friends_ids()
        self.api.get_friends_ids(screen_name='twitter')

    def test_get_followers_ids(self):
        """Test returning ids of users the authenticated user and then a random
        user are followed by succeeds"""
        self.api.get_followers_ids()
        self.api.get_followers_ids(screen_name='twitter')

    def test_lookup_friendships(self):
        """Test returning relationships of the authenticating user to the
        comma-separated list of up to 100 screen_names or user_ids provided
        succeeds"""
        self.api.lookup_friendships(screen_name='twitter,ryanmcgrath')

    def test_get_incoming_friendship_ids(self):
        """Test returning incoming friendship ids succeeds"""
        self.api.get_incoming_friendship_ids()

    def test_get_outgoing_friendship_ids(self):
        """Test returning outgoing friendship ids succeeds"""
        self.api.get_outgoing_friendship_ids()

    def test_create_friendship(self):
        """Test creating a friendship succeeds"""
        self.api.create_friendship(screen_name='justinbieber')

    def test_destroy_friendship(self):
        """Test destroying a friendship succeeds"""
        self.api.destroy_friendship(screen_name='justinbieber')

    def test_update_friendship(self):
        """Test updating friendships succeeds"""
        self.api.update_friendship(screen_name=protected_twitter_1,
                                   retweets='true')

        self.api.update_friendship(screen_name=protected_twitter_1,
                                   retweets=False)

    def test_show_friendships(self):
        """Test showing specific friendship succeeds"""
        self.api.show_friendship(target_screen_name=protected_twitter_1)

    def test_get_friends_list(self):
        """Test getting list of users authenticated user then random user is
        following succeeds"""
        self.api.get_friends_list()
        self.api.get_friends_list(screen_name='twitter')

    def test_get_followers_list(self):
        """Test getting list of users authenticated user then random user are
        followed by succeeds"""
        self.api.get_followers_list()
        self.api.get_followers_list(screen_name='twitter')

    # Users
    def test_get_account_settings(self):
        """Test getting the authenticated user account settings succeeds"""
        self.api.get_account_settings()

    def test_verify_credentials(self):
        """Test representation of the authenticated user call succeeds"""
        self.api.verify_credentials()

    def test_update_account_settings(self):
        """Test updating a user account settings succeeds"""
        self.api.update_account_settings(lang='en')

    def test_update_delivery_service(self):
        """Test updating delivery settings fails because we don't have
        a mobile number on the account"""
        self.assertRaises(TwythonError, self.api.update_delivery_service,
                          device='none')

    def test_update_profile(self):
        """Test updating profile succeeds"""
        self.api.update_profile(include_entities='true')

    def test_update_profile_colors(self):
        """Test updating profile colors succeeds"""
        self.api.update_profile_colors(profile_background_color='3D3D3D')

    def test_list_blocks(self):
        """Test listing users who are blocked by the authenticated user
        succeeds"""
        self.api.list_blocks()

    def test_list_block_ids(self):
        """Test listing user ids who are blocked by the authenticated user
        succeeds"""
        self.api.list_block_ids()

    def test_create_block(self):
        """Test blocking a user succeeds"""
        self.api.create_block(screen_name='justinbieber')

    def test_destroy_block(self):
        """Test unblocking a user succeeds"""
        self.api.destroy_block(screen_name='justinbieber')

    def test_lookup_user(self):
        """Test listing a number of user objects succeeds"""
        self.api.lookup_user(screen_name='twitter,justinbieber')

    def test_show_user(self):
        """Test showing one user works"""
        self.api.show_user(screen_name='twitter')

    def test_search_users(self):
        """Test that searching for users succeeds"""
        self.api.search_users(q='Twitter API')

    def test_get_contributees(self):
        """Test returning list of accounts the specified user can
        contribute to succeeds"""
        self.api.get_contributees(screen_name='TechCrunch')

    def test_get_contributors(self):
        """Test returning list of accounts that contribute to the
        authenticated user fails because we are not a Contributor account"""
        self.assertRaises(TwythonError, self.api.get_contributors,
                          screen_name=screen_name)

    def test_remove_profile_banner(self):
        """Test removing profile banner succeeds"""
        self.api.remove_profile_banner()

    def test_get_profile_banner_sizes(self):
        """Test getting list of profile banner sizes fails because
        we have not uploaded a profile banner"""
        self.assertRaises(TwythonError, self.api.get_profile_banner_sizes)

    # Suggested Users
    def test_get_user_suggestions_by_slug(self):
        """Test getting user suggestions by slug succeeds"""
        self.api.get_user_suggestions_by_slug(slug='twitter')

    def test_get_user_suggestions(self):
        """Test getting user suggestions succeeds"""
        self.api.get_user_suggestions()

    def test_get_user_suggestions_statuses_by_slug(self):
        """Test getting status of suggested users succeeds"""
        self.api.get_user_suggestions_statuses_by_slug(slug='funny')

    # Favorites
    def test_get_favorites(self):
        """Test getting list of favorites for the authenticated
        user succeeds"""
        self.api.get_favorites()

    def test_create_and_destroy_favorite(self):
        """Test creating and destroying a favorite on a tweet succeeds"""
        self.api.create_favorite(id=test_tweet_id)
        self.api.destroy_favorite(id=test_tweet_id)

    # Lists
    def test_show_lists(self):
        """Test show lists for specified user"""
        self.api.show_lists(screen_name='twitter')

    def test_get_list_statuses(self):
        """Test timeline of tweets authored by members of the
        specified list succeeds"""
        self.api.get_list_statuses(list_id=test_list_id)

    def test_create_update_destroy_list_add_remove_list_members(self):
        """Test create a list, adding and removing members then
        deleting the list succeeds"""
        the_list = self.api.create_list(name='Stuff')
        list_id = the_list['id_str']

        self.api.update_list(list_id=list_id, name='Stuff Renamed')

        screen_names = ['johncena', 'xbox']
        # Multi add/delete members
        self.api.create_list_members(list_id=list_id,
                                     screen_name=screen_names)
        self.api.delete_list_members(list_id=list_id,
                                     screen_name=screen_names)

        # Single add/delete member
        self.api.add_list_member(list_id=list_id, screen_name='justinbieber')
        self.api.delete_list_member(list_id=list_id, screen_name='justinbieber')

        self.api.delete_list(list_id=list_id)

    def test_get_list_subscribers(self):
        """Test list of subscribers of a specific list succeeds"""
        self.api.get_list_subscribers(list_id=test_list_id)

    def test_subscribe_is_subbed_and_unsubscribe_to_list(self):
        """Test subscribing, is a list sub and unsubbing to list succeeds"""
        self.api.subscribe_to_list(list_id=test_list_id)
        # Returns 404 if user is not a subscriber
        self.api.is_list_subscriber(list_id=test_list_id,
                                    screen_name=screen_name)
        self.api.unsubscribe_from_list(list_id=test_list_id)

    def test_is_list_member(self):
        """Test returning if specified user is member of a list succeeds"""
        # Returns 404 if not list member
        self.api.is_list_member(list_id=test_list_id, screen_name='jack')

    def test_get_list_members(self):
        """Test listing members of the specified list succeeds"""
        self.api.get_list_members(list_id=test_list_id)

    def test_get_specific_list(self):
        """Test getting specific list succeeds"""
        self.api.get_specific_list(list_id=test_list_id)

    def test_get_list_subscriptions(self):
        """Test collection of the lists the specified user is
        subscribed to succeeds"""
        self.api.get_list_subscriptions(screen_name='twitter')

    def test_show_owned_lists(self):
        """Test collection of lists the specified user owns succeeds"""
        self.api.show_owned_lists(screen_name='twitter')

    # Saved Searches
    def test_get_saved_searches(self):
        """Test getting list of saved searches for authenticated
        user succeeds"""
        self.api.get_saved_searches()

    def test_create_get_destroy_saved_search(self):
        """Test getting list of saved searches for authenticated
        user succeeds"""
        saved_search = self.api.create_saved_search(query='#Twitter')
        saved_search_id = saved_search['id_str']

        self.api.show_saved_search(id=saved_search_id)
        self.api.destroy_saved_search(id=saved_search_id)

    # Places & Geo
    def test_get_geo_info(self):
        """Test getting info about a geo location succeeds"""
        self.api.get_geo_info(place_id='df51dec6f4ee2b2c')

    def test_reverse_geo_code(self):
        """Test reversing geocode succeeds"""
        self.api.reverse_geocode(lat='37.76893497', long='-122.42284884')

    def test_search_geo(self):
        """Test search for places that can be attached
        to a statuses/update succeeds"""
        self.api.search_geo(query='Toronto')

    def test_get_similar_places(self):
        """Test locates places near the given coordinates which
        are similar in name succeeds"""
        self.api.get_similar_places(lat='37', long='-122', name='Twitter HQ')

    # Trends
    def test_get_place_trends(self):
        """Test getting the top 10 trending topics for a specific
        WOEID succeeds"""
        self.api.get_place_trends(id=1)

    def test_get_available_trends(self):
        """Test returning locations that Twitter has trending
        topic information for succeeds"""
        self.api.get_available_trends()

    def test_get_closest_trends(self):
        """Test getting the locations that Twitter has trending topic
        information for, closest to a specified location succeeds"""
        self.api.get_closest_trends(lat='37', long='-122')

    # Help
    def test_get_application_rate_limit_status(self):
        """Test getting application rate limit status succeeds"""
        self.oauth2_api.get_application_rate_limit_status()
Example #5
0
class TwythonAPITestCase(unittest.TestCase):
    def setUp(self):

        client_args = {
            'headers': {
                'User-Agent': '__twython__ Test'
            },
            'allow_redirects': False
        }

        oauth2_client_args = {
            'headers': {
            }  # This is so we can hit coverage that Twython sets User-Agent for us if none is supplied
        }

        self.api = Twython(app_key,
                           app_secret,
                           oauth_token,
                           oauth_token_secret,
                           client_args=client_args)

        self.oauth2_api = Twython(app_key,
                                  access_token=access_token,
                                  client_args=oauth2_client_args)

    def test_construct_api_url(self):
        """Test constructing a Twitter API url works as we expect"""
        url = 'https://api.twitter.com/1.1/search/tweets.json'
        constructed_url = self.api.construct_api_url(url, q='#twitter')
        self.assertEqual(
            constructed_url,
            'https://api.twitter.com/1.1/search/tweets.json?q=%23twitter')

    def test_get(self):
        """Test Twython generic GET request works"""
        self.api.get('account/verify_credentials')

    def test_post(self):
        """Test Twython generic POST request works, with a full url and
        with just an endpoint"""
        update_url = 'https://api.twitter.com/1.1/statuses/update.json'
        status = self.api.post(
            update_url,
            params={'status': 'I love Twython! %s' % int(time.time())})
        self.api.post('statuses/destroy/%s' % status['id_str'])

    def test_get_lastfunction_header(self):
        """Test getting last specific header of the last API call works"""
        self.api.get('statuses/home_timeline')
        self.api.get_lastfunction_header('x-rate-limit-remaining')

    def test_get_lastfunction_header_not_present(self):
        """Test getting specific header that does not exist from the last call returns None"""
        self.api.get('statuses/home_timeline')
        header = self.api.get_lastfunction_header('does-not-exist')
        self.assertEqual(header, None)

    def test_get_lastfunction_header_no_last_api_call(self):
        """Test attempting to get a header when no API call was made raises a TwythonError"""
        self.assertRaises(TwythonError, self.api.get_lastfunction_header,
                          'no-api-call-was-made')

    def test_cursor(self):
        """Test looping through the generator results works, at least once that is"""
        search = self.api.cursor(self.api.search, q='twitter', count=1)
        counter = 0
        while counter < 2:
            counter += 1
            result = next(search)
            new_id_str = int(result['id_str'])
            if counter == 1:
                prev_id_str = new_id_str
                time.sleep(
                    1)  # Give time for another tweet to come into search
            if counter == 2:
                self.assertTrue(new_id_str > prev_id_str)

    def test_encode(self):
        """Test encoding UTF-8 works"""
        self.api.encode('Twython is awesome!')

    def test_html_for_tweet(self):
        """Test HTML for Tweet returns what we want"""
        tweet_text = self.api.html_for_tweet(test_tweet_object)
        self.assertEqual(test_tweet_html, tweet_text)

    def test_html_for_tweet_expanded_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object,
                                             use_expanded_url=True)
        # Make sure full url is in HTML
        self.assertTrue('http://google.com' in tweet_text)

    def test_html_for_tweet_short_url(self):
        """Test using expanded url in HTML for Tweet displays full urls"""
        tweet_text = self.api.html_for_tweet(test_tweet_object, False)
        # Make sure HTML doesn't contain the display OR exapanded url
        self.assertTrue(not 'http://google.com' in tweet_text)
        self.assertTrue(not 'google.com' in tweet_text)

    def test_raise_error_on_bad_ssl_cert(self):
        """Test TwythonError is raised by a RequestException when an actual HTTP happens"""
        self.assertRaises(TwythonError, self.api.get, 'https://example.com')

    # Timelines
    def test_get_mentions_timeline(self):
        """Test returning mentions timeline for authenticated user succeeds"""
        self.api.get_mentions_timeline()

    def test_get_user_timeline(self):
        """Test returning timeline for authenticated user and random user
        succeeds"""
        self.api.get_user_timeline()  # Authenticated User Timeline
        self.api.get_user_timeline(
            screen_name='twitter')  # Random User Timeline

    def test_get_protected_user_timeline_following(self):
        """Test returning a protected user timeline who you are following
        succeeds"""
        self.api.get_user_timeline(screen_name=protected_twitter_1)

    def test_get_protected_user_timeline_not_following(self):
        """Test returning a protected user timeline who you are not following
        fails and raise a TwythonAuthError"""
        self.assertRaises(TwythonAuthError,
                          self.api.get_user_timeline,
                          screen_name=protected_twitter_2)

    def test_retweeted_of_me(self):
        """Test that getting recent tweets by authenticated user that have
        been retweeted by others succeeds"""
        self.api.retweeted_of_me()

    def test_get_home_timeline(self):
        """Test returning home timeline for authenticated user succeeds"""
        self.api.get_home_timeline()

    # Tweets
    def test_get_retweets(self):
        """Test getting retweets of a specific tweet succeeds"""
        self.api.get_retweets(id=test_tweet_id)

    def test_show_status(self):
        """Test returning a single status details succeeds"""
        self.api.show_status(id=test_tweet_id)

    def test_update_and_destroy_status(self):
        """Test updating and deleting a status succeeds"""
        status = self.api.update_status(
            status='Test post just to get deleted :( %s' % int(time.time()))
        self.api.destroy_status(id=status['id_str'])

    def test_get_oembed_tweet(self):
        """Test getting info to embed tweet on Third Party site succeeds"""
        self.api.get_oembed_tweet(id='99530515043983360')

    def test_get_retweeters_ids(self):
        """Test getting ids for people who retweeted a tweet succeeds"""
        self.api.get_retweeters_ids(id='99530515043983360')

    # Search
    def test_search(self):
        """Test searching tweets succeeds"""
        self.api.search(q='twitter')

    # Direct Messages
    def test_get_direct_messages(self):
        """Test getting the authenticated users direct messages succeeds"""
        self.api.get_direct_messages()

    def test_get_sent_messages(self):
        """Test getting the authenticated users direct messages they've
        sent succeeds"""
        self.api.get_sent_messages()

    def test_send_get_and_destroy_direct_message(self):
        """Test sending, getting, then destory a direct message succeeds"""
        message = self.api.send_direct_message(screen_name=protected_twitter_1,
                                               text='Hey d00d! %s' %
                                               int(time.time()))

        self.api.get_direct_message(id=message['id_str'])
        self.api.destroy_direct_message(id=message['id_str'])

    def test_send_direct_message_to_non_follower(self):
        """Test sending a direct message to someone who doesn't follow you
        fails"""
        self.assertRaises(TwythonError,
                          self.api.send_direct_message,
                          screen_name=protected_twitter_2,
                          text='Yo, man! %s' % int(time.time()))

    # Friends & Followers
    def test_get_user_ids_of_blocked_retweets(self):
        """Test that collection of user_ids that the authenticated user does
        not want to receive retweets from succeeds"""
        self.api.get_user_ids_of_blocked_retweets(stringify_ids=True)

    def test_get_friends_ids(self):
        """Test returning ids of users the authenticated user and then a random
        user is following succeeds"""
        self.api.get_friends_ids()
        self.api.get_friends_ids(screen_name='twitter')

    def test_get_followers_ids(self):
        """Test returning ids of users the authenticated user and then a random
        user are followed by succeeds"""
        self.api.get_followers_ids()
        self.api.get_followers_ids(screen_name='twitter')

    def test_lookup_friendships(self):
        """Test returning relationships of the authenticating user to the
        comma-separated list of up to 100 screen_names or user_ids provided
        succeeds"""
        self.api.lookup_friendships(screen_name='twitter,ryanmcgrath')

    def test_get_incoming_friendship_ids(self):
        """Test returning incoming friendship ids succeeds"""
        self.api.get_incoming_friendship_ids()

    def test_get_outgoing_friendship_ids(self):
        """Test returning outgoing friendship ids succeeds"""
        self.api.get_outgoing_friendship_ids()

    def test_create_friendship(self):
        """Test creating a friendship succeeds"""
        self.api.create_friendship(screen_name='justinbieber')

    def test_destroy_friendship(self):
        """Test destroying a friendship succeeds"""
        self.api.destroy_friendship(screen_name='justinbieber')

    def test_update_friendship(self):
        """Test updating friendships succeeds"""
        self.api.update_friendship(screen_name=protected_twitter_1,
                                   retweets='true')

        self.api.update_friendship(screen_name=protected_twitter_1,
                                   retweets=False)

    def test_show_friendships(self):
        """Test showing specific friendship succeeds"""
        self.api.show_friendship(target_screen_name=protected_twitter_1)

    def test_get_friends_list(self):
        """Test getting list of users authenticated user then random user is
        following succeeds"""
        self.api.get_friends_list()
        self.api.get_friends_list(screen_name='twitter')

    def test_get_followers_list(self):
        """Test getting list of users authenticated user then random user are
        followed by succeeds"""
        self.api.get_followers_list()
        self.api.get_followers_list(screen_name='twitter')

    # Users
    def test_get_account_settings(self):
        """Test getting the authenticated user account settings succeeds"""
        self.api.get_account_settings()

    def test_verify_credentials(self):
        """Test representation of the authenticated user call succeeds"""
        self.api.verify_credentials()

    def test_update_account_settings(self):
        """Test updating a user account settings succeeds"""
        self.api.update_account_settings(lang='en')

    def test_update_delivery_service(self):
        """Test updating delivery settings fails because we don't have
        a mobile number on the account"""
        self.assertRaises(TwythonError,
                          self.api.update_delivery_service,
                          device='none')

    def test_update_profile(self):
        """Test updating profile succeeds"""
        self.api.update_profile(include_entities='true')

    def test_update_profile_colors(self):
        """Test updating profile colors succeeds"""
        self.api.update_profile_colors(profile_background_color='3D3D3D')

    def test_list_blocks(self):
        """Test listing users who are blocked by the authenticated user
        succeeds"""
        self.api.list_blocks()

    def test_list_block_ids(self):
        """Test listing user ids who are blocked by the authenticated user
        succeeds"""
        self.api.list_block_ids()

    def test_create_block(self):
        """Test blocking a user succeeds"""
        self.api.create_block(screen_name='justinbieber')

    def test_destroy_block(self):
        """Test unblocking a user succeeds"""
        self.api.destroy_block(screen_name='justinbieber')

    def test_lookup_user(self):
        """Test listing a number of user objects succeeds"""
        self.api.lookup_user(screen_name='twitter,justinbieber')

    def test_show_user(self):
        """Test showing one user works"""
        self.api.show_user(screen_name='twitter')

    def test_search_users(self):
        """Test that searching for users succeeds"""
        self.api.search_users(q='Twitter API')

    def test_get_contributees(self):
        """Test returning list of accounts the specified user can
        contribute to succeeds"""
        self.api.get_contributees(screen_name='TechCrunch')

    def test_get_contributors(self):
        """Test returning list of accounts that contribute to the
        authenticated user fails because we are not a Contributor account"""
        self.assertRaises(TwythonError,
                          self.api.get_contributors,
                          screen_name=screen_name)

    def test_remove_profile_banner(self):
        """Test removing profile banner succeeds"""
        self.api.remove_profile_banner()

    def test_get_profile_banner_sizes(self):
        """Test getting list of profile banner sizes fails because
        we have not uploaded a profile banner"""
        self.assertRaises(TwythonError, self.api.get_profile_banner_sizes)

    # Suggested Users
    def test_get_user_suggestions_by_slug(self):
        """Test getting user suggestions by slug succeeds"""
        self.api.get_user_suggestions_by_slug(slug='twitter')

    def test_get_user_suggestions(self):
        """Test getting user suggestions succeeds"""
        self.api.get_user_suggestions()

    def test_get_user_suggestions_statuses_by_slug(self):
        """Test getting status of suggested users succeeds"""
        self.api.get_user_suggestions_statuses_by_slug(slug='funny')

    # Favorites
    def test_get_favorites(self):
        """Test getting list of favorites for the authenticated
        user succeeds"""
        self.api.get_favorites()

    def test_create_and_destroy_favorite(self):
        """Test creating and destroying a favorite on a tweet succeeds"""
        self.api.create_favorite(id=test_tweet_id)
        self.api.destroy_favorite(id=test_tweet_id)

    # Lists
    def test_show_lists(self):
        """Test show lists for specified user"""
        self.api.show_lists(screen_name='twitter')

    def test_get_list_statuses(self):
        """Test timeline of tweets authored by members of the
        specified list succeeds"""
        self.api.get_list_statuses(
            slug=test_list_slug, owner_screen_name=test_list_owner_screen_name)

    def test_create_update_destroy_list_add_remove_list_members(self):
        """Test create a list, adding and removing members then
        deleting the list succeeds"""
        the_list = self.api.create_list(name='Stuff %s' % int(time.time()))
        list_id = the_list['id_str']

        self.api.update_list(list_id=list_id,
                             name='Stuff Renamed %s' % int(time.time()))

        screen_names = ['johncena', 'xbox']
        # Multi add/delete members
        self.api.create_list_members(list_id=list_id, screen_name=screen_names)
        self.api.delete_list_members(list_id=list_id, screen_name=screen_names)

        # Single add/delete member
        self.api.add_list_member(list_id=list_id, screen_name='justinbieber')
        self.api.delete_list_member(list_id=list_id,
                                    screen_name='justinbieber')

        self.api.delete_list(list_id=list_id)

    def test_get_list_memberships(self):
        """Test list of memberhips the authenticated user succeeds"""
        self.api.get_list_memberships()

    def test_get_list_subscribers(self):
        """Test list of subscribers of a specific list succeeds"""
        self.api.get_list_subscribers(
            slug=test_list_slug, owner_screen_name=test_list_owner_screen_name)

    def test_subscribe_is_subbed_and_unsubscribe_to_list(self):
        """Test subscribing, is a list sub and unsubbing to list succeeds"""
        self.api.subscribe_to_list(
            slug=test_list_slug, owner_screen_name=test_list_owner_screen_name)
        # Returns 404 if user is not a subscriber
        self.api.is_list_subscriber(
            slug=test_list_slug,
            owner_screen_name=test_list_owner_screen_name,
            screen_name=screen_name)
        self.api.unsubscribe_from_list(
            slug=test_list_slug, owner_screen_name=test_list_owner_screen_name)

    def test_is_list_member(self):
        """Test returning if specified user is member of a list succeeds"""
        # Returns 404 if not list member
        self.api.is_list_member(slug=test_list_slug,
                                owner_screen_name=test_list_owner_screen_name,
                                screen_name='themattharris')

    def test_get_list_members(self):
        """Test listing members of the specified list succeeds"""
        self.api.get_list_members(
            slug=test_list_slug, owner_screen_name=test_list_owner_screen_name)

    def test_get_specific_list(self):
        """Test getting specific list succeeds"""
        self.api.get_specific_list(
            slug=test_list_slug, owner_screen_name=test_list_owner_screen_name)

    def test_get_list_subscriptions(self):
        """Test collection of the lists the specified user is
        subscribed to succeeds"""
        self.api.get_list_subscriptions(screen_name='twitter')

    def test_show_owned_lists(self):
        """Test collection of lists the specified user owns succeeds"""
        self.api.show_owned_lists(screen_name='twitter')

    # Saved Searches
    def test_get_saved_searches(self):
        """Test getting list of saved searches for authenticated
        user succeeds"""
        self.api.get_saved_searches()

    def test_create_get_destroy_saved_search(self):
        """Test getting list of saved searches for authenticated
        user succeeds"""
        saved_search = self.api.create_saved_search(query='#Twitter')
        saved_search_id = saved_search['id_str']

        self.api.show_saved_search(id=saved_search_id)
        self.api.destroy_saved_search(id=saved_search_id)

    # Places & Geo
    def test_get_geo_info(self):
        """Test getting info about a geo location succeeds"""
        self.api.get_geo_info(place_id='df51dec6f4ee2b2c')

    def test_reverse_geo_code(self):
        """Test reversing geocode succeeds"""
        self.api.reverse_geocode(lat='37.76893497', long='-122.42284884')

    def test_search_geo(self):
        """Test search for places that can be attached
        to a statuses/update succeeds"""
        self.api.search_geo(query='Toronto')

    def test_get_similar_places(self):
        """Test locates places near the given coordinates which
        are similar in name succeeds"""
        self.api.get_similar_places(lat='37', long='-122', name='Twitter HQ')

    # Trends
    def test_get_place_trends(self):
        """Test getting the top 10 trending topics for a specific
        WOEID succeeds"""
        self.api.get_place_trends(id=1)

    def test_get_available_trends(self):
        """Test returning locations that Twitter has trending
        topic information for succeeds"""
        self.api.get_available_trends()

    def test_get_closest_trends(self):
        """Test getting the locations that Twitter has trending topic
        information for, closest to a specified location succeeds"""
        self.api.get_closest_trends(lat='37', long='-122')

    # Help
    def test_get_twitter_configuration(self):
        """Test getting Twitter's configuration succeeds"""
        self.api.get_twitter_configuration()

    def test_get_supported_languages(self):
        """Test getting languages supported by Twitter succeeds"""
        self.api.get_supported_languages()

    def test_privacy_policy(self):
        """Test getting Twitter's Privacy Policy succeeds"""
        self.api.get_privacy_policy()

    def test_get_tos(self):
        """Test getting the Twitter Terms of Service succeeds"""
        self.api.get_tos()

    def test_get_application_rate_limit_status(self):
        """Test getting application rate limit status succeeds"""
        self.oauth2_api.get_application_rate_limit_status()