mirror of https://github.com/nucypher/nucypher.git
381 lines
14 KiB
Python
381 lines
14 KiB
Python
"""
|
|
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/>.
|
|
"""
|
|
|
|
|
|
import random
|
|
|
|
import pytest
|
|
|
|
from nucypher.blockchain.eth.actors import Bidder
|
|
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
|
from nucypher.blockchain.eth.token import NU
|
|
from nucypher.cli.commands.worklock import worklock
|
|
from tests.utils.constants import (
|
|
TEMPORARY_DOMAIN, MOCK_PROVIDER_URI, YES,
|
|
)
|
|
from tests.cli.functional.test_ursula_local_keystore_cli_functionality import CLI_ENV
|
|
from tests.mock.agents import FAKE_RECEIPT, MockWorkLockAgent
|
|
|
|
|
|
@pytest.fixture(scope='function')
|
|
def mock_worklock_agent(mock_testerchain, token_economics):
|
|
mock_agent = MockWorkLockAgent()
|
|
yield mock_agent
|
|
mock_agent.reset()
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
def surrogate_bidder(mock_testerchain, test_registry):
|
|
address = mock_testerchain.etherbase_account
|
|
bidder = Bidder(checksum_address=address, registry=test_registry)
|
|
return bidder
|
|
|
|
|
|
def assert_successful_transaction_echo(bidder_address: str, cli_output: str):
|
|
expected = (bidder_address,
|
|
FAKE_RECEIPT['blockHash'].hex(),
|
|
FAKE_RECEIPT['blockNumber'],
|
|
FAKE_RECEIPT['transactionHash'].hex())
|
|
for output in expected:
|
|
assert str(output) in cli_output, f'"{output}" not in bidding output'
|
|
|
|
|
|
def test_status(click_runner, mock_worklock_agent, test_registry_source_manager):
|
|
command = ('status', '--provider', MOCK_PROVIDER_URI, '--network', TEMPORARY_DOMAIN)
|
|
result = click_runner.invoke(worklock, command, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
def bidding_command(token_economics, surrogate_bidder):
|
|
minimum = token_economics.worklock_min_allowed_bid
|
|
bid_value = random.randint(minimum, minimum*100)
|
|
command = ('bid',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--value', bid_value,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--force')
|
|
return command
|
|
|
|
|
|
def test_bid_too_soon(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
token_economics,
|
|
test_registry_source_manager,
|
|
surrogate_bidder,
|
|
mock_testerchain,
|
|
bidding_command):
|
|
|
|
# Bidding window is not open yet
|
|
now = mock_testerchain.get_blocktime()
|
|
a_month_too_soon = now-(3600*30)
|
|
mocker.patch.object(BlockchainInterface, 'get_blocktime', return_value=a_month_too_soon)
|
|
with pytest.raises(Bidder.BiddingIsClosed):
|
|
result = click_runner.invoke(worklock, bidding_command, catch_exceptions=False, input=YES, env=CLI_ENV)
|
|
assert result.exit_code != 0
|
|
|
|
|
|
def test_bid_too_late(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
token_economics,
|
|
test_registry_source_manager,
|
|
surrogate_bidder,
|
|
mock_testerchain,
|
|
bidding_command):
|
|
|
|
# Bidding window is closed
|
|
now = mock_testerchain.get_blocktime()
|
|
a_month_too_late = now+(3600*30)
|
|
mocker.patch.object(BlockchainInterface, 'get_blocktime', return_value=a_month_too_late)
|
|
with pytest.raises(Bidder.BiddingIsClosed):
|
|
result = click_runner.invoke(worklock, bidding_command, catch_exceptions=False, input=YES, env=CLI_ENV)
|
|
assert result.exit_code != 0
|
|
|
|
|
|
def test_valid_bid(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
token_economics,
|
|
test_registry_source_manager,
|
|
surrogate_bidder,
|
|
mock_testerchain):
|
|
|
|
now = mock_testerchain.get_blocktime()
|
|
sometime_later = now + 100
|
|
mock_blocktime = mocker.patch.object(BlockchainInterface, 'get_blocktime', return_value=sometime_later)
|
|
|
|
# Spy on the corresponding CLI function we are testing
|
|
mock_ensure = mocker.spy(Bidder, 'ensure_bidding_is_open')
|
|
mock_bidder = mocker.spy(Bidder, 'place_bid')
|
|
|
|
minimum = token_economics.worklock_min_allowed_bid
|
|
bid_value = random.randint(minimum, minimum * 100)
|
|
|
|
command = ('bid',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--value', bid_value,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--force')
|
|
|
|
result = click_runner.invoke(worklock, command, catch_exceptions=False, input=YES, env=CLI_ENV)
|
|
assert result.exit_code == 0
|
|
|
|
# OK - Let's see what happened
|
|
|
|
# Bidder
|
|
mock_ensure.assert_called_once() # checked that the bidding window was open via actors layer
|
|
mock_bidder.assert_called_once()
|
|
nunits = NU.from_tokens(bid_value).to_nunits()
|
|
mock_bidder.assert_called_once_with(surrogate_bidder, value=nunits)
|
|
assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_any_transaction()
|
|
mock_worklock_agent.assert_only_one_transaction_executed()
|
|
mock_worklock_agent.assert_transaction(name='bid', checksum_address=surrogate_bidder.checksum_address, value=nunits)
|
|
|
|
# Calls
|
|
expected_calls = ('get_deposited_eth', 'eth_to_tokens')
|
|
mock_worklock_agent.assert_contract_calls(calls=expected_calls)
|
|
|
|
|
|
def test_cancel_bid(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
|
|
# Spy on the corresponding CLI function we are testing
|
|
mock_cancel = mocker.spy(Bidder, 'cancel_bid')
|
|
|
|
command = ('cancel-bid',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--force')
|
|
result = click_runner.invoke(worklock, command, input=YES, env=CLI_ENV, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
# Bidder
|
|
mock_cancel.assert_called_once()
|
|
assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_any_transaction()
|
|
mock_worklock_agent.assert_only_one_transaction_executed()
|
|
mock_worklock_agent.assert_transaction(name='cancel_bid', checksum_address=surrogate_bidder.checksum_address)
|
|
|
|
# Calls
|
|
expected_calls = ('get_deposited_eth',)
|
|
mock_worklock_agent.assert_contract_calls(calls=expected_calls)
|
|
|
|
|
|
@pytest.mark.skip # TODO
|
|
def test_post_initialization(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
|
|
# Spy on the corresponding CLI function we are testing
|
|
mock_enable = mocker.spy(Bidder, 'enable_claiming')
|
|
|
|
command = ('enable-claiming',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--force',
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--gas-limit', 100000)
|
|
|
|
result = click_runner.invoke(worklock, command, input=YES, env=CLI_ENV, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
# Bidder
|
|
mock_enable.assert_called_once()
|
|
assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_any_transaction()
|
|
mock_worklock_agent.assert_only_one_transaction_executed()
|
|
mock_worklock_agent.assert_transaction(name='enable_claiming', checksum_address=surrogate_bidder.checksum_address)
|
|
|
|
|
|
def test_initial_claim(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
|
|
# Spy on the corresponding CLI function we are testing
|
|
mock_withdraw_compensation = mocker.spy(Bidder, 'withdraw_compensation')
|
|
mock_claim = mocker.spy(Bidder, 'claim')
|
|
|
|
# TODO: Test this functionality in isolation
|
|
mocker.patch.object(Bidder, '_ensure_cancellation_window')
|
|
|
|
# Bidder has not claimed yet
|
|
mocker.patch.object(
|
|
Bidder, 'has_claimed',
|
|
new_callable=mocker.PropertyMock,
|
|
return_value=False
|
|
)
|
|
|
|
command = ('claim',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--force')
|
|
|
|
result = click_runner.invoke(worklock, command, input=YES, env=CLI_ENV, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
mock_worklock_agent.assert_transaction(name='claim', checksum_address=surrogate_bidder.checksum_address)
|
|
|
|
# Bidder
|
|
mock_withdraw_compensation.assert_called_once()
|
|
mock_claim.assert_called_once()
|
|
assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_any_transaction()
|
|
mock_worklock_agent.assert_transaction(name='withdraw_compensation', checksum_address=surrogate_bidder.checksum_address)
|
|
mock_worklock_agent.assert_transaction(name='claim', checksum_address=surrogate_bidder.checksum_address)
|
|
|
|
# Calls
|
|
expected_calls = ('get_deposited_eth', 'eth_to_tokens')
|
|
mock_worklock_agent.assert_contract_calls(calls=expected_calls)
|
|
|
|
|
|
def test_already_claimed(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
|
|
# Spy on the corresponding CLI function we are testing
|
|
mock_withdraw_compensation = mocker.spy(Bidder, 'withdraw_compensation')
|
|
mock_claim = mocker.spy(Bidder, 'claim')
|
|
|
|
# TODO: Test this functionality in isolation
|
|
mocker.patch.object(Bidder, '_ensure_cancellation_window')
|
|
|
|
# Bidder already claimed
|
|
mocker.patch.object(
|
|
Bidder, 'has_claimed',
|
|
new_callable=mocker.PropertyMock,
|
|
return_value=True
|
|
)
|
|
|
|
command = ('claim',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--force')
|
|
|
|
result = click_runner.invoke(worklock, command, input=YES, env=CLI_ENV, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
# Bidder
|
|
mock_withdraw_compensation.assert_called_once()
|
|
assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)
|
|
mock_claim.assert_not_called()
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_any_transaction()
|
|
mock_worklock_agent.assert_transaction(name='withdraw_compensation', checksum_address=surrogate_bidder.checksum_address)
|
|
mock_worklock_agent.assert_transaction_not_called(name='claim')
|
|
|
|
|
|
def test_remaining_work(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
|
|
remaining_work = 100
|
|
mock_remaining_work = mocker.patch.object(Bidder,
|
|
'remaining_work',
|
|
new_callable=mocker.PropertyMock,
|
|
return_value=remaining_work)
|
|
|
|
command = ('remaining-work',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
result = click_runner.invoke(worklock, command, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert str(remaining_work) in result.output, "Remaining work was not echoed."
|
|
|
|
# Bidder
|
|
mock_remaining_work.assert_called_once()
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_no_transactions()
|
|
|
|
|
|
def test_refund(click_runner,
|
|
mocker,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
|
|
# Spy on the corresponding CLI function we are testing
|
|
mock_refund = mocker.spy(Bidder, 'refund_deposit')
|
|
|
|
command = ('refund',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--force')
|
|
|
|
result = click_runner.invoke(worklock, command, input=YES, env=CLI_ENV, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
# Bidder
|
|
mock_refund.assert_called_once()
|
|
assert_successful_transaction_echo(bidder_address=surrogate_bidder.checksum_address, cli_output=result.output)
|
|
|
|
# Transactions
|
|
mock_worklock_agent.assert_any_transaction()
|
|
mock_worklock_agent.assert_only_one_transaction_executed()
|
|
mock_worklock_agent.assert_transaction(name='refund', checksum_address=surrogate_bidder.checksum_address)
|
|
|
|
|
|
def test_participant_status(click_runner,
|
|
mock_worklock_agent,
|
|
surrogate_bidder):
|
|
command = ('status',
|
|
'--bidder-address', surrogate_bidder.checksum_address,
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
result = click_runner.invoke(worklock, command, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
expected_calls = ('check_claim',
|
|
'eth_to_tokens',
|
|
'get_deposited_eth',
|
|
'get_eth_supply',
|
|
'get_base_deposit_rate',
|
|
'get_bonus_lot_value',
|
|
'get_bonus_deposit_rate',
|
|
'get_bonus_refund_rate',
|
|
'get_base_refund_rate',
|
|
'get_completed_work',
|
|
'get_refunded_work')
|
|
# Calls
|
|
mock_worklock_agent.assert_contract_calls(calls=expected_calls)
|