diff --git a/README.md b/README.md index 01c1bd1..cde91d6 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,23 @@ $ gpg-group-chat --server $ gpg-group-chat --client ``` +## Basic usage flow + +1. Server starts with gpg-group-chat --server at 192.168.0.1 +2. The client1 starts with gpg-group-chat --client --server-ip=192.168.0.1 --public-key=public-key1 +3. The client1 connect to the server and the it stores the public key for client1 +4. The server send the current list of keys for client1 (contains public key1) +5. The client2 starts with gpg-group-chat --client --server-ip=192.168.0.1 --public-key=public-key2 +6. The client2 connect to the server and the it stores the public key for client2 +7. The server send the current list of keys for client1 and client2 (contains public key1 and public key2) +8. The client3 starts with gpg-group-chat --client --server-ip=192.168.0.1 --public-key=public-key3 +9. The client3 connect to the server and the it stores the public key for client3 +10. The server send the current list of keys for client1, client2 and client3 (contains public key1, public key2 and public key3) +11. The client2 disconnects from the server +12. the server delete the public key from client 2 from the storage +13. The server send the current list of keys for client1 and client3 (contains public key1 and public key3) +... + # Issue board [Waffle.io/bahackers/gpg-group-chat](https://waffle.io/bahackers/gpg-group-chat) diff --git a/gpg_group_chat/client/client.py b/gpg_group_chat/client/client.py index f8867fd..4ecf3b1 100644 --- a/gpg_group_chat/client/client.py +++ b/gpg_group_chat/client/client.py @@ -1,4 +1,54 @@ +import select +import socket +import sys + + class Client(): - def start(self, port, host): - print('Hello friend!') + def __init__(self): + self._socket = None + self._working = True + + def start(self, server_port, server_ip): + print('Hello friend') + dest = (server_ip, server_port) + + try: + self._socket = socket.create_connection(dest) + except ConnectionRefusedError as err: + print('Connection refused') + sys.exit(1) + + self._handle_messages() + + def _handle_messages(self): + input_list = [sys.stdin, self._socket] + self._prompt() + while self._working: + ready_to_read, _, _ = select.select(input_list, [], []) + self._handle_input_channel(ready_to_read) + + def _handle_input_channel(self, ready_to_read): + for input_channel in ready_to_read: + if input_channel is self._socket: + self._receive_data_from_server() + else: + self._send_message_to_server() + + def _receive_data_from_server(self): + data = self._socket.recv(4096) + if not data: + print('\nDisconnected from chat server') + sys.exit(0) + else: + print(data.decode('utf-8')) + self._prompt() + + def _send_message_to_server(self): + msg = '[*] %s' % input() + self._socket.send(msg.encode('utf-8')) + self._prompt() + + def _prompt(self): + print('[Me] ', end='') + sys.stdout.flush() diff --git a/gpg_group_chat/server/server.py b/gpg_group_chat/server/server.py index c1c861e..abc8c72 100644 --- a/gpg_group_chat/server/server.py +++ b/gpg_group_chat/server/server.py @@ -10,7 +10,7 @@ def __init__(self): self._working = False def start(self, port, target): - print('Server listening on %s:%d' % (target, port)) + print('Server is not done yet!') self._working = True try: self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -49,3 +49,4 @@ def _client_handler(client_socket, thread_event): break else: print('[INFO] Received: %s' % data) + client_socket.sendall(b'\rACK{}\n') diff --git a/gpg_group_chat/utils.py b/gpg_group_chat/utils.py new file mode 100644 index 0000000..7da6e4a --- /dev/null +++ b/gpg_group_chat/utils.py @@ -0,0 +1,2 @@ +def show_help(): + print('Helper will be available soon!') diff --git a/test/client/client_test.py b/test/client/client_test.py index 5571151..820f181 100644 --- a/test/client/client_test.py +++ b/test/client/client_test.py @@ -1,5 +1,88 @@ -import gpg_group_chat.client +from gpg_group_chat.client.client import Client +from socket import socket as SocketType +from unittest import TestCase +from unittest.mock import Mock +from unittest.mock import patch +import sys -def test_start_gpg_groupd_chat(): - assert True +class ClientTest(TestCase): + + def setUp(self): + self.create_connection = patch('socket.create_connection').start() + self.exit = patch('sys.exit').start() + self.readline = patch('sys.stdin.readline').start() + self.select = patch('select.select').start() + self.print_stdout = patch('sys.stdout.write').start() + + self.socket = Mock(SocketType) + self.create_connection.return_value = self.socket + + self.client = Client() + + def test_connect_to_a_server(self): + def _handle_messages(): + pass + + self.client._handle_messages = _handle_messages + self.client.start(9999, '127.0.0.1') + + self.create_connection.assert_called_once_with(('127.0.0.1', 9999)) + + def test_to_connect_to_unexist_server(self): + def side_effect(dest): + raise ConnectionRefusedError(111, 'connection refused') + + def _handle_messages(): + pass + + self.create_connection.side_effect = side_effect + self.client._handle_messages = _handle_messages + + self.client.start(9999, 'server.runkown') + + self.exit.assert_called_once_with(1) + + def test_send_a_message_to_the_server(self): + def side_effect(msg): + self.client._working = False + + self.socket.send.side_effect = side_effect + self.readline.return_value = 'message' + self.select.return_value = ([sys.stdin], None, None) + + self.client.start(9999, '127.0.0.1') + + self.socket.send.assert_called_once_with(b'[*] message') + + def test_receive_a_message_from_the_server(self): + def side_effect(size): + self.client._working = False + return b'message from server' + + self.socket.recv.side_effect = side_effect + self.select.return_value = ([self.socket], None, None) + + self.client.start(9999, '127.0.0.1') + + self.print_stdout.assert_any_call('message from server') + + def test_exit_when_disconnect_with_the_server(self): + def side_effect(size): + self.client._working = False + return None + + self.socket.recv.side_effect = side_effect + self.select.return_value = ([self.socket], None, None) + + self.client.start(9999, '127.0.0.1') + + self.print_stdout.assert_any_call('\nDisconnected from chat server') + self.exit.assert_called_once_with(0) + + def test_the_prompt_meyhod_does_not_print_with_break_line_char(self): + def side_effect(txt): + assert not txt.endswith('\n') + + self.print_stdout.side_effect = side_effect + self.client._prompt()