def test_get_account_list_no_dir(self): """ The keystore directory should be created if it doesn't exist, refs: https://github.com/AndreMiras/PyWallet/issues/133 """ # nominal case when the directory already exists with TemporaryDirectory() as keystore_dir: self.assertTrue(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertEqual(account_utils.get_account_list(), []) # when the directory doesn't exist it should also be created self.assertFalse(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertTrue(os.path.isdir(keystore_dir)) self.assertEqual(account_utils.get_account_list(), []) shutil.rmtree(keystore_dir, ignore_errors=True)
class TestAccountUtils(unittest.TestCase): def setUp(self): self.keystore_dir = mkdtemp() self.account_utils = AccountUtils(self.keystore_dir) def tearDown(self): shutil.rmtree(self.keystore_dir, ignore_errors=True) def test_new_account(self): """ Simple account creation test case. 1) verifies the current account list is empty 2) creates a new account and verify we can retrieve it 3) tries to unlock the account """ # 1) verifies the current account list is empty self.assertEqual(self.account_utils.get_account_list(), []) # 2) creates a new account and verify we can retrieve it password = PASSWORD account = self.account_utils.new_account(password, iterations=1) self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(account, self.account_utils.get_account_list()[0]) # 3) tries to unlock the account # it's unlocked by default after creation self.assertFalse(account.locked) # let's lock it and unlock it back account.lock() self.assertTrue(account.locked) account.unlock(password) self.assertFalse(account.locked) def test_get_account_list(self): """ Makes sure get_account_list() loads properly accounts from file system. """ password = PASSWORD self.assertEqual(self.account_utils.get_account_list(), []) account = self.account_utils.new_account(password, iterations=1) self.assertEqual(len(self.account_utils.get_account_list()), 1) account = self.account_utils.get_account_list()[0] self.assertIsNotNone(account.path) # removes the cache copy and checks again if it gets loaded self.account_utils._accounts = None self.assertEqual(len(self.account_utils.get_account_list()), 1) account = self.account_utils.get_account_list()[0] self.assertIsNotNone(account.path) def test_get_account_list_error(self): """ get_account_list() should not cache empty account on PermissionError. """ # creates a temporary account that we'll try to list password = PASSWORD account = Account.new(password, uuid=None, iterations=1) account.path = os.path.join(self.keystore_dir, account.address.hex()) with open(account.path, 'w') as f: f.write(account.dump()) # `listdir()` can raise a `PermissionError` with mock.patch('os.listdir') as mock_listdir: mock_listdir.side_effect = PermissionError with self.assertRaises(PermissionError): self.account_utils.get_account_list() # the empty account list should not be catched and loading it again # should show the existing account on file system self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(self.account_utils.get_account_list()[0].address, account.address) def test_get_account_list_no_dir(self): """ The keystore directory should be created if it doesn't exist, refs: https://github.com/AndreMiras/PyWallet/issues/133 """ # nominal case when the directory already exists with TemporaryDirectory() as keystore_dir: self.assertTrue(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertEqual(account_utils.get_account_list(), []) # when the directory doesn't exist it should also be created self.assertFalse(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertTrue(os.path.isdir(keystore_dir)) self.assertEqual(account_utils.get_account_list(), []) shutil.rmtree(keystore_dir, ignore_errors=True) def test_deleted_account_dir(self): """ The deleted_account_dir() helper method should be working with and without trailing slash. """ expected_deleted_keystore_dir = '/tmp/keystore-deleted' keystore_dirs = [ # without trailing slash '/tmp/keystore', # with one trailing slash '/tmp/keystore/', # with two trailing slashes '/tmp/keystore//', ] for keystore_dir in keystore_dirs: self.assertEqual(AccountUtils.deleted_account_dir(keystore_dir), expected_deleted_keystore_dir) def test_delete_account(self): """ Creates a new account and delete it. Then verify we can load the account from the backup/trash location. """ password = PASSWORD account = self.account_utils.new_account(password, iterations=1) address = account.address self.assertEqual(len(self.account_utils.get_account_list()), 1) # deletes the account and verifies it's not loaded anymore self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0) # even recreating the AccountUtils object self.account_utils = AccountUtils(self.keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 0) # tries to reload it from the backup/trash location deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) self.account_utils = AccountUtils(deleted_keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(self.account_utils.get_account_list()[0].address, address) def test_delete_account_already_exists(self): """ If the destination (backup/trash) directory where the account is moved already exists, it should be handled gracefully. This could happens if the account gets deleted, then reimported and deleted again, refs: https://github.com/AndreMiras/PyWallet/issues/88 """ password = PASSWORD account = self.account_utils.new_account(password, iterations=1) # creates a file in the backup/trash folder that would conflict # with the deleted account deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) os.makedirs(deleted_keystore_dir) account_filename = os.path.basename(account.path) deleted_account_path = os.path.join(deleted_keystore_dir, account_filename) # create that file open(deleted_account_path, 'a').close() # then deletes the account and verifies it worked self.assertEqual(len(self.account_utils.get_account_list()), 1) self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0) def test_get_or_create(self): """ Checks if the singleton is handled properly. It should only be different when changing keystore_dir. """ account_utils = AccountUtils.get_or_create(self.keystore_dir) assert account_utils == AccountUtils.get_or_create(self.keystore_dir) with TemporaryDirectory() as keystore_dir: assert account_utils != AccountUtils.get_or_create(keystore_dir) assert AccountUtils.get_or_create(keystore_dir) == \ AccountUtils.get_or_create(keystore_dir)