def test_wrong_unsubscribe_ack(self): with make_client(endpoint=endpoint, appkey=appkey) as client: client.observer = ClientObserver() sync_subscribe(client, channel) old_received_message =\ client._internal.connection.on_incoming_text_frame client._internal.connection.on_incoming_text_frame =\ lambda *args: None client.unsubscribe(channel) client._queue.join() old_received_message( b'{"action":"rtm/publish/ok","body":{},"id":1}') client._queue.join() client.observer.wait_connected() expected_log = [ 'on_leave_connected', 'on_enter_awaiting', 'on_leave_awaiting', 'on_enter_connecting', 'on_leave_connecting', 'on_enter_connected', ] self.assertEqual(client.observer.log, expected_log)
def _run(self, N): channel = make_channel_name('two_identical_subscribers') with make_client(endpoint=endpoint, appkey=appkey) as pub: with make_client(endpoint=endpoint, appkey=appkey, protocol='cbor') as sub1: with make_client(endpoint=endpoint, appkey=appkey) as sub2: origin = sync_publish(pub, channel, u'prime') so1 = sync_subscribe(sub1, channel, {u'position': origin}) so2 = sync_subscribe(sub2, channel, {u'position': origin}) for i in range(N): pub.publish(channel, i) msgs1 = [] msgs2 = [] origin = time.time() while time.time() < origin + 5: msgs1 = so1.extract_received_messages() msgs2 = so2.extract_received_messages() if len(msgs1) == N + 1 and len(msgs2) == N + 1: break time.sleep(0.1) self.assertEqual(msgs1, msgs2)
def test_quickly_change_observer_twice(self): with make_client(endpoint=endpoint, appkey=appkey) as client: channel = make_channel_name('change_observer_twice') so2 = SubscriptionObserver() so1 = sync_subscribe(client, channel) client.unsubscribe(channel) client.subscribe(channel, SubscriptionMode.ADVANCED, so2) client.unsubscribe(channel) client.subscribe(channel, SubscriptionMode.ADVANCED, None) client.unsubscribe(channel) so3 = sync_subscribe(client, channel) expected_so1_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', 'on_leave_subscribed', 'on_enter_unsubscribing', 'on_leave_unsubscribing', 'on_enter_unsubscribed', 'on_deleted' ] expected_so2_log = ['on_deleted'] expected_so3_log = [ 'on_created', 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed' ] self.assertEqual(so1.log, expected_so1_log) self.assertEqual(so2.log, expected_so2_log) self.assertEqual(so3.log, expected_so3_log)
def test_fast_forward(self): with make_client(endpoint=endpoint, appkey=appkey) as client: sync_subscribe(client, channel) co = ClientObserver() client.observer = co emulate_fast_forward(client, channel) client._queue.join() self.assertEqual([('on_fast_forward', channel)], co.log)
def generic_test_two_clients_with_message_list(self, message_list): with make_client(endpoint=endpoint, appkey=appkey, reconnect_interval=0) as subscriber: with make_client(endpoint=endpoint, appkey=appkey) as publisher: co1 = ClientObserver() subscriber.observer = co1 co2 = ClientObserver() publisher.observer = co2 so = sync_subscribe(subscriber, channel) for msg in message_list: publisher.publish(channel, msg) sync_publish(publisher, channel, 'finalizer') while 'finalizer' !=\ so.last_received_channel_data['messages'][-1]: print(so.last_received_channel_data) last_data = so.wait_for_channel_data() if last_data['messages'][-1] == 'finalizer': break got_messages = so.extract_received_messages() self.assertEqual(got_messages, message_list + ['finalizer'])
def test_unsubscribe_nack_error(self): with make_client( endpoint=endpoint, appkey=appkey) as client: client.unsubscribe(channel) so = sync_subscribe(client, channel) old_received_message =\ client._internal.connection.on_incoming_text_frame client._internal.connection.on_incoming_text_frame =\ lambda *args: None client.unsubscribe(channel) client._queue.join() old_received_message( b'{"action":"rtm/unsubscribe/error","body":{},"id":1}') client._queue.join() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', 'on_leave_subscribed', 'on_enter_unsubscribing', 'on_leave_unsubscribing', 'on_enter_subscribed'] self.assertEqual(so.log, expected_log)
def test_double_unsubscribe_error(self): with make_client( endpoint=endpoint, appkey=appkey) as client: client.unsubscribe(channel) so = sync_subscribe(client, channel) client.unsubscribe(channel) so.wait_deleted() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', 'on_leave_subscribed', 'on_enter_unsubscribing', 'on_leave_unsubscribing', 'on_enter_unsubscribed', 'on_deleted' ] self.assertEqual(so.log, expected_log) self.assertTrue( channel not in client._internal.subscriptions) client.unsubscribe(channel)
def test_concurrent_publishes_in_different_clients(self): mailbox2 = [] self.publish_errors = [] def work(): with make_client(endpoint=endpoint, appkey=appkey) as publisher: for i in six.moves.range(100): try: sync_publish(publisher, channel, message) except Exception as e: self.publish_errors.append(e) with make_client(endpoint=endpoint, appkey=appkey) as subscriber: so = sync_subscribe(subscriber, channel) so.on_subscription_data = lambda data:\ mailbox2.extend(data['messages']) threads = [] for i in range(10): threads.append(threading.Thread(target=work)) for t in threads: t.start() for t in threads: t.join() time.sleep(3) self.assertEqual([], self.publish_errors) # assertEqual seems to choke on 1000 of 'hello's self.assertEqual(len(mailbox2), 1000) self.assertTrue(all((m == 'hello' for m in mailbox2)))
def test_concurrent_publishes_in_one_client(self): with make_client(endpoint=endpoint, appkey=appkey) as client: mailbox = [] self.publish_errors = [] so = sync_subscribe(client, channel) so.on_subscription_data = lambda data:\ mailbox.extend(data['messages']) def work(): for i in six.moves.range(100): try: sync_publish(client, channel, message) except Exception as e: self.publish_errors.append(e) threads = [] for i in range(10): threads.append(threading.Thread(target=work)) for t in threads: t.start() for t in threads: t.join() print('Waiting for the rest of messages') time.sleep(3) print("We've hopefully waited long enough") self.assertEqual([], self.publish_errors) # assertEqual seems to choke on 1000 of 'hello's self.assertEqual(len(mailbox), 1000) self.assertTrue(all((m == 'hello' for m in mailbox)))
def test_channel_error_during_unsubscribing(self): with make_client(endpoint=endpoint, appkey=appkey) as client: so = sync_subscribe(client, channel) client._internal.subscriptions[channel]\ .on_unsubscribe_ok = lambda *args: None client.unsubscribe(channel) client._queue.join() emulate_channel_error(client, channel) so.wait_not_subscribed() sync_publish(client, channel, u'should-be-missed') so.wait_not_subscribed() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', 'on_leave_subscribed', 'on_enter_unsubscribing', 'on_leave_unsubscribing', 'on_enter_unsubscribed', 'on_deleted' ] self.assertEqual(so.log, expected_log)
def test_single_publisher_lots_of_messages(self): with make_client(endpoint=endpoint, appkey=appkey) as pub: with make_client(endpoint=endpoint, appkey=appkey) as sub1: with make_client(endpoint=endpoint, appkey=appkey) as sub2: origin = sync_publish(pub, channel, 'prime') so1 = sync_subscribe(sub1, channel, {'position': origin}) so2 = sync_subscribe(sub2, channel, {'position': origin}) for i in range(1000): pub.publish(channel, i) time.sleep(5) self.assertEqual(so1.extract_received_messages(), so2.extract_received_messages())
def test_fatal_channel_error_recovery_into_unsubscribed(self): with make_client(endpoint=endpoint, appkey=appkey) as client: threads = [] class RecoveringObserver(SubscriptionObserver): def on_enter_failed(this, reason): this.log.append(('on_enter_failed', reason)) import threading t = threading.Thread( target=lambda: client.unsubscribe(channel), name='test_fatal_channel_error_recovery') t.start() threads.append(t) so = RecoveringObserver() sync_subscribe(client, channel, observer=so) emulate_channel_error(client, channel, 'out_of_sync') sync_publish(client, channel, 'should-be-missed') so.wait_not_subscribed() client._queue.join() threads[0].join() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', # note there's no message here 'on_leave_subscribed', ('on_enter_failed', { 'subscription_id': channel, 'error': 'out_of_sync' }), 'on_leave_failed', 'on_enter_unsubscribed', 'on_deleted' ] self.assertEqual(so.log, expected_log)
def test_two_clients_with_deduplication(self): with make_client(endpoint=endpoint, appkey=appkey) as pub: with make_client(endpoint=endpoint, appkey=appkey) as sub: so = sync_subscribe(sub, channel, args={'only': 'value_changes'}) pub.publish(channel, "first") so.wait_for_channel_data() for _ in range(10): pub.publish(channel, "second") so.wait_for_channel_data() pub.publish(channel, "third") so.wait_for_channel_data() got_messages = so.extract_received_messages() self.assertEqual(got_messages, ['first', 'second', 'third'])
def test_automatic_resubscribe(self): client = Client( endpoint=endpoint, appkey=appkey, reconnect_interval=0) client.observer = ClientObserver() channel = make_channel_name('resubscribe') client.start() client.observer.wait_connected('First connect timeout') so = sync_subscribe(client, channel) sync_publish(client, channel, 'first-message') first_channel_data = so.wait_for_channel_data() emulate_websocket_disconnect(client) so.wait_not_subscribed() client.observer.wait_disconnected() client.observer.wait_connected('Second connect timeout') so.wait_subscribed('Second subscribe timeout') sync_publish(client, channel, 'second-message') second_channel_data = so.wait_for_channel_data() client.unsubscribe(channel) so.wait_not_subscribed() client.stop() client.dispose() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', ('data', first_channel_data), # point of disconnect 'on_leave_subscribed', 'on_enter_unsubscribed', # point of reconnect 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', ('data', second_channel_data), 'on_leave_subscribed', 'on_enter_unsubscribing', 'on_leave_unsubscribing', 'on_enter_unsubscribed', 'on_deleted'] self.assertEqual(so.log, expected_log)
def test_two_clients_with_best_effort_delivery(self): with make_client(endpoint=endpoint, appkey=appkey, reconnect_interval=0) as subscriber: with make_client(endpoint=endpoint, appkey=appkey) as publisher: co1 = ClientObserver() subscriber.observer = co1 co2 = ClientObserver() publisher.observer = co2 try: so = sync_subscribe(subscriber, channel, mode=SubscriptionMode.RELIABLE) sync_publish(publisher, channel, 'first-message') so.wait_for_channel_data('First receive timeout') emulate_websocket_disconnect(subscriber) so.wait_not_subscribed() # send a message while subscriber is disconnected sync_publish(publisher, channel, 'second-message') so.wait_subscribed('Second subscribe timeout') so.wait_for_channel_data('Second receive timeout') # send a message after subscribed reconnected publisher.publish(channel, 'third-message') so.wait_for_channel_data('Third receive timeout') expected_messages =\ ['first-message', 'second-message', 'third-message'] got_messages = [] for log_entry in so.log: if log_entry[0] == 'data': got_messages += log_entry[1]['messages'] self.assertEqual(got_messages, expected_messages) except Exception: print('Subscriber log: {0}'.format(co1.log)) print('Publisher log: {0}'.format(co2.log)) print('Subscription log: {0}'.format(so.log)) raise
def test_reauth(self): client = Client(endpoint=endpoint, appkey=appkey, reconnect_interval=0) auth_delegate = auth.RoleSecretAuthDelegate(role, secret) auth_event = threading.Event() mailbox = [] co = ClientObserver() client.observer = co client.start() co.wait_connected() def auth_callback(auth_result): if type(auth_result) == auth.Done: mailbox.append('Auth success') auth_event.set() else: mailbox.append('Auth failure: {0}'.format( auth_result.message)) auth_event.set() client.authenticate(auth_delegate, auth_callback) if not auth_event.wait(30): raise RuntimeError("Auth timeout") self.assertEqual(mailbox, ['Auth success']) so = sync_subscribe(client, restricted_channel) message1 = make_channel_name('before disconnect') sync_publish(client, restricted_channel, message1) first_data = so.wait_for_channel_data() self.assertTrue(message1 in first_data['messages']) emulate_websocket_disconnect(client) co.wait_disconnected() co.wait_connected() message2 = make_channel_name('after reconnect') sync_publish(client, restricted_channel, message2) second_data = so.wait_for_channel_data() self.assertTrue(message2 in second_data['messages']) client.stop() client.dispose()
def test_all_types(self): messages = basic_messages + dict_messages + list_messages with make_client(endpoint=endpoint, appkey=appkey) as client: so = sync_subscribe(client, channel) for msg in messages: sync_publish(client, channel, msg) so.wait_for_channel_data() got_messages = [] for x in so.log: try: got_messages += x[1]['messages'] except: pass self.assertEqual(messages, got_messages)
def test_non_fatal_channel_error(self): with make_client(endpoint=endpoint, appkey=appkey) as client: so = sync_subscribe(client, channel) emulate_channel_error(client, channel) client._queue.join() so.wait_subscribed() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', 'on_leave_subscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', ] self.assertEqual(so.log, expected_log)
def test_double_subscribe(self): with make_client(endpoint=endpoint, appkey=appkey, reconnect_interval=1) as client: client.observer = ClientObserver() so = sync_subscribe(client, channel) client.subscribe(channel, SubscriptionMode.ADVANCED, subscription_observer=so) sync_publish(client, channel, 'message') data = so.wait_for_channel_data() expected_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', ('data', data) ] self.assertEqual(so.log, expected_log)
def test_two_clients(self): with make_client(endpoint=endpoint, appkey=appkey, reconnect_interval=0) as subscriber: with make_client(endpoint=endpoint, appkey=appkey) as publisher: co1 = ClientObserver() subscriber.observer = co1 co2 = ClientObserver() publisher.observer = co2 try: so = sync_subscribe(subscriber, channel) sync_publish(publisher, channel, 'first-message') so.wait_for_channel_data('First receive timeout') emulate_websocket_disconnect(subscriber) so.wait_not_subscribed() # send a message while subscriber is disconnected sync_publish(publisher, channel, 'second-message') so.wait_subscribed('Second subscribe timeout') so.wait_for_channel_data('Second receive timeout') # send a message after subscribed reconnected publisher.publish(channel, 'third-message') so.wait_for_channel_data('Third receive timeout') expected_messages =\ ['first-message', 'second-message', 'third-message'] got_messages = so.extract_received_messages() self.assertEqual(got_messages, expected_messages) except Exception: print('Subscriber log: {0}'.format(co1.log)) print('Publisher log: {0}'.format(co2.log)) print('Subscription log: {0}'.format(so.log)) raise
def test_change_observer_from_none(self): with make_client(endpoint=endpoint, appkey=appkey) as client: channel = make_channel_name('change_observer') client.subscribe(channel, SubscriptionMode.ADVANCED, subscription_observer=None) client.unsubscribe(channel) so2 = sync_subscribe(client, channel) self.maxDiff = None expected_so2_log = [ 'on_created', 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed' ] self.assertEqual(so2.log, expected_so2_log) client.stop()
def test_resubscribe_after_manual_reconnect(self): with make_client(endpoint, appkey) as client: channel = make_channel_name('manual_reconnect') so = sync_subscribe(client, channel) sync_publish(client, channel, 'first-message') m = so.wait_for_channel_data() self.assertEqual(m['messages'], ['first-message']) client.observer = ClientObserver() client.stop() client.observer.wait_disconnected() client.start() client._queue.join() client.observer.wait_connected() sync_publish(client, channel, 'second-message') m = so.wait_for_channel_data() self.assertEqual(m['messages'], ['second-message'])
def test_change_observer(self): with make_client(endpoint=endpoint, appkey=appkey) as client: co = ClientObserver() client.observer = co channel = make_channel_name('change_observer') so1 = sync_subscribe(client, channel) client.unsubscribe(channel) so2 = SubscriptionObserver() client.subscribe(channel, SubscriptionMode.ADVANCED, subscription_observer=so2) so2.wait_subscribed() client.stop() co.wait_disconnected() self.maxDiff = None expected_so1_log = [ 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed', 'on_leave_subscribed', 'on_enter_unsubscribing', 'on_leave_unsubscribing', 'on_enter_unsubscribed', 'on_deleted' ] expected_so2_log = [ 'on_created', 'on_leave_unsubscribed', 'on_enter_subscribing', 'on_leave_subscribing', 'on_enter_subscribed' ] self.assertEqual(so1.log, expected_so1_log) self.assertEqual(so2.log, expected_so2_log)
def test_repeat_second_message(self): client = Client(endpoint=endpoint, appkey=appkey, reconnect_interval=1) client.observer = ClientObserver() channel = make_channel_name('resubscribe') client.start() client.observer.wait_connected() so = sync_subscribe(client, channel) sync_publish(client, channel, 'first-message') first_channel_data = so.wait_for_channel_data() sync_publish(client, channel, 'second-message') second_channel_data = so.wait_for_channel_data() client.unsubscribe(channel) client.subscribe(channel, SubscriptionMode.ADVANCED, so, args={'position': first_channel_data['position']}) self.assertEqual(second_channel_data['messages'], so.wait_for_channel_data()['messages']) client.unsubscribe(channel) so.wait_not_subscribed() client.stop() client.dispose()