class TestSendFile(TFCTestCase): def setUp(self) -> None: """Pre-test actions.""" self.unit_test_dir = cd_unit_test() self.settings = Settings() self.queues = gen_queue_dict() self.window = TxWindow() self.onion_service = OnionService() self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie']) self.args = self.settings, self.queues, self.window def tearDown(self) -> None: """Post-test actions.""" cleanup(self.unit_test_dir) tear_queues(self.queues) def test_traffic_masking_raises_se(self) -> None: self.settings.traffic_masking = True self.assert_se("Error: Command is disabled during traffic masking.", send_file, "testfile.txt", *self.args) def test_missing_file_raises_se(self) -> None: self.assert_se("Error: File not found.", send_file, "testfile.txt", *self.args) def test_empty_file_raises_se(self) -> None: # Setup open('testfile.txt', 'wb+').close() # Test self.assert_se("Error: Target file is empty.", send_file, "testfile.txt", *self.args) @mock.patch('time.sleep', return_value=None) def test_file_transmission_to_contact(self, _: Any) -> None: # Setup self.window.window_contacts = [self.contact_list.get_contact_by_address_or_nick('Alice')] self.window.type_print = 'contact' input_data = os.urandom(5) with open('testfile.txt', 'wb+') as f: f.write(input_data) # Test self.assertIsNone(send_file("testfile.txt", *self.args)) self.assertEqual(self.queues[MESSAGE_PACKET_QUEUE].qsize(), 1) self.assertEqual(self.queues[RELAY_PACKET_QUEUE].qsize(), 1) @mock.patch('time.sleep', return_value=None) def test_file_transmission_to_group(self, _: Any) -> None: # Setup self.window.window_contacts = [self.contact_list.get_contact_by_address_or_nick('Alice'), self.contact_list.get_contact_by_address_or_nick('Bob')] self.window.type_print = 'group' input_data = os.urandom(5) with open('testfile.txt', 'wb+') as f: f.write(input_data) self.assertIsNone(send_file("testfile.txt", *self.args)) self.assertEqual(self.queues[MESSAGE_PACKET_QUEUE].qsize(), 2) self.assertEqual(self.queues[RELAY_PACKET_QUEUE].qsize(), 1)
class TestChangeNick(TFCTestCase): def setUp(self) -> None: """Pre-test actions.""" self.contact_list = ContactList(nicks=['Alice']) self.group_list = GroupList() self.settings = Settings() self.queues = gen_queue_dict() self.args = self.contact_list, self.group_list, self.settings, self.queues def tearDown(self) -> None: """Post-test actions.""" tear_queues(self.queues) def test_missing_nick_raises_soft_error(self) -> None: self.assert_se("Error: No nick specified.", change_nick, UserInput("nick "), TxWindow(type=WIN_TYPE_CONTACT), *self.args) def test_invalid_nick_raises_soft_error(self) -> None: # Setup window = TxWindow(type=WIN_TYPE_CONTACT, contact=create_contact('Bob')) # Test self.assert_se("Error: Nick must be printable.", change_nick, UserInput("nick Alice\x01"), window, *self.args) def test_no_contact_raises_soft_error(self) -> None: # Setup window = TxWindow(type=WIN_TYPE_CONTACT, contact=create_contact('Bob')) window.contact = None # Test self.assert_se("Error: Window does not have contact.", change_nick, UserInput("nick Alice\x01"), window, *self.args) def test_successful_nick_change(self) -> None: # Setup window = TxWindow(name='Alice', type=WIN_TYPE_CONTACT, contact=self.contact_list.get_contact_by_address_or_nick('Alice')) # Test self.assertIsNone(change_nick(UserInput("nick Alice_"), window, *self.args)) self.assertEqual(self.contact_list.get_contact_by_pub_key(nick_to_pub_key('Alice')).nick, 'Alice_') @mock.patch('time.sleep', return_value=None) def test_successful_group_nick_change(self, _: Any) -> None: # Setup group = create_group('test_group') user_input = UserInput("nick group2") window = TxWindow(name ='test_group', type =WIN_TYPE_GROUP, group=group, uid =group.group_id) # Test self.assert_se("Renamed group 'test_group' to 'group2'.", change_nick, user_input, window, *self.args) self.assertEqual(window.group.name, 'group2')
class TestPacketList(unittest.TestCase): def setUp(self): self.contact_list = ContactList(nicks=['Alice', 'Bob']) self.settings = Settings() self.onion_pub_key = nick_to_pub_key('Alice') packet = Packet(self.onion_pub_key, ORIGIN_CONTACT_HEADER, MESSAGE, self.contact_list.get_contact_by_address_or_nick('Alice'), self.settings) self.packet_list = PacketList(self.settings, self.contact_list) self.packet_list.packets = [packet] def test_packet_list_iterates_over_contact_objects(self): for p in self.packet_list: self.assertIsInstance(p, Packet) def test_len_returns_number_of_contacts(self): self.assertEqual(len(self.packet_list), 1) def test_has_packet(self): self.assertTrue(self.packet_list.has_packet(self.onion_pub_key, ORIGIN_CONTACT_HEADER, MESSAGE)) self.assertFalse(self.packet_list.has_packet(self.onion_pub_key, ORIGIN_USER_HEADER, MESSAGE)) def test_get_packet(self): packet = self.packet_list.get_packet(self.onion_pub_key, ORIGIN_CONTACT_HEADER, MESSAGE) self.assertEqual(packet.onion_pub_key, self.onion_pub_key) self.assertEqual(packet.origin, ORIGIN_CONTACT_HEADER) self.assertEqual(packet.type, MESSAGE) packet = self.packet_list.get_packet(self.onion_pub_key, ORIGIN_CONTACT_HEADER, MESSAGE) self.assertEqual(packet.onion_pub_key, self.onion_pub_key) self.assertEqual(packet.origin, ORIGIN_CONTACT_HEADER) self.assertEqual(packet.type, MESSAGE)
def test_loop(self): # Setup queues = gen_queue_dict() settings = Settings(traffic_masking=True, tm_static_delay=0.001, tm_random_delay=0.001) gateway = Gateway() key_list = KeyList(nicks=['Alice', LOCAL_ID]) window = TxWindow(log_messages=True) contact_list = ContactList(nicks=['Alice', LOCAL_ID]) window.contact_list = contact_list window.window_contacts = [contact_list.get_contact_by_address_or_nick('Alice')] user_input = UserInput(plaintext='test') def queue_delayer(): """Place packets to queue after delay.""" time.sleep(0.01) queues[WINDOW_SELECT_QUEUE].put(window.window_contacts) time.sleep(0.01) queue_command(b'test', settings, queues) # 1 queue_message(user_input, window, settings, queues) # 2 queue_message(user_input, window, settings, queues) # 3 queue_command(b'test', settings, queues) # 4 queues[TM_NOISE_COMMAND_QUEUE].put((C_N_HEADER + bytes(PADDING_LENGTH))) # 5 queue_to_nc(UNENCRYPTED_DATAGRAM_HEADER + UNENCRYPTED_EXIT_COMMAND, queues[RELAY_PACKET_QUEUE]) # 6 queue_to_nc(UNENCRYPTED_DATAGRAM_HEADER + UNENCRYPTED_WIPE_COMMAND, queues[RELAY_PACKET_QUEUE]) # 7 queues[SENDER_MODE_QUEUE].put(settings) # Test threading.Thread(target=queue_delayer).start() self.assertIsInstance(traffic_masking_loop(queues, settings, gateway, key_list), Settings) self.assertEqual(len(gateway.packets), 7) # Teardown tear_queues(queues)
class TestAddNewContact(TFCTestCase): def setUp(self) -> None: """Pre-test actions.""" self.contact_list = ContactList(nicks=[LOCAL_ID]) self.group_list = GroupList() self.settings = Settings(disable_gui_dialog=True) self.queues = gen_queue_dict() self.onion_service = OnionService() self.args = self.contact_list, self.group_list, self.settings, self.queues, self.onion_service def tearDown(self) -> None: """Post-test actions.""" with ignored(OSError): os.remove(f'v4dkh.psk - Give to hpcra') tear_queues(self.queues) def test_adding_new_contact_during_traffic_masking_raises_soft_error(self) -> None: # Setup self.settings.traffic_masking = True # Test self.assert_se("Error: Command is disabled during traffic masking.", add_new_contact, *self.args) def test_contact_list_full_raises_soft_error(self) -> None: # Setup contact_list = ContactList(nicks=[str(n) for n in range(50)]) self.contact_list.contacts = contact_list.contacts # Test self.assert_se("Error: TFC settings only allow 50 accounts.", add_new_contact, *self.args) @mock.patch('builtins.input', side_effect=[nick_to_onion_address("Bob"), 'Bob', '', VALID_ECDHE_PUB_KEY, 'Yes', blake2b(nick_to_pub_key('Bob'), digest_size=CONFIRM_CODE_LENGTH).hex()]) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) @mock.patch('time.sleep', return_value=None) def test_default_nick_ecdhe(self, *_: Any) -> None: self.assertIsNone(add_new_contact(*self.args)) contact = self.contact_list.get_contact_by_address_or_nick("Bob") self.assertEqual(contact.nick, 'Bob') self.assertNotEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH)) @mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_MEMORY_COST', 200) @mock.patch('src.common.statics.MIN_KEY_DERIVATION_TIME', 0.1) @mock.patch('src.common.statics.MAX_KEY_DERIVATION_TIME', 1.0) @mock.patch('builtins.input', side_effect=[nick_to_onion_address("Alice"), 'Alice_', 'psk', '.', '', 'ff', 'fc']) @mock.patch('getpass.getpass', return_value='test_password') @mock.patch('time.sleep', return_value=None) def test_standard_nick_psk_kex(self, *_: Any) -> None: self.onion_service.account = nick_to_onion_address('Bob').encode() self.assertIsNone(add_new_contact(*self.args)) contact = self.contact_list.get_contact_by_pub_key(nick_to_pub_key("Alice")) self.assertEqual(contact.nick, 'Alice_') self.assertEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH)) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=KeyboardInterrupt) def test_keyboard_interrupt_raises_soft_error(self, *_: Any) -> None: self.assert_se('Contact creation aborted.', add_new_contact, *self.args)
class TestNewFile(TFCTestCase): def setUp(self) -> None: """Pre-test actions.""" self.unit_test_dir = cd_unit_test() self.ts = datetime.now() self.packet = b'' self.file_keys = dict() self.file_buf = dict() self.contact_list = ContactList(nicks=['Alice']) self.window_list = WindowList() self.file_key = SYMMETRIC_KEY_LENGTH*b'a' self.settings = Settings() self.compressed = zlib.compress(str_to_bytes("test_file.txt") + b'file_data', level=COMPRESSION_LEVEL) self.args = self.file_keys, self.file_buf, self.contact_list, self.window_list, self.settings def tearDown(self) -> None: """Post-test actions.""" cleanup(self.unit_test_dir) def test_unknown_account_raises_soft_error(self) -> None: # Setup file_ct = encrypt_and_sign(self.compressed, self.file_key) packet = nick_to_pub_key('Bob') + ORIGIN_CONTACT_HEADER + file_ct # Test self.assert_se("File from an unknown account.", new_file, self.ts, packet, *self.args) def test_disabled_file_reception_raises_soft_error(self) -> None: # Setup file_ct = encrypt_and_sign(self.compressed, self.file_key) packet = nick_to_pub_key('Alice') + ORIGIN_CONTACT_HEADER + file_ct self.contact_list.get_contact_by_address_or_nick('Alice').file_reception = False # Test self.assert_se("Alert! Discarded file from Alice as file reception for them is disabled.", new_file, self.ts, packet, *self.args) def test_valid_file_without_key_is_cached(self) -> None: # Setup file_ct = encrypt_and_sign(self.compressed, self.file_key) file_hash = blake2b(file_ct) packet = nick_to_pub_key('Alice') + ORIGIN_CONTACT_HEADER + file_ct # Test self.assertIsNone(new_file(self.ts, packet, *self.args)) self.assertEqual(self.file_buf[nick_to_pub_key('Alice') + file_hash], (self.ts, file_ct)) @mock.patch('time.sleep', return_value=None) def test_valid_file_with_key_is_processed(self, _: Any) -> None: # Setup file_ct = encrypt_and_sign(self.compressed, self.file_key) file_hash = blake2b(file_ct) packet = nick_to_pub_key('Alice') + ORIGIN_CONTACT_HEADER + file_ct self.file_keys = {(nick_to_pub_key('Alice') + file_hash): self.file_key} self.args = self.file_keys, self.file_buf, self.contact_list, self.window_list, self.settings # Test self.assertIsNone(new_file(self.ts, packet, *self.args))
def test_no_contact_found_on_transmitter(self, *_: Any) -> None: # Setup user_input = UserInput(f'rm {nick_to_onion_address("Charlie")}') contact_list = ContactList(nicks=['Bob']) window = TxWindow(window_contact=[contact_list.get_contact_by_address_or_nick('Bob')], type=WIN_TYPE_GROUP) # Test self.assertIsNone(remove_contact(user_input, window, *self.args)) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1) self.assertEqual(self.queues[RELAY_PACKET_QUEUE].qsize(), 1) command_packet = self.queues[COMMAND_PACKET_QUEUE].get() self.assertIsInstance(command_packet, bytes)
def test_successful_removal(self): # Setup contact_list = ContactList(nicks=['Alice', 'Bob']) contact = contact_list.get_contact_by_address_or_nick("Bob") group_list = GroupList(groups=['test_group', 'test_group2']) key_list = KeyList(nicks=['Alice', 'Bob']) self.window_list.windows = [RxWindow(type=WIN_TYPE_GROUP)] # Test self.assert_fr("No log database available.", contact_rem, *self.args, contact_list, group_list, key_list, self.settings, self.master_key) self.assertFalse(contact_list.has_pub_key(nick_to_pub_key("Bob"))) self.assertFalse(key_list.has_keyset(nick_to_pub_key("Bob"))) for g in group_list: self.assertFalse(contact in g.members)
class TestVerify(TFCTestCase): def setUp(self): """Pre-test actions.""" self.window = TxWindow(uid=nick_to_pub_key("Alice"), name='Alice', window_contacts=[create_contact('test_group')], log_messages=True, type=WIN_TYPE_CONTACT) self.contact_list = ContactList(nicks=['Alice']) self.contact = self.contact_list.get_contact_by_address_or_nick( 'Alice') self.window.contact = self.contact self.args = self.window, self.contact_list def test_active_group_raises_fr(self): self.window.type = WIN_TYPE_GROUP self.assert_fr("Error: A group is selected.", verify, *self.args) def test_psk_raises_fr(self): self.contact.kex_status = KEX_STATUS_NO_RX_PSK self.assert_fr("Pre-shared keys have no fingerprints.", verify, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['No', 'Yes']) def test_fingerprint_check(self, *_): self.contact.kex_status = KEX_STATUS_VERIFIED self.assertIsNone(verify(*self.args)) self.assertEqual(self.contact.kex_status, KEX_STATUS_UNVERIFIED) self.assertIsNone(verify(*self.args)) self.assertEqual(self.contact.kex_status, KEX_STATUS_VERIFIED) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=KeyboardInterrupt) def test_keyboard_interrupt_raises_fr(self, *_): self.contact.kex_status = KEX_STATUS_VERIFIED self.assert_fr("Fingerprint verification aborted.", verify, *self.args) self.assertEqual(self.contact.kex_status, KEX_STATUS_VERIFIED)
class TestChContactSetting(TFCTestCase): def setUp(self): self.ts = datetime.fromtimestamp(1502750000) self.contact_list = ContactList(nicks=['Alice', 'Bob']) self.group_list = GroupList(groups=['test_group', 'test_group2']) self.window_list = WindowList(contact_list=self.contact_list, group_list=self.group_list) self.args = self.ts, self.window_list, self.contact_list, self.group_list def test_invalid_window_raises_fr(self): # Setup cmd_data = ENABLE + nick_to_pub_key("Bob") header = CH_LOGGING self.contact_list = ContactList(nicks=['Alice']) self.window_list = WindowList(contact_list=self.contact_list, group_list=self.group_list) # Test self.assert_fr(f"Error: Found no window for '{nick_to_short_address('Bob')}'.", ch_contact_s, cmd_data, *self.args, header) def test_setting_change_contact(self): # Setup self.window = self.window_list.get_window(nick_to_pub_key("Bob")) self.window.type = WIN_TYPE_CONTACT self.window.type_print = 'contact' self.window.window_contacts = self.contact_list.contacts bob = self.contact_list.get_contact_by_address_or_nick("Bob") # Test for attr, header in [('log_messages', CH_LOGGING), ('notifications', CH_NOTIFY), ('file_reception', CH_FILE_RECV)]: for s in [ENABLE, ENABLE, DISABLE, DISABLE]: cmd_data = s + nick_to_pub_key("Bob") self.assertIsNone(ch_contact_s(cmd_data, *self.args, header)) self.assertEqual(bob.__getattribute__(attr), (s == ENABLE)) def test_setting_change_group(self): # Setup self.window = self.window_list.get_window(group_name_to_group_id('test_group')) self.window.type = WIN_TYPE_GROUP self.window.type_print = 'group' self.window.window_contacts = self.group_list.get_group('test_group').members # Test for attr, header in [('log_messages', CH_LOGGING), ('notifications', CH_NOTIFY), ('file_reception', CH_FILE_RECV)]: for s in [ENABLE, ENABLE, DISABLE, DISABLE]: cmd_data = s + group_name_to_group_id('test_group') self.assertIsNone(ch_contact_s(cmd_data, *self.args, header)) if header in [CH_LOGGING, CH_NOTIFY]: self.assertEqual(self.group_list.get_group('test_group').__getattribute__(attr), (s == ENABLE)) if header == CH_FILE_RECV: for m in self.group_list.get_group('test_group').members: self.assertEqual(m.file_reception, (s == ENABLE)) def test_setting_change_all(self): # Setup self.window = self.window_list.get_window(nick_to_pub_key("Bob")) self.window.type = WIN_TYPE_CONTACT self.window.type_print = 'contact' self.window.window_contacts = self.contact_list.contacts # Test for attr, header in [('log_messages', CH_LOGGING), ('notifications', CH_NOTIFY), ('file_reception', CH_FILE_RECV)]: for s in [ENABLE, ENABLE, DISABLE, DISABLE]: cmd_data = s.upper() + US_BYTE self.assertIsNone(ch_contact_s(cmd_data, *self.args, header)) if header in [CH_LOGGING, CH_NOTIFY]: for c in self.contact_list.get_list_of_contacts(): self.assertEqual(c.__getattribute__(attr), (s == ENABLE)) for g in self.group_list.groups: self.assertEqual(g.__getattribute__(attr), (s == ENABLE)) if header == CH_FILE_RECV: for c in self.contact_list.get_list_of_contacts(): self.assertEqual(c.__getattribute__(attr), (s == ENABLE))
class TestContactSetting(TFCTestCase): def setUp(self) -> None: """Pre-test actions.""" self.contact_list = ContactList(nicks=['Alice', 'Bob']) self.group_list = GroupList(groups=['test_group']) self.settings = Settings() self.queues = gen_queue_dict() self.pub_key = nick_to_pub_key("Alice") self.args = self.contact_list, self.group_list, self.settings, self.queues def tearDown(self) -> None: """Post-test actions.""" tear_queues(self.queues) def test_invalid_command_raises_soft_error(self) -> None: self.assert_se("Error: Invalid command.", contact_setting, UserInput('loging on'), None, *self.args) def test_missing_parameter_raises_soft_error(self) -> None: self.assert_se("Error: Invalid command.", contact_setting, UserInput(''), None, *self.args) def test_invalid_extra_parameter_raises_soft_error(self) -> None: self.assert_se("Error: Invalid command.", contact_setting, UserInput('logging on al'), None, *self.args) def test_enable_logging_for_user(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.log_messages = False window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact) # Test self.assertFalse(contact.log_messages) self.assertIsNone(contact_setting(UserInput('logging on'), window, *self.args)) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1) self.assertEqual(self.queues[LOG_SETTING_QUEUE].qsize(), 0) self.assertTrue(contact.log_messages) def test_enable_logging_for_user_during_traffic_masking(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.log_messages = False window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, log_messages=False) self.settings.traffic_masking = True # Test self.assertFalse(contact.log_messages) self.assertFalse(window.log_messages) self.assertIsNone(contact_setting(UserInput('logging on'), window, *self.args)) self.assertEqual(self.queues[TM_COMMAND_PACKET_QUEUE].qsize(), 1) self.assertTrue(self.queues[LOG_SETTING_QUEUE].get()) self.assertTrue(window.log_messages) self.assertTrue(contact.log_messages) def test_enable_logging_for_group(self) -> None: # Setup group = self.group_list.get_group('test_group') group.log_messages = False window = TxWindow(uid=group_name_to_group_id('test_group'), type=WIN_TYPE_GROUP, group=group, window_contacts=group.members) # Test self.assertFalse(group.log_messages) self.assertIsNone(contact_setting(UserInput('logging on'), window, *self.args)) self.assertTrue(group.log_messages) def test_enable_logging_for_all_users(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick("Alice") window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) for c in self.contact_list: c.log_messages = False for g in self.group_list: g.log_messages = False # Test for c in self.contact_list: self.assertFalse(c.log_messages) for g in self.group_list: self.assertFalse(g.log_messages) self.assertIsNone(contact_setting(UserInput('logging on all'), window, *self.args)) for c in self.contact_list: self.assertTrue(c.log_messages) for g in self.group_list: self.assertTrue(g.log_messages) def test_disable_logging_for_user(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.log_messages = True window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) # Test self.assertTrue(contact.log_messages) self.assertIsNone(contact_setting(UserInput('logging off'), window, *self.args)) self.assertFalse(contact.log_messages) def test_disable_logging_for_group(self) -> None: # Setup group = self.group_list.get_group('test_group') group.log_messages = True window = TxWindow(uid=group_name_to_group_id('test_group'), type=WIN_TYPE_GROUP, group=group, window_contacts=group.members) # Test self.assertTrue(group.log_messages) self.assertIsNone(contact_setting(UserInput('logging off'), window, *self.args)) self.assertFalse(group.log_messages) def test_disable_logging_for_all_users(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick("Alice") window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) for c in self.contact_list: c.log_messages = True for g in self.group_list: g.log_messages = True # Test for c in self.contact_list: self.assertTrue(c.log_messages) for g in self.group_list: self.assertTrue(g.log_messages) self.assertIsNone(contact_setting(UserInput('logging off all'), window, *self.args)) for c in self.contact_list: self.assertFalse(c.log_messages) for g in self.group_list: self.assertFalse(g.log_messages) def test_enable_file_reception_for_user(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.file_reception = False window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) # Test self.assertFalse(contact.file_reception) self.assertIsNone(contact_setting(UserInput('store on'), window, *self.args)) self.assertTrue(contact.file_reception) def test_enable_file_reception_for_group(self) -> None: # Setup group = self.group_list.get_group('test_group') window = TxWindow(uid=group_name_to_group_id('test_group'), type=WIN_TYPE_GROUP, group=group, window_contacts=group.members) for m in group: m.file_reception = False # Test for m in group: self.assertFalse(m.file_reception) self.assertIsNone(contact_setting(UserInput('store on'), window, *self.args)) for m in group: self.assertTrue(m.file_reception) def test_enable_file_reception_for_all_users(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick("Alice") window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) for c in self.contact_list: c.file_reception = False # Test for c in self.contact_list: self.assertFalse(c.file_reception) self.assertIsNone(contact_setting(UserInput('store on all'), window, *self.args)) for c in self.contact_list: self.assertTrue(c.file_reception) def test_disable_file_reception_for_user(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.file_reception = True window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) # Test self.assertTrue(contact.file_reception) self.assertIsNone(contact_setting(UserInput('store off'), window, *self.args)) self.assertFalse(contact.file_reception) def test_disable_file_reception_for_group(self) -> None: # Setup group = self.group_list.get_group('test_group') window = TxWindow(uid=group_name_to_group_id('test_group'), type=WIN_TYPE_GROUP, group=group, window_contacts=group.members) for m in group: m.file_reception = True # Test for m in group: self.assertTrue(m.file_reception) self.assertIsNone(contact_setting(UserInput('store off'), window, *self.args)) for m in group: self.assertFalse(m.file_reception) def test_disable_file_reception_for_all_users(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick("Alice") window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) for c in self.contact_list: c.file_reception = True # Test for c in self.contact_list: self.assertTrue(c.file_reception) self.assertIsNone(contact_setting(UserInput('store off all'), window, *self.args)) for c in self.contact_list: self.assertFalse(c.file_reception) def test_enable_notifications_for_user(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.notifications = False window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact) # Test self.assertFalse(contact.notifications) self.assertIsNone(contact_setting(UserInput('notify on'), window, *self.args)) self.assertTrue(contact.notifications) def test_enable_notifications_for_group(self) -> None: # Setup user_input = UserInput('notify on') group = self.group_list.get_group('test_group') group.notifications = False window = TxWindow(uid=group_name_to_group_id('test_group'), type=WIN_TYPE_GROUP, group=group, window_contacts=group.members) # Test self.assertFalse(group.notifications) self.assertIsNone(contact_setting(user_input, window, *self.args)) self.assertTrue(group.notifications) def test_enable_notifications_for_all_users(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick("Alice") window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) for c in self.contact_list: c.notifications = False for g in self.group_list: g.notifications = False # Test for c in self.contact_list: self.assertFalse(c.notifications) for g in self.group_list: self.assertFalse(g.notifications) self.assertIsNone(contact_setting(UserInput('notify on all'), window, *self.args)) for c in self.contact_list: self.assertTrue(c.notifications) for g in self.group_list: self.assertTrue(g.notifications) def test_disable_notifications_for_user(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick('Alice') contact.notifications = True window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) # Test self.assertTrue(contact.notifications) self.assertIsNone(contact_setting(UserInput('notify off'), window, *self.args)) self.assertFalse(contact.notifications) def test_disable_notifications_for_group(self) -> None: # Setup group = self.group_list.get_group('test_group') group.notifications = True window = TxWindow(uid=group_name_to_group_id('test_group'), type=WIN_TYPE_GROUP, group=group, window_contacts=group.members) # Test self.assertTrue(group.notifications) self.assertIsNone(contact_setting(UserInput('notify off'), window, *self.args)) self.assertFalse(group.notifications) def test_disable_notifications_for_all_users(self) -> None: # Setup contact = self.contact_list.get_contact_by_address_or_nick("Alice") window = TxWindow(uid=self.pub_key, type=WIN_TYPE_CONTACT, contact=contact, window_contacts=[contact]) for c in self.contact_list: c.notifications = True for g in self.group_list: g.notifications = True # Test for c in self.contact_list: self.assertTrue(c.notifications) for g in self.group_list: self.assertTrue(g.notifications) self.assertIsNone(contact_setting(UserInput('notify off all'), window, *self.args)) for c in self.contact_list: self.assertFalse(c.notifications) for g in self.group_list: self.assertFalse(g.notifications)
class TestRemoveContact(TFCTestCase): def setUp(self) -> None: """Pre-test actions.""" self.unit_test_dir = cd_unit_test() self.contact_list = ContactList(nicks=['Alice']) self.group_list = GroupList(groups=['test_group']) self.settings = Settings() self.queues = gen_queue_dict() self.master_key = MasterKey() self.pub_key = nick_to_pub_key('Alice') self.args = self.contact_list, self.group_list, self.settings, self.queues, self.master_key def tearDown(self) -> None: """Post-test actions.""" cleanup(self.unit_test_dir) tear_queues(self.queues) def test_contact_removal_during_traffic_masking_raises_soft_error(self) -> None: # Setup self.settings.traffic_masking = True # Test self.assert_se("Error: Command is disabled during traffic masking.", remove_contact, UserInput(), None, *self.args) def test_missing_account_raises_soft_error(self) -> None: self.assert_se("Error: No account specified.", remove_contact, UserInput('rm '), None, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('shutil.get_terminal_size', return_value=[150, 150]) @mock.patch('builtins.input', return_value='Yes') def test_invalid_account_raises_soft_error(self, *_: Any) -> None: # Setup user_input = UserInput(f'rm {nick_to_onion_address("Alice")[:-1]}') window = TxWindow(window_contacts=[self.contact_list.get_contact_by_address_or_nick('Alice')], type=WIN_TYPE_CONTACT, uid=self.pub_key) # Test self.assert_se("Error: Invalid selection.", remove_contact, user_input, window, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('shutil.get_terminal_size', return_value=[150, 150]) @mock.patch('builtins.input', return_value='No') def test_user_abort_raises_soft_error(self, *_: Any) -> None: # Setup user_input = UserInput(f'rm {nick_to_onion_address("Alice")}') # Test self.assert_se("Removal of contact aborted.", remove_contact, user_input, None, *self.args) @mock.patch('builtins.input', return_value='Yes') def test_successful_removal_of_contact(self, _: Any) -> None: # Setup window = TxWindow(window_contacts=[self.contact_list.get_contact_by_address_or_nick('Alice')], type=WIN_TYPE_CONTACT, uid=self.pub_key) # Test for g in self.group_list: self.assertIsInstance(g, Group) self.assertTrue(g.has_member(self.pub_key)) self.assertIsNone(remove_contact(UserInput('rm Alice'), window, *self.args)) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1) km_data = self.queues[KEY_MANAGEMENT_QUEUE].get() self.assertEqual(km_data, (KDB_REMOVE_ENTRY_HEADER, self.pub_key)) self.assertFalse(self.contact_list.has_pub_key(self.pub_key)) for g in self.group_list: self.assertIsInstance(g, Group) self.assertFalse(g.has_member(self.pub_key)) @mock.patch('builtins.input', return_value='Yes') def test_successful_removal_of_last_member_of_active_group(self, _: Any) -> None: # Setup user_input = UserInput('rm Alice') window = TxWindow(window_contacts=[self.contact_list.get_contact_by_address_or_nick("Alice")], type=WIN_TYPE_GROUP, name='test_group') group = self.group_list.get_group('test_group') group.members = [self.contact_list.get_contact_by_address_or_nick("Alice")] pub_key = nick_to_pub_key('Alice') # Test for g in self.group_list: self.assertIsInstance(g, Group) self.assertTrue(g.has_member(pub_key)) self.assertEqual(len(group), 1) self.assertIsNone(remove_contact(user_input, window, *self.args)) for g in self.group_list: self.assertIsInstance(g, Group) self.assertFalse(g.has_member(pub_key)) self.assertFalse(self.contact_list.has_pub_key(pub_key)) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1) km_data = self.queues[KEY_MANAGEMENT_QUEUE].get() self.assertEqual(km_data, (KDB_REMOVE_ENTRY_HEADER, pub_key)) @mock.patch('shutil.get_terminal_size', return_value=[150, 150]) @mock.patch('builtins.input', return_value='Yes') def test_no_contact_found_on_transmitter(self, *_: Any) -> None: # Setup user_input = UserInput(f'rm {nick_to_onion_address("Charlie")}') contact_list = ContactList(nicks=['Bob']) window = TxWindow(window_contact=[contact_list.get_contact_by_address_or_nick('Bob')], type=WIN_TYPE_GROUP) # Test self.assertIsNone(remove_contact(user_input, window, *self.args)) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1) self.assertEqual(self.queues[RELAY_PACKET_QUEUE].qsize(), 1) command_packet = self.queues[COMMAND_PACKET_QUEUE].get() self.assertIsInstance(command_packet, bytes)
class TestKeyExchange(TFCTestCase): def setUp(self): self.contact_list = ContactList() self.settings = Settings() self.queues = gen_queue_dict() self.args = self.contact_list, self.settings, self.queues def tearDown(self): tear_queues(self.queues) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) @mock.patch('builtins.input', return_value=b58encode(bytes(TFC_PUBLIC_KEY_LENGTH), public_key=True)) def test_zero_public_key_raises_fr(self, *_): self.assert_fr("Error: Zero public key", start_key_exchange, nick_to_pub_key("Alice"), 'Alice', *self.args) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) @mock.patch('builtins.input', return_value=b58encode((TFC_PUBLIC_KEY_LENGTH - 1) * b'a', public_key=True)) def test_invalid_public_key_length_raises_fr(self, *_): self.assert_fr("Error: Invalid public key length", start_key_exchange, nick_to_pub_key("Alice"), 'Alice', *self.args) @mock.patch( 'builtins.input', side_effect=[ '', # Empty message should resend key VALID_ECDHE_PUB_KEY[:-1], # Short key should fail VALID_ECDHE_PUB_KEY + 'a', # Long key should fail VALID_ECDHE_PUB_KEY[:-1] + 'a', # Invalid key should fail VALID_ECDHE_PUB_KEY, # Correct key 'No' ]) # Fingerprint mismatch) @mock.patch('time.sleep', return_value=None) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) def test_fingerprint_mismatch_raises_fr(self, *_): self.assert_fr("Error: Fingerprint mismatch", start_key_exchange, nick_to_pub_key("Alice"), 'Alice', *self.args) @mock.patch( 'builtins.input', side_effect=[ '', # Resend public key VALID_ECDHE_PUB_KEY, # Correct key 'Yes', # Fingerprint match '', # Resend contact data 'ff', # Invalid confirmation code blake2b(nick_to_pub_key('Alice'), digest_size=CONFIRM_CODE_LENGTH).hex() ]) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) @mock.patch('time.sleep', return_value=None) def test_successful_exchange(self, *_): self.assertIsNone( start_key_exchange(nick_to_pub_key("Alice"), 'Alice', *self.args)) contact = self.contact_list.get_contact_by_pub_key( nick_to_pub_key("Alice")) self.assertEqual(contact.onion_pub_key, nick_to_pub_key("Alice")) self.assertEqual(contact.nick, 'Alice') self.assertEqual(contact.kex_status, KEX_STATUS_VERIFIED) self.assertIsInstance(contact.tx_fingerprint, bytes) self.assertIsInstance(contact.rx_fingerprint, bytes) self.assertEqual(len(contact.tx_fingerprint), FINGERPRINT_LENGTH) self.assertEqual(len(contact.rx_fingerprint), FINGERPRINT_LENGTH) self.assertFalse(contact.log_messages) self.assertFalse(contact.file_reception) self.assertTrue(contact.notifications) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 2) self.assertEqual(self.queues[RELAY_PACKET_QUEUE].qsize(), 2) cmd, account, tx_key, rx_key, tx_hek, rx_hek = self.queues[ KEY_MANAGEMENT_QUEUE].get() self.assertEqual(cmd, KDB_ADD_ENTRY_HEADER) self.assertEqual(account, nick_to_pub_key("Alice")) self.assertEqual(len(tx_key), SYMMETRIC_KEY_LENGTH) for key in [tx_key, rx_key, tx_hek, rx_hek]: self.assertIsInstance(key, bytes) self.assertEqual(len(key), SYMMETRIC_KEY_LENGTH) @mock.patch( 'builtins.input', side_effect=[ '', # Resend public key VALID_ECDHE_PUB_KEY, # Correct key KeyboardInterrupt, # Skip fingerprint verification '', # Manual proceed for warning message blake2b(nick_to_pub_key('Alice'), digest_size=CONFIRM_CODE_LENGTH).hex() ]) @mock.patch('time.sleep', return_value=None) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) def test_successful_exchange_skip_fingerprint_verification(self, *_): self.assertIsNone( start_key_exchange(nick_to_pub_key("Alice"), 'Alice', *self.args)) contact = self.contact_list.get_contact_by_pub_key( nick_to_pub_key("Alice")) self.assertEqual(contact.onion_pub_key, nick_to_pub_key("Alice")) self.assertEqual(contact.nick, 'Alice') self.assertEqual(contact.kex_status, KEX_STATUS_UNVERIFIED) @mock.patch( 'os.getrandom', side_effect=[SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a']) @mock.patch('builtins.input', side_effect=[ KeyboardInterrupt, VALID_ECDHE_PUB_KEY, 'Yes', blake2b(nick_to_pub_key('Alice'), digest_size=CONFIRM_CODE_LENGTH).hex() ]) @mock.patch('time.sleep', return_value=None) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) def test_successful_exchange_with_previous_key(self, *_): # Test caching of private key self.assert_fr("Key exchange interrupted.", start_key_exchange, nick_to_pub_key('Alice'), 'Alice', *self.args) alice = self.contact_list.get_contact_by_address_or_nick('Alice') self.assertEqual(alice.kex_status, KEX_STATUS_PENDING) # Test re-using private key self.assertIsNone( start_key_exchange(nick_to_pub_key('Alice'), 'Alice', *self.args)) self.assertIsNone(alice.tfc_private_key) self.assertEqual(alice.kex_status, KEX_STATUS_VERIFIED)
class TestTxWindow(TFCTestCase): def setUp(self): self.contact_list = ContactList(['Alice', 'Bob']) self.group_list = GroupList(groups=['test_group', 'test_group_2']) self.window = TxWindow(self.contact_list, self.group_list) self.window.group = self.group_list.get_group('test_group') self.window.type = WIN_TYPE_GROUP self.settings = Settings() self.queues = gen_queue_dict() self.onion_service = OnionService() self.gateway = Gateway() self.args = self.settings, self.queues, self.onion_service, self.gateway def tearDown(self): tear_queues(self.queues) def test_window_iterates_over_contacts(self): # Setup self.window.window_contacts = self.contact_list.contacts # Test for c in self.window: self.assertIsInstance(c, Contact) def test_len_returns_number_of_contacts_in_window(self): # Setup self.window.window_contacts = self.contact_list.contacts # Test self.assertEqual(len(self.window), 2) def test_group_window_change_during_traffic_masking_raises_fr(self): # Setup self.settings.traffic_masking = True self.window.uid = 'test_group' # Test self.assert_fr("Error: Can't change window during traffic masking.", self.window.select_tx_window, *self.args, selection='test_group_2', cmd=True) def test_contact_window_change_during_traffic_masking_raises_fr(self): # Setup self.settings.traffic_masking = True self.window.uid = nick_to_pub_key("Alice") # Test self.assert_fr("Error: Can't change window during traffic masking.", self.window.select_tx_window, *self.args, selection=nick_to_onion_address("Bob"), cmd=True) def test_contact_window_reload_during_traffic_masking(self): # Setup self.settings.traffic_masking = True self.window.uid = nick_to_pub_key("Alice") # Test self.assertIsNone( self.window.select_tx_window( *self.args, selection=nick_to_onion_address("Alice"), cmd=True)) self.assertEqual(self.window.uid, nick_to_pub_key("Alice")) def test_group_window_reload_during_traffic_masking(self): # Setup self.settings.traffic_masking = True self.window.name = 'test_group' self.window.uid = group_name_to_group_id('test_group') # Test self.assertIsNone( self.window.select_tx_window(*self.args, selection='test_group', cmd=True)) self.assertEqual(self.window.uid, group_name_to_group_id('test_group')) def test_invalid_selection_raises_fr(self): # Setup self.window.uid = nick_to_pub_key("Alice") # Test self.assert_fr("Error: No contact/group was found.", self.window.select_tx_window, *self.args, selection=nick_to_onion_address("Charlie"), cmd=True) @mock.patch('builtins.input', return_value=nick_to_onion_address("Bob")) def test_window_selection_during_traffic_masking(self, *_): # Setup self.settings.traffic_masking = True self.window.uid = None # Test self.assertIsNone(self.window.select_tx_window(*self.args)) self.assertEqual(self.queues[WINDOW_SELECT_QUEUE].qsize(), 1) @mock.patch('builtins.input', return_value=nick_to_onion_address("Bob")) def test_contact_window_selection_from_input(self, *_): # Setup self.window.uid = None # Test self.assertIsNone(self.window.select_tx_window(*self.args)) self.assertEqual(self.window.uid, nick_to_pub_key("Bob")) def test_group_window_selection_from_command(self): # Setup self.window.uid = None self.assertIsNone( self.window.select_tx_window(*self.args, selection='test_group', cmd=True)) self.assertEqual(self.window.uid, group_name_to_group_id('test_group')) def test_deselect_window(self): # Setup self.window.window_contacts = self.contact_list.contacts self.window.contact = self.contact_list.get_contact_by_address_or_nick( "Bob") self.window.name = 'Bob' self.window.type = WIN_TYPE_CONTACT self.window.uid = nick_to_pub_key("Bob") # Test self.assertIsNone(self.window.deselect()) self.assertIsNone(self.window.contact) self.assertEqual(self.window.name, '') self.assertEqual(self.window.type, '') self.assertEqual(self.window.uid, b'') def test_is_selected(self): self.window.name = '' self.assertFalse(self.window.is_selected()) self.window.name = nick_to_pub_key("Bob") self.assertTrue(self.window.is_selected()) def test_update_log_messages_for_contact(self): # Setup self.window.type = WIN_TYPE_CONTACT self.window.log_messages = None self.window.contact = self.contact_list.get_contact_by_address_or_nick( 'Alice') self.window.contact.log_messages = False # Test self.assertIsNone(self.window.update_log_messages()) self.assertFalse(self.window.log_messages) def test_update_log_messages_for_group(self): # Setup self.window.type = WIN_TYPE_GROUP self.window.log_messages = None self.window.group = self.group_list.get_group('test_group') self.window.group.log_messages = False # Test self.assertIsNone(self.window.update_log_messages()) self.assertFalse(self.window.log_messages) def test_update_group_win_members_if_group_is_available(self): # Setup self.window.window_contacts = [] self.window.group = None self.window.group_id = group_name_to_group_id('test_group') self.window.name = 'test_group' self.window.type = WIN_TYPE_GROUP # Test self.assertIsNone(self.window.update_window(self.group_list)) self.assertEqual(self.window.group, self.group_list.get_group('test_group')) self.assertEqual(self.window.window_contacts, self.window.group.members) def test_window_contact_is_reloaded_when_contact_is_active(self): # Setup self.window.type = WIN_TYPE_CONTACT self.window.contact = create_contact('Alice') self.window.window_contacts = [self.window.contact] self.assertIsNot( self.window.contact, self.window.contact_list.get_contact_by_pub_key( nick_to_pub_key('Alice'))) self.assertIsNot( self.window.window_contacts[0], self.window.contact_list.get_contact_by_pub_key( nick_to_pub_key('Alice'))) # Test self.assertIsNone(self.window.update_window(self.group_list)) self.assertIs( self.window.contact, self.window.contact_list.get_contact_by_pub_key( nick_to_pub_key('Alice'))) self.assertIs( self.window.window_contacts[0], self.window.contact_list.get_contact_by_pub_key( nick_to_pub_key('Alice'))) def test_deactivate_window_if_group_is_not_available(self): # Setup self.window.window_contacts = [] self.window.group = None self.window.name = 'test_group_3' self.window.type = WIN_TYPE_GROUP # Test self.assertIsNone(self.window.update_window(self.group_list)) self.assertIsNone(self.window.contact) self.assertEqual(self.window.name, '') self.assertEqual(self.window.type, '') self.assertEqual(self.window.uid, b'') @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=[ 'Alice', VALID_ECDHE_PUB_KEY, 'yes', blake2b(nick_to_pub_key('Alice'), digest_size=CONFIRM_CODE_LENGTH).hex() ]) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) def test_selecting_pending_contact_starts_key_exchange(self, *_): # Setup alice = self.contact_list.get_contact_by_address_or_nick('Alice') bob = self.contact_list.get_contact_by_address_or_nick('Bob') alice.kex_status = KEX_STATUS_PENDING bob.kex_status = KEX_STATUS_PENDING # Test self.assertIsNone(self.window.select_tx_window(*self.args)) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 2) self.assertEqual(self.queues[WINDOW_SELECT_QUEUE].qsize(), 0) self.assertEqual(alice.kex_status, KEX_STATUS_VERIFIED) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=[ '/add', nick_to_onion_address('Alice'), 'Alice', '', VALID_ECDHE_PUB_KEY, 'yes', blake2b(nick_to_pub_key('Alice'), digest_size=CONFIRM_CODE_LENGTH).hex() ]) @mock.patch('shutil.get_terminal_size', return_value=[200, 200]) def test_adding_new_contact_from_contact_selection(self, *_): # Setup alice = self.contact_list.get_contact_by_address_or_nick('Alice') alice.kex_status = KEX_STATUS_PENDING # Test self.assert_fr('New contact added.', self.window.select_tx_window, *self.args) self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1) self.assertEqual(self.queues[WINDOW_SELECT_QUEUE].qsize(), 0) self.assertEqual(alice.kex_status, KEX_STATUS_VERIFIED) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['/rm ']) def test_missing_account_when_removing_raises_fr(self, *_): self.assert_fr("Error: No account specified.", self.window.select_tx_window, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['/rm Charlie', 'yes']) def test_unknown_account_when_removing_raises_fr(self, *_): self.assert_fr("Error: Unknown contact 'Charlie'.", self.window.select_tx_window, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['/rm Alice', 'no']) def test_abort_removal_of_contact_form_contact_selection(self, *_): self.assert_fr("Removal of contact aborted.", self.window.select_tx_window, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['/rm Alice', 'yes']) def test_removing_pending_contact_from_contact_selection(self, *_): self.assert_fr("Removed contact 'Alice'.", self.window.select_tx_window, *self.args) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['/connect', b'a'.hex()]) def test_sending_onion_service_data_from_contact_selection(self, *_): self.assertIsNone(self.window.select_tx_window(*self.args)) self.assertEqual(len(self.gateway.packets), 1) @mock.patch('time.sleep', return_value=None) @mock.patch('builtins.input', side_effect=['/help']) def test_invalid_command_raises_fr(self, *_): self.assert_fr("Error: Invalid command.", self.window.select_tx_window, *self.args)
class TestRxWindow(TFCTestCase): def setUp(self): """Pre-test actions.""" self.contact_list = ContactList( nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID]) self.group_list = GroupList(groups=['test_group', 'test_group2']) self.settings = Settings() self.packet_list = PacketList() self.ts = datetime.fromtimestamp(1502750000) self.time = self.ts.strftime('%H:%M:%S.%f')[:-4] group = self.group_list.get_group('test_group') group.members = list( map(self.contact_list.get_contact_by_address_or_nick, ['Alice', 'Bob', 'Charlie'])) def create_window(self, uid: bytes): """Create new RxWindow object.""" return RxWindow(uid, self.contact_list, self.group_list, self.settings, self.packet_list) def test_command_window_creation(self): window = self.create_window(WIN_UID_LOCAL) self.assertEqual(window.type, WIN_TYPE_COMMAND) self.assertEqual(window.name, WIN_TYPE_COMMAND) def test_file_window_creation(self): window = self.create_window(WIN_UID_FILE) self.assertEqual(window.type, WIN_TYPE_FILE) def test_contact_window_creation(self): window = self.create_window(nick_to_pub_key("Alice")) self.assertEqual(window.type, WIN_TYPE_CONTACT) self.assertEqual(window.window_contacts[0].onion_pub_key, nick_to_pub_key("Alice")) self.assertEqual(window.name, 'Alice') def test_group_window_creation(self): window = self.create_window(group_name_to_group_id('test_group')) self.assertEqual(window.type, WIN_TYPE_GROUP) self.assertEqual(window.window_contacts[0].onion_pub_key, nick_to_pub_key("Alice")) self.assertEqual(window.name, 'test_group') def test_invalid_uid_raises_fr(self): self.assert_fr( "Invalid window 'mfqwcylbmfqwcylbmfqwcylbmfqwcylbmfqwcylbmfqwcylbmfqwbfad'.", self.create_window, ONION_SERVICE_PUBLIC_KEY_LENGTH * b'a') self.assert_fr("Invalid window '2dnAMoWNfTXAJ'.", self.create_window, GROUP_ID_LENGTH * b'a') self.assert_fr("Invalid window '<unable to encode>'.", self.create_window, b'bad_uid') def test_window_iterates_over_message_tuples(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.message_log = 5 * [ (datetime.now(), 'Lorem ipsum', nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER, False, False) ] # Test for mt in window: self.assertEqual(mt[1:], ("Lorem ipsum", nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER, False, False)) def test_len_returns_number_of_messages_in_window(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.message_log = 5 * [ (datetime.now(), "Lorem ipsum", nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER, False, False) ] # Test self.assertEqual(len(window), 5) def test_remove_contacts(self): # Setup window = self.create_window(group_name_to_group_id('test_group')) # Test self.assertEqual(len(window.window_contacts), 3) self.assertIsNone( window.remove_contacts([ nick_to_pub_key("Alice"), nick_to_pub_key("Bob"), nick_to_pub_key("DoesNotExist") ])) self.assertEqual(len(window.window_contacts), 1) def test_add_contacts(self): # Setup window = self.create_window(group_name_to_group_id('test_group')) window.window_contacts = [ self.contact_list.get_contact_by_address_or_nick('Alice') ] # Test self.assertIsNone( window.add_contacts([ nick_to_pub_key("Alice"), nick_to_pub_key("Bob"), nick_to_pub_key("DoesNotExist") ])) self.assertEqual(len(window.window_contacts), 2) def test_reset_window(self): # Setup window = self.create_window(group_name_to_group_id('test_group')) window.message_log = \ [(datetime.now(), "Hi everybody", nick_to_pub_key("Alice"), ORIGIN_USER_HEADER, False, False), (datetime.now(), "Hi David", nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER, False, False), (datetime.now(), "Hi David", nick_to_pub_key("Bob"), ORIGIN_CONTACT_HEADER, False, False)] # Test self.assertIsNone(window.reset_window()) self.assertEqual(len(window), 0) def test_has_contact(self): window = self.create_window(group_name_to_group_id('test_group')) self.assertTrue(window.has_contact(nick_to_pub_key("Alice"))) self.assertFalse(window.has_contact(nick_to_pub_key("DoesNotExist"))) def test_create_handle_dict(self): # Setup window = self.create_window(group_name_to_group_id('test_group')) message_log = [ (datetime.now(), "Lorem ipsum", nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER, False, False), (datetime.now(), "Lorem ipsum", nick_to_pub_key("Bob"), ORIGIN_USER_HEADER, False, False), (datetime.now(), "Lorem ipsum", nick_to_pub_key("Charlie"), ORIGIN_CONTACT_HEADER, False, False), (datetime.now(), "Lorem ipsum", nick_to_pub_key("Charlie"), ORIGIN_CONTACT_HEADER, True, False), (datetime.now(), "Lorem ipsum", nick_to_pub_key("Charlie"), ORIGIN_CONTACT_HEADER, False, False), (datetime.now(), "Lorem ipsum", nick_to_pub_key("David"), ORIGIN_CONTACT_HEADER, False, False), (datetime.now(), "Lorem ipsum", nick_to_pub_key("Eric"), ORIGIN_CONTACT_HEADER, False, False) ] # Test self.assertIsNone(window.create_handle_dict(message_log)) self.assertEqual( window.handle_dict, { nick_to_pub_key("Alice"): 'Alice', nick_to_pub_key("Bob"): 'Bob', nick_to_pub_key("Charlie"): 'Charlie', nick_to_pub_key("David"): nick_to_short_address("David"), nick_to_pub_key("Eric"): nick_to_short_address("Eric") }) def test_get_command_handle(self): # Setup window = self.create_window(WIN_UID_LOCAL) window.is_active = True # Test self.assertEqual( window.get_handle(self.ts, WIN_UID_LOCAL, ORIGIN_USER_HEADER), f"{self.time} -!- ") def test_get_contact_handle(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.is_active = True window.handle_dict = {nick_to_pub_key("Alice"): 'Alice'} # Test self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Alice"), ORIGIN_USER_HEADER), f"{self.time} Me: ") self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER), f"{self.time} Alice: ") window.is_active = False self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Alice"), ORIGIN_USER_HEADER), f"{self.time} Me (private message): ") self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Alice"), ORIGIN_CONTACT_HEADER), f"{self.time} Alice (private message): ") def test_get_group_contact_handle(self): # Setup window = self.create_window(group_name_to_group_id('test_group')) window.is_active = True window.handle_dict = { nick_to_pub_key("Alice"): 'Alice', nick_to_pub_key("Charlie"): 'Charlie', nick_to_pub_key("David"): nick_to_short_address("David"), nick_to_pub_key("Eric"): nick_to_short_address("Eric") } # Test self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Alice"), ORIGIN_USER_HEADER), f"{self.time} Me: ") self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Charlie"), ORIGIN_CONTACT_HEADER), f"{self.time} Charlie: ") window.is_active = False self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Alice"), ORIGIN_USER_HEADER), f"{self.time} Me (group test_group): ") self.assertEqual( window.get_handle(self.ts, nick_to_pub_key("Charlie"), ORIGIN_CONTACT_HEADER), f"{self.time} Charlie (group test_group): ") @mock.patch('time.sleep', return_value=None) def test_print_to_inactive_window_preview_on_short_message(self, _): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.handle_dict = {nick_to_pub_key("Alice"): 'Alice'} window.is_active = False window.settings = Settings(new_message_notify_preview=True) msg_tuple = (self.ts, "Hi Bob", nick_to_pub_key("Bob"), ORIGIN_USER_HEADER, False, False) # Test self.assert_prints( f"{BOLD_ON}{self.time} Me (private message): {NORMAL_TEXT}" f"Hi Bob\n{CURSOR_UP_ONE_LINE}{CLEAR_ENTIRE_LINE}", window.print, msg_tuple) @mock.patch('time.sleep', return_value=None) def test_print_to_inactive_window_preview_on_long_message(self, _): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.is_active = False window.handle_dict = {nick_to_pub_key("Alice"): 'Alice'} window.settings = Settings(new_message_notify_preview=True) long_message = ( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque consequat libero et lao" "reet egestas. Aliquam a arcu malesuada, elementum metus eget, elementum mi. Vestibulum i" "d arcu sem. Ut sodales odio sed viverra mollis. Praesent gravida ante tellus, pellentesq" "ue venenatis massa placerat quis. Nullam in magna porta, hendrerit sem vel, dictum ipsum" ". Ut sagittis, ipsum ut bibendum ornare, ex lorem congue metus, vel posuere metus nulla " "at augue.") msg_tuple = (self.ts, long_message, nick_to_pub_key("Bob"), ORIGIN_USER_HEADER, False, False) # Test self.assert_prints( f"{BOLD_ON}{self.time} Me (private message): {NORMAL_TEXT}Lorem ipsum dolor sit " f"amet, consectetu…\n{CURSOR_UP_ONE_LINE}{CLEAR_ENTIRE_LINE}", window.print, msg_tuple) @mock.patch('time.sleep', return_value=None) def test_print_to_inactive_window_preview_off(self, _): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.is_active = False window.handle_dict = {nick_to_pub_key("Alice"): 'Alice'} window.settings = Settings(new_message_notify_preview=False) msg_tuple = (self.ts, "Hi Bob", nick_to_pub_key("Bob"), ORIGIN_USER_HEADER, False, False) # Test self.assert_prints( f"{BOLD_ON}{self.time} Me (private message): {NORMAL_TEXT}{BOLD_ON}1 unread message{NORMAL_TEXT}\n" f"{CURSOR_UP_ONE_LINE}{CLEAR_ENTIRE_LINE}", window.print, msg_tuple) def test_print_to_active_window_no_date_change(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.previous_msg_ts = datetime.fromtimestamp(1502750000) window.is_active = True window.handle_dict = {nick_to_pub_key("Bob"): 'Bob'} window.settings = Settings(new_message_notify_preview=False) msg_tuple = (self.ts, "Hi Alice", nick_to_pub_key("Bob"), ORIGIN_CONTACT_HEADER, False, False) # Test self.assert_prints( f"{BOLD_ON}{self.time} Bob: {NORMAL_TEXT}Hi Alice\n", window.print, msg_tuple) def test_print_to_active_window_with_date_change_and_whisper(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.previous_msg_ts = datetime.fromtimestamp(1501750000) window.is_active = True window.handle_dict = {nick_to_pub_key("Bob"): 'Bob'} window.settings = Settings(new_message_notify_preview=False) msg_tuple = (self.ts, "Hi Alice", nick_to_pub_key("Bob"), ORIGIN_CONTACT_HEADER, True, False) self.time = self.ts.strftime('%H:%M:%S.%f')[:-4] # Test self.assert_prints( f"""\ {BOLD_ON}00:00 -!- Day changed to 2017-08-15{NORMAL_TEXT} {BOLD_ON}{self.time} Bob (whisper): {NORMAL_TEXT}Hi Alice """, window.print, msg_tuple) def test_print_to_active_window_with_date_change_and_whisper_empty_message( self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.previous_msg_ts = datetime.fromtimestamp(1501750000) window.is_active = True window.handle_dict = {nick_to_pub_key("Bob"): 'Bob'} window.settings = Settings(new_message_notify_preview=False) msg_tuple = (self.ts, " ", nick_to_pub_key("Bob"), ORIGIN_CONTACT_HEADER, True, False) # Test self.assert_prints( f"""\ {BOLD_ON}00:00 -!- Day changed to 2017-08-15{NORMAL_TEXT} {BOLD_ON}{self.time} Bob (whisper): {NORMAL_TEXT} """, window.print, msg_tuple) @mock.patch('time.sleep', return_value=None) def test_print_new(self, _): # Setup window = self.create_window(nick_to_pub_key("Alice")) # Test self.assertIsNone( window.add_new(self.ts, "Hi Alice", nick_to_pub_key("Bob"), ORIGIN_CONTACT_HEADER, output=True)) self.assertEqual(len(window.message_log), 1) self.assertEqual(window.handle_dict[nick_to_pub_key("Bob")], 'Bob') def test_redraw_message_window(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.is_active = True window.message_log = [(self.ts, "Hi Alice", nick_to_pub_key("Bob"), ORIGIN_CONTACT_HEADER, False, False)] window.unread_messages = 1 # Test self.assert_prints( f"""\ {CLEAR_ENTIRE_SCREEN}{CURSOR_LEFT_UP_CORNER} ------------------------------- Unread Messages -------------------------------- {BOLD_ON}{self.time} Bob: {NORMAL_TEXT}Hi Alice """, window.redraw) self.assertEqual(window.unread_messages, 0) def test_redraw_empty_window(self): # Setup window = self.create_window(nick_to_pub_key("Alice")) window.is_active = True window.message_log = [] # Test self.assert_prints( f"""\ {CLEAR_ENTIRE_SCREEN}{CURSOR_LEFT_UP_CORNER} {BOLD_ON} This window for Alice is currently empty. {NORMAL_TEXT}\n """, window.redraw) @mock.patch('time.sleep', return_value=None) def test_redraw_file_win(self, _): # Setup self.packet_list.packets = [ Packet(type=FILE, name='testfile.txt', assembly_pt_list=5 * [b'a'], packets=10, size="100.0KB", contact=create_contact('Bob')), Packet(type=FILE, name='testfile2.txt', assembly_pt_list=7 * [b'a'], packets=100, size="15.0KB", contact=create_contact('Charlie')) ] # Test window = self.create_window(WIN_UID_FILE) self.assert_prints( f"""\ File name Size Sender Complete ──────────────────────────────────────────────────────────────────────────────── testfile.txt 100.0KB Bob 50.00% testfile2.txt 15.0KB Charlie 7.00% {6*(CURSOR_UP_ONE_LINE+CLEAR_ENTIRE_LINE)}""", window.redraw_file_win) @mock.patch('time.sleep', return_value=None) def test_redraw_empty_file_win(self, _): # Setup self.packet_list.packet_l = [] # Test window = self.create_window(WIN_UID_FILE) self.assert_prints( f"""\ {BOLD_ON} No file transmissions currently in progress. {NORMAL_TEXT} {3*(CURSOR_UP_ONE_LINE+CLEAR_ENTIRE_LINE)}""", window.redraw_file_win)
def test_loop(self): # Setup queues = gen_queue_dict() settings = Settings(traffic_masking=False) gateway = Gateway() key_list = KeyList() window = TxWindow(log_messages=True) contact_list = ContactList(nicks=['Alice', LOCAL_ID]) window.contact_list = contact_list window.window_contacts = [contact_list.get_contact_by_address_or_nick('Alice')] user_input = UserInput(plaintext='test') delay = 0.01 def queue_delayer(): """Place datagrams into queue after delay.""" time.sleep(delay) queue_command(b'test', settings, queues) time.sleep(delay) queue_to_nc(PUBLIC_KEY_DATAGRAM_HEADER + TFC_PUBLIC_KEY_LENGTH * b'a' + nick_to_pub_key('Alice'), # 1 queues[RELAY_PACKET_QUEUE]) time.sleep(delay) queue_to_nc(UNENCRYPTED_DATAGRAM_HEADER + UNENCRYPTED_WIPE_COMMAND, queues[RELAY_PACKET_QUEUE]) # 2 time.sleep(delay) queue_to_nc(UNENCRYPTED_DATAGRAM_HEADER + UNENCRYPTED_EXIT_COMMAND, queues[RELAY_PACKET_QUEUE]) # 3 time.sleep(delay) queues[KEY_MANAGEMENT_QUEUE].put((KDB_ADD_ENTRY_HEADER, LOCAL_PUBKEY, # 4 SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a')) time.sleep(delay) queue_message(user_input, window, settings, queues) # 5 time.sleep(delay) queue_message(user_input, window, settings, queues) # 6 time.sleep(delay) queues[KEY_MANAGEMENT_QUEUE].put((KDB_ADD_ENTRY_HEADER, nick_to_pub_key('Alice'), SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a')) time.sleep(delay) queue_message(user_input, window, settings, queues) # 7 time.sleep(delay) queue_message(user_input, window, settings, queues) # 8 time.sleep(delay) queues[SENDER_MODE_QUEUE].put(settings) threading.Thread(target=queue_delayer).start() # Test settings, m_buffer = standard_sender_loop(queues, gateway, key_list) self.assertIsInstance(settings, Settings) self.assertEqual(m_buffer, {nick_to_pub_key('Alice'): []}) self.assertEqual(len(gateway.packets), 8) self.assertEqual(queues[EXIT_QUEUE].qsize(), 2) # Teardown tear_queues(queues)