class FakeStream(object): def __init__(self): self.resp = FakeResponse(None) self._message_types = {} def add_message_type(self, message_type, predicate): self._message_types[message_type] = predicate def accepts(self, message_type, data): predicate = self._message_types.get(message_type) return predicate is not None and predicate(data) def deliver(self, data): self.resp.deliver_data(json.dumps(data)) self.resp.deliver_data('\r\n')
def test_stream_filter_track(self): agent, client = self._agent_and_TwitterClient() uri = 'https://stream.twitter.com/1.1/statuses/filter.json' stream = FakeResponse(None) agent.add_expected_request('POST', uri, {'track': 'foo,bar'}, stream) connected = Deferred() tweets = [] svc = client.stream_filter(tweets.append, track=['foo', 'bar']) svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) self.assertEqual(tweets, []) stream.deliver_data( '{"id_str": "1", "text": "Tweet 1", "user": {}}\r\n') self.assertEqual(tweets, [ {"id_str": "1", "text": "Tweet 1", "user": {}}, ]) stream.deliver_data( '{"id_str": "2", "text": "Tweet 2", "user": {}}\r\n') self.assertEqual(tweets, [ {"id_str": "1", "text": "Tweet 1", "user": {}}, {"id_str": "2", "text": "Tweet 2", "user": {}}, ]) yield svc.stopService() stream.finished()
def test_userstream_user_with_user(self): agent, client = self._agent_and_TwitterClient() uri = 'https://userstream.twitter.com/1.1/user.json' stream = FakeResponse(None) agent.add_expected_request('GET', uri, { 'stringify_friend_ids': 'true', 'with': 'user', }, stream) connected = Deferred() tweets = [] svc = client.userstream_user(tweets.append, with_='user') svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) self.assertEqual(tweets, []) stream.deliver_data( '{"friends_str": []}\r\n' '{"id_str": "1", "text": "Tweet 1", "user": {}}\r\n') self.assertEqual(tweets, [ {"friends_str": []}, {"id_str": "1", "text": "Tweet 1", "user": {}}, ]) stream.deliver_data( '{"id_str": "2", "text": "Tweet 2", "user": {}}\r\n') self.assertEqual(tweets, [ {"friends_str": []}, {"id_str": "1", "text": "Tweet 1", "user": {}}, {"id_str": "2", "text": "Tweet 2", "user": {}}, ]) yield svc.stopService() stream.finished()
def test_HTTP_500_schedules_reconnect(self): """ An HTTP error should schedule a reconnection attempt. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.clock = Clock() svc.startService() d.callback(FakeResponse(None, 500)) self.assertEqual(svc._connect, svc._reconnect_delayedcall.func)
def test_stream_filter_all_params(self): agent, client = self._agent_and_TwitterClient() uri = 'https://stream.twitter.com/1.1/statuses/filter.json' stream = FakeResponse(None) expected_params = { 'follow': 'Alice,Bob', 'track': 'foo,bar', 'stall_warnings': 'true', } agent.add_expected_request('POST', uri, expected_params, stream) connected = Deferred() svc = client.stream_filter( lambda tweet: None, follow=['Alice', 'Bob'], track=['foo', 'bar'], stall_warnings=True) svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) yield svc.stopService() stream.finished()
def test_connect_callback(self): """ The connect callback should be called on a successful connection. """ d = Deferred() called = [] svc = self._TwitterStreamService(lambda: d, None) svc.set_connect_callback(lambda s: called.append(s)) svc.startService() self.assertEqual(called, []) d.callback(FakeResponse(None)) self.assertEqual(called, [svc])
def test_userstream_user_all_params(self): agent, client = self._agent_and_TwitterClient() uri = 'https://userstream.twitter.com/1.1/user.json' stream = FakeResponse(None) agent.add_expected_request('GET', uri, { 'stringify_friend_ids': 'true', 'stall_warnings': 'true', 'with': 'user', 'replies': 'all', }, stream) connected = Deferred() svc = client.userstream_user( lambda tweet: None, stall_warnings=True, with_='user', replies='all') svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) yield svc.stopService() stream.finished()
def test_rate_limit_second_reconnect_delay(self): """ The second HTTP rate limit response should double the reconnect delay. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.clock = Clock() svc.reconnect_delay = 60 svc.startService() d.callback(FakeResponse(None, 420)) self.assertEqual(svc.reconnect_delay, 120)
def test_reconnect(self): """ A reconnect should wait for the required amount of time and then attempt to connect again. """ d1 = Deferred() d2 = Deferred() connect_deferreds = [d1, d2] called = [] svc = self._TwitterStreamService( lambda: connect_deferreds.pop(0), None) svc.set_connect_callback(lambda s: called.append(s)) svc.clock = Clock() svc.startService() d1.callback(FakeResponse(None, 500)) self.assertEqual([], called) svc.clock.advance(svc.reconnect_delay) self.assertEqual(svc._reconnect_delayedcall, None) d2.callback(FakeResponse(None)) self.assertEqual([svc], called)
def test_userstream_user_with_user(self): agent, client = self._agent_and_TwitterClient() uri = 'https://userstream.twitter.com/1.1/user.json' stream = FakeResponse(None) agent.add_expected_request('GET', uri, { 'stringify_friend_ids': 'true', 'with': 'user', }, stream) connected = Deferred() tweets = [] svc = client.userstream_user(tweets.append, with_='user') svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) self.assertEqual(tweets, []) stream.deliver_data( '{"friends_str": []}\r\n' '{"id_str": "1", "text": "Tweet 1", "user": {}}\r\n') self.assertEqual(tweets, [ { "friends_str": [] }, { "id_str": "1", "text": "Tweet 1", "user": {} }, ]) stream.deliver_data( '{"id_str": "2", "text": "Tweet 2", "user": {}}\r\n') self.assertEqual(tweets, [ { "friends_str": [] }, { "id_str": "1", "text": "Tweet 1", "user": {} }, { "id_str": "2", "text": "Tweet 2", "user": {} }, ]) yield svc.stopService() stream.finished()
def test_HTTP_500_max_reconnect_delay(self): """ The reconnect delay should never go over the maximum of ten minutes. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.clock = Clock() svc.reconnect_delay = 60 * 60 * 24 svc.startService() self.assertEqual(svc.reconnect_delay, 60 * 60 * 24) d.callback(FakeResponse(None, 500)) self.assertEqual(svc.reconnect_delay, 60 * 10)
def test_rate_limit_initial_reconnect_delay_existing_delay(self): """ The first HTTP rate limit response should set the reconnect delay to one minute if there is an existing delay less than this. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.clock = Clock() svc.reconnect_delay = 16 svc.startService() d.callback(FakeResponse(None, 420)) self.assertEqual(svc.reconnect_delay, 60)
def test_stream_filter_all_params(self): agent, client = self._agent_and_TwitterClient() uri = 'https://stream.twitter.com/1.1/statuses/filter.json' stream = FakeResponse(None) expected_params = { 'follow': 'Alice,Bob', 'track': 'foo,bar', 'stall_warnings': 'true', } agent.add_expected_request('POST', uri, expected_params, stream) connected = Deferred() svc = client.stream_filter(lambda tweet: None, follow=['Alice', 'Bob'], track=['foo', 'bar'], stall_warnings=True) svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) yield svc.stopService() stream.finished()
def test_HTTP_500_initial_reconnect_delay(self): """ The first HTTP error response should set the initial reconnect delay to one second. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.clock = Clock() svc.startService() self.assertEqual(svc.reconnect_delay, 0) d.callback(FakeResponse(None, 500)) self.assertEqual(svc.reconnect_delay, 1)
def test_HTTP_500_second_reconnect_delay(self): """ An HTTP error response when we already have a reconnect delay should double the delay and attempt to reconnect again. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.clock = Clock() svc.reconnect_delay = 1 svc.startService() self.assertEqual(svc.reconnect_delay, 1) d.callback(FakeResponse(None, 500)) self.assertEqual(svc.reconnect_delay, 2)
def test_userstream_user_all_params(self): agent, client = self._agent_and_TwitterClient() uri = 'https://userstream.twitter.com/1.1/user.json' stream = FakeResponse(None) agent.add_expected_request( 'GET', uri, { 'stringify_friend_ids': 'true', 'stall_warnings': 'true', 'with': 'user', 'replies': 'all', }, stream) connected = Deferred() svc = client.userstream_user(lambda tweet: None, stall_warnings=True, with_='user', replies='all') svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) yield svc.stopService() stream.finished()
def test_HTTP_500_calls_disconnect_callback(self): """ An HTTP error should schedule a reconnection attempt. """ from txtwitter.error import TwitterAPIError d = Deferred() called = [] svc = self._TwitterStreamService(lambda: d, None) svc.set_disconnect_callback(lambda s, r: called.append(r)) svc.clock = Clock() svc.startService() d.callback(FakeResponse(None, 500)) [failure] = called self.assertEqual(TwitterAPIError, type(failure.value))
def test_stop_service_connected(self): """ Stopping a connected service should close the connection cleanly. """ d = Deferred() called = [] svc = self._TwitterStreamService(lambda: d, None) svc.set_disconnect_callback(lambda s, r: called.append(r)) svc.startService() d.callback(FakeResponse(None)) self.assertEqual(svc.running, True) svc.stopService() self.assertEqual(svc.running, False) [failure] = called self.assertEqual(ResponseDone, type(failure.value))
def test_connect_callback_None(self): """ The connect callback should not be called if it is unset. It's hard to assert that something /doesn't/ happen, so we make sure we see something else that happens at connect time and assert that no errors were logged. """ d = Deferred() svc = self._TwitterStreamService(lambda: d, None) svc.startService() self.assertEqual(None, svc.connect_callback) self.assertEqual(None, svc._stream_response) d.callback(FakeResponse(None)) self.assertNotEqual(None, svc._stream_response) self.assertEqual([], self.flushLoggedErrors())
def test_stream_filter_track(self): agent, client = self._agent_and_TwitterClient() uri = 'https://stream.twitter.com/1.1/statuses/filter.json' stream = FakeResponse(None) agent.add_expected_request('POST', uri, {'track': 'foo,bar'}, stream) connected = Deferred() tweets = [] svc = client.stream_filter(tweets.append, track=['foo', 'bar']) svc.set_connect_callback(connected.callback) svc.startService() connected_svc = yield connected self.assertIs(svc, connected_svc) self.assertEqual(tweets, []) stream.deliver_data( '{"id_str": "1", "text": "Tweet 1", "user": {}}\r\n') self.assertEqual(tweets, [ { "id_str": "1", "text": "Tweet 1", "user": {} }, ]) stream.deliver_data( '{"id_str": "2", "text": "Tweet 2", "user": {}}\r\n') self.assertEqual(tweets, [ { "id_str": "1", "text": "Tweet 1", "user": {} }, { "id_str": "2", "text": "Tweet 2", "user": {} }, ]) yield svc.stopService() stream.finished()
def test_stop_service_pending_reconnect(self): """ Stopping a service with a pending reconnect should cancel the reconnect. """ d = Deferred() called = [] svc = self._TwitterStreamService(lambda: d, None) svc.set_disconnect_callback(lambda s, r: called.append(r)) svc.clock = Clock() svc.startService() d.callback(FakeResponse(None, 500)) self.assertEqual(svc.running, True) self.assertNotEqual(svc._reconnect_delayedcall, None) self.assertEqual(len(called), 1) self.assertNotEqual(svc.reconnect_delay, 0) svc.stopService() self.assertEqual(svc.running, False) self.assertEqual(svc._reconnect_delayedcall, None) self.assertEqual(svc.reconnect_delay, 0)
def __init__(self): self.resp = FakeResponse(None) self._message_types = {}
def _resp_json(self, data, code=200): return FakeResponse(json.dumps(data), code)