2020-05-02 23:42:14 +00:00
|
|
|
"""
|
|
|
|
This file is part of nucypher.
|
|
|
|
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
nucypher is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
"""
|
2020-05-13 14:53:11 +00:00
|
|
|
|
2020-07-14 12:55:07 +00:00
|
|
|
import lmdb
|
2020-05-14 04:14:44 +00:00
|
|
|
import os
|
2020-05-02 23:42:14 +00:00
|
|
|
import pytest
|
2020-09-11 12:34:51 +00:00
|
|
|
import threading
|
2020-06-26 09:33:55 +00:00
|
|
|
from contextlib import contextmanager
|
2020-05-14 04:14:44 +00:00
|
|
|
from eth_account.account import Account
|
2020-07-14 12:55:07 +00:00
|
|
|
from functools import partial
|
2020-05-02 23:42:14 +00:00
|
|
|
|
|
|
|
from nucypher.blockchain.economics import EconomicsFactory
|
2020-05-21 22:39:01 +00:00
|
|
|
from nucypher.blockchain.eth.agents import (
|
|
|
|
AdjudicatorAgent,
|
|
|
|
ContractAgency,
|
|
|
|
MultiSigAgent,
|
|
|
|
NucypherTokenAgent,
|
|
|
|
PolicyManagerAgent,
|
|
|
|
StakingEscrowAgent,
|
|
|
|
WorkLockAgent
|
|
|
|
)
|
2020-10-19 19:42:44 +00:00
|
|
|
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
2020-05-05 22:21:25 +00:00
|
|
|
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
2020-05-14 21:59:53 +00:00
|
|
|
from nucypher.blockchain.eth.signers import KeystoreSigner
|
2020-06-16 15:23:28 +00:00
|
|
|
from nucypher.config.characters import UrsulaConfiguration, StakeHolderConfiguration
|
2020-05-15 05:57:05 +00:00
|
|
|
from tests.constants import (
|
|
|
|
KEYFILE_NAME_TEMPLATE,
|
|
|
|
MOCK_KEYSTORE_PATH,
|
|
|
|
MOCK_PROVIDER_URI,
|
|
|
|
NUMBER_OF_MOCK_KEYSTORE_ACCOUNTS
|
|
|
|
)
|
2020-10-19 19:42:44 +00:00
|
|
|
from tests.fixtures import make_token_economics
|
2020-05-19 21:54:56 +00:00
|
|
|
from tests.mock.agents import MockContractAgency, MockContractAgent
|
2020-05-22 01:51:02 +00:00
|
|
|
from tests.mock.interfaces import MockBlockchain, mock_registry_source_manager
|
2020-05-24 20:23:49 +00:00
|
|
|
from tests.mock.io import MockStdinWrapper
|
2020-05-22 01:51:02 +00:00
|
|
|
from tests.utils.config import (
|
|
|
|
make_alice_test_configuration,
|
|
|
|
make_bob_test_configuration,
|
|
|
|
make_ursula_test_configuration
|
|
|
|
)
|
2020-05-17 17:23:49 +00:00
|
|
|
from tests.utils.ursula import MOCK_URSULA_STARTING_PORT
|
2020-05-05 22:21:25 +00:00
|
|
|
|
2020-05-02 23:42:14 +00:00
|
|
|
|
2020-07-14 12:55:07 +00:00
|
|
|
class TestLMDBEnv:
|
2020-06-26 09:33:55 +00:00
|
|
|
"""
|
2020-07-14 12:55:07 +00:00
|
|
|
This class is used to have LMDB environments open just-in-time as they're
|
|
|
|
needed.
|
|
|
|
|
|
|
|
This is necessary in testing environments because there may be too many
|
|
|
|
LMDB environments open at once.
|
2020-06-26 09:33:55 +00:00
|
|
|
"""
|
2020-08-10 13:35:14 +00:00
|
|
|
__test__ = False # Prohibit pytest from collecting this
|
2020-06-26 09:33:55 +00:00
|
|
|
|
2020-07-14 12:55:07 +00:00
|
|
|
LMDB_OPEN_FUNC = lmdb.open
|
2020-09-15 10:56:33 +00:00
|
|
|
THREAD_LOCK = threading.Lock()
|
2020-07-14 12:55:07 +00:00
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.db_path = args[0]
|
|
|
|
self.open = partial(self.LMDB_OPEN_FUNC, *args, **kwargs)
|
2020-06-26 09:33:55 +00:00
|
|
|
|
|
|
|
@contextmanager
|
2020-07-14 12:55:07 +00:00
|
|
|
def begin(self, *args, **kwargs):
|
2020-06-26 09:33:55 +00:00
|
|
|
try:
|
2020-09-15 10:56:33 +00:00
|
|
|
with self.THREAD_LOCK:
|
2020-09-11 14:29:22 +00:00
|
|
|
with self.open() as lmdb_env:
|
|
|
|
with lmdb_env.begin(*args, **kwargs) as lmdb_tx:
|
|
|
|
yield lmdb_tx
|
2020-06-26 09:33:55 +00:00
|
|
|
finally:
|
2020-09-11 14:29:22 +00:00
|
|
|
pass
|
2020-07-14 12:55:07 +00:00
|
|
|
|
|
|
|
def path(self):
|
|
|
|
return self.db_path
|
|
|
|
|
2020-08-26 00:54:07 +00:00
|
|
|
|
2020-07-14 12:55:07 +00:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def JIT_lmdb_env(monkeypatch):
|
|
|
|
monkeypatch.setattr("lmdb.open", TestLMDBEnv)
|
2020-06-26 09:33:55 +00:00
|
|
|
|
2020-08-26 00:54:07 +00:00
|
|
|
|
2020-05-19 22:25:34 +00:00
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
2020-05-14 23:26:35 +00:00
|
|
|
def mock_contract_agency(monkeypatch, module_mocker, token_economics):
|
|
|
|
monkeypatch.setattr(ContractAgency, 'get_agent', MockContractAgency.get_agent)
|
2020-05-14 19:00:36 +00:00
|
|
|
module_mocker.patch.object(EconomicsFactory, 'get_economics', return_value=token_economics)
|
2020-05-19 22:25:34 +00:00
|
|
|
mock_agency = MockContractAgency()
|
|
|
|
yield mock_agency
|
|
|
|
mock_agency.reset()
|
2020-05-14 04:14:44 +00:00
|
|
|
|
|
|
|
|
2020-05-19 22:25:34 +00:00
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
2020-05-14 21:16:30 +00:00
|
|
|
def mock_token_agent(mock_testerchain, token_economics, mock_contract_agency):
|
2020-07-22 15:47:06 +00:00
|
|
|
mock_agent = mock_contract_agency.get_agent(NucypherTokenAgent)
|
|
|
|
yield mock_agent
|
|
|
|
mock_agent.reset()
|
2020-05-19 22:25:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
|
|
|
def mock_staking_agent(mock_testerchain, token_economics, mock_contract_agency):
|
|
|
|
mock_agent = mock_contract_agency.get_agent(StakingEscrowAgent)
|
2020-06-16 14:58:19 +00:00
|
|
|
yield mock_agent
|
|
|
|
mock_agent.reset()
|
2020-05-19 22:25:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
|
|
|
def mock_adjudicator_agent(mock_testerchain, token_economics, mock_contract_agency):
|
|
|
|
mock_agent = mock_contract_agency.get_agent(AdjudicatorAgent)
|
2020-06-16 14:58:19 +00:00
|
|
|
yield mock_agent
|
|
|
|
mock_agent.reset()
|
2020-05-19 22:25:34 +00:00
|
|
|
|
2020-05-14 21:16:30 +00:00
|
|
|
|
2020-05-19 22:25:34 +00:00
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
|
|
|
def mock_policy_manager_agent(mock_testerchain, token_economics, mock_contract_agency):
|
|
|
|
mock_agent = mock_contract_agency.get_agent(PolicyManagerAgent)
|
2020-06-16 14:58:19 +00:00
|
|
|
yield mock_agent
|
|
|
|
mock_agent.reset()
|
2020-05-14 21:16:30 +00:00
|
|
|
|
2020-05-19 22:25:34 +00:00
|
|
|
|
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
|
|
|
def mock_multisig_agent(mock_testerchain, token_economics, mock_contract_agency):
|
|
|
|
mock_agent = mock_contract_agency.get_agent(MultiSigAgent)
|
2020-06-16 14:58:19 +00:00
|
|
|
yield mock_agent
|
|
|
|
mock_agent.reset()
|
2020-05-19 22:25:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='function', autouse=True)
|
2020-05-14 04:14:44 +00:00
|
|
|
def mock_worklock_agent(mock_testerchain, token_economics, mock_contract_agency):
|
2020-05-19 19:30:41 +00:00
|
|
|
economics = token_economics
|
2020-05-20 18:14:15 +00:00
|
|
|
|
2020-05-19 19:30:41 +00:00
|
|
|
mock_agent = mock_contract_agency.get_agent(WorkLockAgent)
|
2020-05-20 18:14:15 +00:00
|
|
|
|
2020-05-19 19:30:41 +00:00
|
|
|
# Customize the mock agent
|
2020-05-20 18:14:15 +00:00
|
|
|
mock_agent.boosting_refund = economics.worklock_boosting_refund_rate
|
2020-05-13 17:18:32 +00:00
|
|
|
mock_agent.slowing_refund = 100
|
2020-05-20 18:14:15 +00:00
|
|
|
mock_agent.start_bidding_date = economics.bidding_start_date
|
|
|
|
mock_agent.end_bidding_date = economics.bidding_end_date
|
|
|
|
mock_agent.end_cancellation_date = economics.cancellation_end_date
|
|
|
|
mock_agent.minimum_allowed_bid = economics.worklock_min_allowed_bid
|
|
|
|
mock_agent.lot_value = economics.worklock_supply
|
|
|
|
|
2020-05-14 04:14:44 +00:00
|
|
|
yield mock_agent
|
|
|
|
mock_agent.reset()
|
|
|
|
|
|
|
|
|
2020-05-19 22:25:34 +00:00
|
|
|
@pytest.fixture(scope='function')
|
2020-05-24 20:23:49 +00:00
|
|
|
def mock_stdin(mocker):
|
2020-05-14 01:32:54 +00:00
|
|
|
|
2020-05-24 20:23:49 +00:00
|
|
|
mock = MockStdinWrapper()
|
2020-05-14 01:32:54 +00:00
|
|
|
|
2020-05-24 20:23:49 +00:00
|
|
|
mocker.patch('sys.stdin', new=mock.mock_stdin)
|
|
|
|
mocker.patch('getpass.getpass', new=mock.mock_getpass)
|
|
|
|
|
|
|
|
yield mock
|
|
|
|
|
|
|
|
# Sanity check.
|
|
|
|
# The user is encouraged to `assert mock_stdin.empty()` explicitly in the test
|
|
|
|
# right after the input-consuming function call.
|
|
|
|
assert mock.empty(), "Stdin mock was not empty on teardown - some unclaimed input remained"
|
2020-05-14 01:32:54 +00:00
|
|
|
|
|
|
|
|
2020-05-05 22:21:25 +00:00
|
|
|
@pytest.fixture(scope='module', autouse=True)
|
2020-10-19 09:24:10 +00:00
|
|
|
def mock_testerchain(_mock_testerchain) -> MockBlockchain:
|
|
|
|
yield _mock_testerchain
|
2020-05-07 20:47:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def token_economics(mock_testerchain):
|
|
|
|
return make_token_economics(blockchain=mock_testerchain)
|
|
|
|
|
2020-05-02 23:42:14 +00:00
|
|
|
|
2020-05-04 22:00:45 +00:00
|
|
|
@pytest.fixture(scope='module', autouse=True)
|
|
|
|
def mock_interface(module_mocker):
|
|
|
|
mock_transaction_sender = module_mocker.patch.object(BlockchainInterface, 'sign_and_broadcast_transaction')
|
2020-05-19 21:54:56 +00:00
|
|
|
mock_transaction_sender.return_value = MockContractAgent.FAKE_RECEIPT
|
2020-05-02 23:42:14 +00:00
|
|
|
return mock_transaction_sender
|
|
|
|
|
|
|
|
|
2020-05-13 14:53:11 +00:00
|
|
|
@pytest.fixture(scope='module')
|
2020-05-05 22:21:25 +00:00
|
|
|
def test_registry():
|
|
|
|
registry = InMemoryContractRegistry()
|
|
|
|
return registry
|
|
|
|
|
|
|
|
|
2020-05-07 22:03:52 +00:00
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def test_registry_source_manager(mock_testerchain, test_registry):
|
2020-05-22 01:51:02 +00:00
|
|
|
with mock_registry_source_manager(blockchain=mock_testerchain,
|
|
|
|
test_registry=test_registry,
|
|
|
|
mock_backend=True) as real_inventory:
|
|
|
|
yield real_inventory
|
2020-05-07 22:03:52 +00:00
|
|
|
|
|
|
|
|
2020-05-04 07:52:03 +00:00
|
|
|
@pytest.fixture(scope='module', autouse=True)
|
|
|
|
def mock_contract_agency(module_mocker, token_economics):
|
|
|
|
|
|
|
|
# Patch
|
2020-05-02 23:42:14 +00:00
|
|
|
module_mocker.patch.object(EconomicsFactory, 'get_economics', return_value=token_economics)
|
2020-05-04 07:52:03 +00:00
|
|
|
|
2020-05-04 08:55:59 +00:00
|
|
|
# Monkeypatch # TODO: Use better tooling for this monkeypatch?
|
2020-05-04 07:52:03 +00:00
|
|
|
get_agent = ContractAgency.get_agent
|
|
|
|
get_agent_by_name = ContractAgency.get_agent_by_contract_name
|
|
|
|
ContractAgency.get_agent = MockContractAgency.get_agent
|
|
|
|
ContractAgency.get_agent_by_contract_name = MockContractAgency.get_agent_by_contract_name
|
|
|
|
|
|
|
|
# Test
|
|
|
|
yield MockContractAgency()
|
|
|
|
|
|
|
|
# Restore the monkey patching
|
|
|
|
ContractAgency.get_agent = get_agent
|
|
|
|
ContractAgency.get_agent_by_contract_name = get_agent_by_name
|
2020-05-14 04:14:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def mock_accounts():
|
|
|
|
accounts = dict()
|
2020-05-14 21:59:53 +00:00
|
|
|
for i in range(NUMBER_OF_MOCK_KEYSTORE_ACCOUNTS):
|
2020-05-14 04:14:44 +00:00
|
|
|
account = Account.create()
|
|
|
|
filename = KEYFILE_NAME_TEMPLATE.format(month=i+1, address=account.address)
|
|
|
|
accounts[filename] = account
|
|
|
|
return accounts
|
|
|
|
|
|
|
|
|
2020-05-15 05:57:05 +00:00
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def mock_account(mock_accounts):
|
|
|
|
return list(mock_accounts.items())[0][1]
|
|
|
|
|
|
|
|
|
2020-05-14 04:14:44 +00:00
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def worker_account(mock_accounts, mock_testerchain):
|
|
|
|
account = list(mock_accounts.values())[0]
|
|
|
|
return account
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def worker_address(worker_account):
|
|
|
|
address = worker_account.address
|
|
|
|
return address
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
|
|
def custom_config_filepath(custom_filepath):
|
|
|
|
filepath = os.path.join(custom_filepath, UrsulaConfiguration.generate_filename())
|
|
|
|
return filepath
|
2020-05-14 21:16:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='function')
|
|
|
|
def patch_keystore(mock_accounts, monkeypatch, mocker):
|
|
|
|
def successful_mock_keyfile_reader(_keystore, path):
|
|
|
|
|
|
|
|
# Ensure the absolute path is passed to the keyfile reader
|
|
|
|
assert MOCK_KEYSTORE_PATH in path
|
|
|
|
full_path = path
|
|
|
|
del path
|
|
|
|
|
|
|
|
for filename, account in mock_accounts.items(): # Walk the mock filesystem
|
|
|
|
if filename in full_path:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise FileNotFoundError(f"No such file {full_path}")
|
|
|
|
return account.address, dict(version=3, address=account.address)
|
|
|
|
|
|
|
|
mocker.patch('os.listdir', return_value=list(mock_accounts.keys()))
|
2020-05-21 05:18:32 +00:00
|
|
|
monkeypatch.setattr(KeystoreSigner, '_KeystoreSigner__read_keystore', successful_mock_keyfile_reader)
|
2020-05-14 21:16:30 +00:00
|
|
|
yield
|
2020-05-21 05:18:32 +00:00
|
|
|
monkeypatch.delattr(KeystoreSigner, '_KeystoreSigner__read_keystore')
|
|
|
|
|
|
|
|
|
2020-06-16 15:23:28 +00:00
|
|
|
@pytest.fixture(scope='function')
|
|
|
|
def patch_stakeholder_configuration(mock_accounts, monkeypatch):
|
|
|
|
|
|
|
|
def mock_read_configuration_file(filepath: str) -> dict:
|
|
|
|
return dict()
|
|
|
|
|
|
|
|
monkeypatch.setattr(StakeHolderConfiguration, '_read_configuration_file', mock_read_configuration_file)
|
|
|
|
yield
|
|
|
|
monkeypatch.delattr(StakeHolderConfiguration, '_read_configuration_file')
|
|
|
|
|
|
|
|
|
2020-05-21 05:18:32 +00:00
|
|
|
@pytest.fixture(scope='function')
|
|
|
|
def mock_keystore(mocker):
|
|
|
|
mocker.patch.object(KeystoreSigner, '_KeystoreSigner__read_keystore')
|
2020-05-15 05:57:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
2020-05-17 17:23:49 +00:00
|
|
|
def alice_blockchain_test_config(mock_testerchain, test_registry):
|
|
|
|
config = make_alice_test_configuration(federated=False,
|
|
|
|
provider_uri=MOCK_PROVIDER_URI,
|
|
|
|
test_registry=test_registry,
|
|
|
|
checksum_address=mock_testerchain.alice_account)
|
|
|
|
yield config
|
|
|
|
config.cleanup()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
def bob_blockchain_test_config(mock_testerchain, test_registry):
|
|
|
|
config = make_bob_test_configuration(federated=False,
|
|
|
|
provider_uri=MOCK_PROVIDER_URI,
|
|
|
|
test_registry=test_registry,
|
|
|
|
checksum_address=mock_testerchain.bob_account)
|
|
|
|
yield config
|
|
|
|
config.cleanup()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
def ursula_decentralized_test_config(mock_testerchain, test_registry):
|
|
|
|
config = make_ursula_test_configuration(federated=False,
|
|
|
|
provider_uri=MOCK_PROVIDER_URI,
|
|
|
|
test_registry=test_registry,
|
|
|
|
rest_port=MOCK_URSULA_STARTING_PORT,
|
|
|
|
checksum_address=mock_testerchain.ursula_account(index=0))
|
2020-05-15 05:57:05 +00:00
|
|
|
yield config
|
|
|
|
config.cleanup()
|