100% coverage on interactive eth account selection

pull/1989/head
Kieran Prasch 2020-05-14 14:59:53 -07:00
parent cc66d9310a
commit f583567222
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
4 changed files with 86 additions and 31 deletions

View File

@ -96,6 +96,9 @@ def select_client_account(emitter,
Note: Showing ETH and/or NU balances, causes an eager blockchain connection.
"""
if wallet and (provider_uri or signer_uri or signer):
raise ValueError("If pass a wallet, don't pass a signer, provider URI, or signer URI also.")
# We use Wallet internally as an account management abstraction
if not wallet:
@ -115,9 +118,6 @@ def select_client_account(emitter,
wallet = Wallet(provider_uri=provider_uri, signer=signer)
elif provider_uri or signer_uri:
raise ValueError("If you input a wallet, don't pass a provider URI or signer URI too")
# Display accounts info
if show_nu_balance or show_staking: # Lazy registry fetching
if not registry:

View File

@ -1,3 +1,4 @@
import click
from unittest.mock import Mock
import pytest
@ -5,66 +6,119 @@ from eth_utils import is_checksum_address
from web3 import Web3
from nucypher.blockchain.eth.actors import Wallet
from nucypher.blockchain.eth.clients import EthereumClient, EthereumTesterClient
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.token import NU
from nucypher.cli.actions.select import select_client_account
from nucypher.cli.literature import NO_ETH_ACCOUNTS
from nucypher.config.constants import TEMPORARY_DOMAIN
from tests.constants import MOCK_PROVIDER_URI, MOCK_SIGNER_URI, NUMBER_OF_ETH_TEST_ACCOUNTS
def test_select_client_account(mock_click_prompt, test_emitter, mock_testerchain):
@pytest.mark.parametrize('selection', range(NUMBER_OF_ETH_TEST_ACCOUNTS))
def test_select_client_account(mock_click_prompt, test_emitter, mock_testerchain, selection):
"""Fine-grained assertions about the return value of interactive client account selection"""
selection = 0
mock_click_prompt.return_value = selection
expected_account = mock_testerchain.client.accounts[selection]
selected_account = select_client_account(emitter=test_emitter, provider_uri=MOCK_PROVIDER_URI)
assert selected_account, "Account selection returned Falsy instead of an address"
assert isinstance(selected_account, str), "Selection is not a str"
assert is_checksum_address(selected_account), "Selection is not a valid checksum address"
assert selected_account == mock_testerchain.etherbase_account, "Selection returned the wrong address"
assert selected_account == expected_account, "Selection returned the wrong address"
def test_select_client_account_invalid_input(mock_click_prompt, test_emitter, mock_testerchain):
def test_select_client_account_with_no_accounts(mocker,
mock_click_prompt,
test_emitter,
mock_testerchain,
stdout_trap):
mocker.patch.object(EthereumClient, 'accounts', return_value=[])
with pytest.raises(click.Abort):
select_client_account(emitter=test_emitter, provider_uri=MOCK_PROVIDER_URI)
output = stdout_trap.getvalue()
assert NO_ETH_ACCOUNTS in output
def test_select_client_account_ambiguous_source(mock_click_prompt, test_emitter, mock_testerchain):
#
# Implicit wallet
#
# Provider URI Problems
error_message = "At least a provider URI or signer URI is necessary to select an account"
with pytest.raises(ValueError, match=error_message):
select_client_account(emitter=test_emitter)
# Signer Problems
error_message = "Pass either signer or signer_uri but not both."
with pytest.raises(ValueError, match=error_message):
select_client_account(emitter=test_emitter, signer=Mock(), signer_uri=MOCK_SIGNER_URI)
#
# Explicit wallet
#
def test_select_client_account_valid_inputs(mock_click_prompt,
test_emitter,
mock_testerchain,
patch_keystore,
mock_accounts):
selection = 0
error_message = "If pass a wallet, don't pass a signer, provider URI, or signer URI also."
with pytest.raises(ValueError, match=error_message):
select_client_account(emitter=test_emitter,
signer_uri=Mock(),
wallet=Mock())
with pytest.raises(ValueError, match=error_message):
select_client_account(emitter=test_emitter,
signer=Mock(),
wallet=Mock())
with pytest.raises(ValueError, match=error_message):
select_client_account(emitter=test_emitter,
provider_uri=Mock(),
wallet=Mock())
@pytest.mark.parametrize('selection', range(NUMBER_OF_ETH_TEST_ACCOUNTS))
def test_select_client_account_valid_sources(mocker,
mock_click_prompt,
test_emitter,
mock_testerchain,
patch_keystore,
mock_accounts,
selection):
# Setup
mock_click_prompt.return_value = selection
# From Provider
selected_account = select_client_account(emitter=test_emitter, provider_uri=MOCK_PROVIDER_URI)
assert selected_account == mock_testerchain.etherbase_account
# From Wallet
wallet = Wallet(provider_uri=MOCK_PROVIDER_URI)
selected_account = select_client_account(emitter=test_emitter, wallet=wallet)
assert selected_account == mock_testerchain.etherbase_account
# From External Signer
selected_account = select_client_account(emitter=test_emitter, signer_uri=MOCK_SIGNER_URI)
signer_etherbase_keystore = list(mock_accounts.items())[0]
signer_etherbase_keystore = list(mock_accounts.items())[selection]
_filename, signer_etherbase_account = signer_etherbase_keystore
assert selected_account == signer_etherbase_account.address
expected_account = signer_etherbase_account.address
assert selected_account == expected_account
# From Wallet
expected_account = mock_testerchain.client.accounts[selection]
wallet = Wallet(provider_uri=MOCK_PROVIDER_URI)
selected_account = select_client_account(emitter=test_emitter, wallet=wallet)
assert selected_account == expected_account
# From pre-initialized Provider
expected_account = mock_testerchain.client.accounts[selection]
selected_account = select_client_account(emitter=test_emitter, provider_uri=MOCK_PROVIDER_URI)
assert selected_account == expected_account
# From uninitialized Provider
mocker.patch.object(BlockchainInterfaceFactory, 'is_interface_initialized', return_value=False)
mocker.patch.object(BlockchainInterfaceFactory, '_interfaces', return_value={})
mocker.patch.object(BlockchainInterfaceFactory, 'get_interface', return_value=mock_testerchain)
selected_account = select_client_account(emitter=test_emitter, provider_uri=MOCK_PROVIDER_URI)
assert selected_account == expected_account
@pytest.mark.parametrize('selection,show_staking,show_eth,show_tokens,mock_stakes',(
@pytest.mark.parametrize('selection,show_staking,show_eth,show_tokens,mock_stakes', (
(0, True, True, True, []),
(1, True, True, True, []),
(5, True, True, True, []),
(NUMBER_OF_ETH_TEST_ACCOUNTS-1, True, True, True, []),
(4, True, True, True, [(1, 2, 3)]),
(7, True, True, True, [(1, 2, 3), (1, 2, 3)]),
(0, False, True, True, []),
(0, False, False, True, []),
(0, False, False, False, []),
@ -82,6 +136,7 @@ def test_select_client_account_with_balance_display(mock_click_prompt,
show_tokens,
mock_stakes):
# Setup
mock_click_prompt.return_value = selection
# Missing network kwarg with balance display active

View File

@ -23,13 +23,13 @@ from eth_account.account import Account
from io import StringIO
from nucypher.blockchain.economics import EconomicsFactory
from nucypher.blockchain.eth import KeystoreSigner
from nucypher.blockchain.eth.agents import ContractAgency
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.signers import KeystoreSigner
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.config.characters import UrsulaConfiguration
from tests.constants import KEYFILE_NAME_TEMPLATE, MOCK_KEYSTORE_PATH, NUMBER_OF_ETH_TEST_ACCOUNTS
from tests.constants import KEYFILE_NAME_TEMPLATE, MOCK_KEYSTORE_PATH, NUMBER_OF_MOCK_KEYSTORE_ACCOUNTS
from tests.fixtures import _make_testerchain, make_token_economics
from tests.mock.agents import FAKE_RECEIPT, MockContractAgency, MockNucypherToken, MockStakingAgent, MockWorkLockAgent
from tests.mock.interfaces import MockBlockchain, make_mock_registry_source_manager
@ -151,7 +151,7 @@ def mock_contract_agency(module_mocker, token_economics):
@pytest.fixture(scope='module')
def mock_accounts():
accounts = dict()
for i in range(NUMBER_OF_ETH_TEST_ACCOUNTS):
for i in range(NUMBER_OF_MOCK_KEYSTORE_ACCOUNTS):
account = Account.create()
filename = KEYFILE_NAME_TEMPLATE.format(month=i+1, address=account.address)
accounts[filename] = account

View File

@ -50,7 +50,7 @@ MOCK_KEYSTORE_PATH = '/home/fakeMcfakeson/.ethereum/llamanet/keystore/'
MOCK_SIGNER_URI = f'keystore://{MOCK_KEYSTORE_PATH}'
NUMBER_OF_MOCK_ACCOUNTS = 3
NUMBER_OF_MOCK_KEYSTORE_ACCOUNTS = NUMBER_OF_ETH_TEST_ACCOUNTS
#