observer.send_message('counted NO!', channel) votes[event.nickname] = -1 print('someone voted no') observer = Observer(nick, votebot_token.token) observer.subscribe(handle_event) observer.send_message('Vote !yes or !no. You have 60 seconds!', channel) observer.start() observer.join_channel(channel) time.sleep(30) observer.send_message('... 30 seconds left ...', channel) time.sleep(30) observer.unsubscribe(handle_event) observer.send_message('Voting is over!', channel) time.sleep(2) tally = sum(votes.values()) if tally > 0: observer.send_message('The yeas have it!', channel) elif tally < 0: observer.send_message('The nays have it!', channel) else: observer.send_message('Its a draw!', channel)
class TestBasicFunctionality(unittest.TestCase): def setUp(self): self.observer = Observer('nickname', 'password123') self.observer._inbound_poll_interval = 0 self.observer._outbound_send_interval = 0 self._patcher = mock.patch('socket.socket', spec=True) self.mock_socket = self._patcher.start() self.mock_socket.return_value.connect.return_value = None self.mock_socket.return_value.recv.side_effect = [''.encode('utf-8')] def tearDown(self): if self.observer: self.observer.stop(force_stop=True) self.observer = None self._patcher.stop() def test_connect(self): self.mock_socket.return_value.recv.side_effect = [ SUCCESSFUL_LOGIN_MESSAGE ] self.observer.start() self.assertEqual(self.observer._nickname, 'nickname', 'Nickname should be set') self.assertEqual(self.observer._password, 'password123', 'Password should be set') self.assertIsNotNone(self.observer._inbound_worker_thread, 'Inbound worker thread should be running') self.assertIsNotNone(self.observer._outbound_worker_thread, 'Outbound worker thread should be running') self.assertTrue(self.observer._is_running, 'The observer should be running') self.observer.stop(force_stop=True) self.assertIsNone(self.observer._inbound_worker_thread, 'Inbound worker thread should not be running') self.assertIsNone(self.observer._outbound_worker_thread, 'Outbound worker thread should not be running') self.assertFalse(self.observer._is_running, 'The observer should be stopped') def test_failed_connect(self): self.mock_socket.return_value.recv.side_effect = [ UNSUCCESSFUL_LOGIN_MESSAGE ] with self.assertRaises(RuntimeError): self.observer.start() def test_server_ping(self): self.mock_socket.return_value.recv.side_effect = [ SUCCESSFUL_LOGIN_MESSAGE, SERVER_PING_MESSAGE ] self.observer.start() self.assertEqual(self.mock_socket.return_value.send.call_args[0][0], CLIENT_PONG_MESSAGE, 'Observer should respond with PONG response') def test_subscribe_unsubscribe(self): def handler(event): pass self.assertEqual(len(self.observer._subscribers), 0, 'There should be no subscribers') self.observer.subscribe(handler) self.assertEqual(len(self.observer._subscribers), 1, 'There should be a single subscriber') self.observer.unsubscribe(handler) self.assertEqual(len(self.observer._subscribers), 0, 'The subscriber should be removed') def test_receive_privmsg(self): self.mock_socket.return_value.recv.side_effect = [ SERVER_PRIVMSG_MESSAGE ] self.callback_invoked = False def verify_event(event): self.callback_invoked = True self.assertEqual(event.type, EventType.MESSAGE, "Type should be " + EventType.MESSAGE) self.assertEqual(event.nickname, 'nickname', "Nickname should be 'nickname'") self.assertEqual(event.message, 'message', "Message should be 'message'") self.assertEqual(event.channel, 'channel', "Channel should be 'channel'") self.observer.subscribe(verify_event) self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked') def test_send_privmsg(self): self.observer.start() self.observer.send_message('message', 'channel') self.observer.stop() self.assertEqual(self.mock_socket.return_value.send.call_args[0][0], CLIENT_PRIVMSG_MESSAGE, 'Observer should respond with PRIVMSG response') def test_receive_join(self): self.mock_socket.return_value.recv.side_effect = [SERVER_JOIN_MESSAGE] self.callback_invoked = False def verify_event(event): self.callback_invoked = True self.assertEqual(event.type, EventType.JOIN, "Type should be " + EventType.JOIN) self.assertEqual(event.nickname, 'nickname', "Nickname should be 'nickname'") self.assertEqual(event.channel, 'channel', "Channel should be 'channel'") self.observer.subscribe(verify_event) self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked') def test_send_join(self): self.observer.start() self.observer.join_channel('channel') self.observer.stop() self.assertEqual(self.mock_socket.return_value.send.call_args[0][0], CLIENT_JOIN_MESSAGE, 'Observer should respond with JOIN response') def test_receive_part(self): self.mock_socket.return_value.recv.side_effect = [SERVER_PART_MESSAGE] self.callback_invoked = False def verify_event(event): self.callback_invoked = True self.assertEqual(event.type, EventType.LEAVE, "Type should be " + EventType.LEAVE) self.assertEqual(event.nickname, 'nickname', "Nickname should be 'nickname'") self.assertEqual(event.channel, 'channel', "Channel should be 'channel'") self.observer.subscribe(verify_event) self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked') def test_send_part(self): self.observer.start() self.observer.leave_channel('channel') self.observer.stop() self.assertEqual(self.mock_socket.return_value.send.call_args[0][0], CLIENT_PART_MESSAGE, 'Observer should respond with PART response') def test_receive_whisper(self): self.mock_socket.return_value.recv.side_effect = [ SERVER_WHISPER_MESSAGE ] self.callback_invoked = False def verify_event(event): self.callback_invoked = True self.assertEqual(event.type, EventType.WHISPER, "Type should be " + EventType.WHISPER) self.assertEqual(event.nickname, 'nickname', "Nickname should be 'nickname'") self.assertEqual(event.message, 'message', "Message should be 'message'") self.observer.subscribe(verify_event) self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked') def test_send_whisper(self): self.observer.start() self.observer.send_whisper('nickname', 'message') self.observer.stop() self.assertEqual(self.mock_socket.return_value.send.call_args[0][0], CLIENT_WHISPER_MESSAGE, 'Observer should respond with PRIVMSG response') def test_receive_userstate_tags(self): self.mock_socket.return_value.recv.side_effect = [ SERVER_USERSTATE_TAGS_MESSAGE ] self.callback_invoked = False def verify_event(event): self.callback_invoked = True expected_tags = { 'display-name': 'nickname', 'emote-sets': '0', 'mod': '1', 'color': '', 'badges': 'moderator/1', 'user-type': 'mod', 'subscriber': '0' } self.assertEqual(event.type, EventType.USERSTATE, "Type should be " + EventType.USERSTATE) self.assertEqual(event.nickname, 'tmi.twitch.tv', "Nickname should be 'tmi.twitch.tv'") self.assertEqual(event.tags, expected_tags, 'Event tags should be equal') self.observer.subscribe(verify_event) self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked') def test_truncated_messages(self): # Bit of a hack. Because the main thread handles consuming the first # server response, we need to first supply a dummy message that gets # ignored. self.mock_socket.return_value.recv.side_effect = [ ''.encode('utf-8'), SERVER_PRIVMSG_MESSAGE[:18], SERVER_PRIVMSG_MESSAGE[18:] ] self.callback_invoked = False def verify_event(event): self.callback_invoked = True self.assertEqual(event.type, EventType.MESSAGE, "Type should be " + EventType.MESSAGE) self.assertEqual(event.nickname, 'nickname', "Nickname should be 'nickname'") self.assertEqual(event.message, 'message', "Message should be 'message'") self.assertEqual(event.channel, 'channel', "Channel should be 'channel'") self.observer.subscribe(verify_event) self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked') def test_context_manager_force_stop(self): with Observer('nickname', 'password123') as observer: observer.stop(force_stop=True) self.assertTrue( len(observer._outbound_event_queue) == 0, 'Outbound event queue should be empty') self.assertTrue( len(observer._inbound_event_queue) == 0, 'Inbound event queue should be empty') def test_on_event_decorator(self): self.mock_socket.return_value.recv.side_effect = [ SERVER_PRIVMSG_MESSAGE ] self.callback_invoked = False self.assertEqual(len(self.observer._subscribers), 0, 'There should be no subscribers') @self.observer.on_event(EventType.MESSAGE) def verify_event(event): self.callback_invoked = True self.assertEqual(event.type, EventType.MESSAGE, "Type should be " + EventType.MESSAGE) self.assertEqual(len(self.observer._subscribers), 1, 'There should be a single subscriber') self.observer.start() self.assertTrue(self.callback_invoked, 'Subscriber callback should be invoked')