Compare commits
10 commits
b5fc8e9001
...
b66aa8b158
Author | SHA1 | Date | |
---|---|---|---|
b66aa8b158 | |||
7f5bd0a1cf | |||
0a2bef6192 | |||
c20ed21c2d | |||
9d75604869 | |||
4cd278c60e | |||
1e2ef88c64 | |||
9eddc6953c | |||
14aa8a6d59 | |||
6f4312cb4f |
22 changed files with 312 additions and 46 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,3 +2,5 @@ venv
|
||||||
__pycache__
|
__pycache__
|
||||||
config.json
|
config.json
|
||||||
state.json
|
state.json
|
||||||
|
.coverage
|
||||||
|
test_state.json
|
||||||
|
|
|
@ -61,7 +61,7 @@ All options are optional (but the companion would do nothing).
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 main.py --help
|
python3 companion/main.py --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,12 +69,14 @@ python3 main.py --help
|
||||||
|
|
||||||
Contributions are welcomed! Feel free to update the code and create a pull-request.
|
Contributions are welcomed! Feel free to update the code and create a pull-request.
|
||||||
|
|
||||||
Be sure to lint the code before:
|
Be sure to lint the code and run tests before:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker build -t pre-commit .
|
docker build -t pre-commit .
|
||||||
docker run -it -v $(pwd):/mnt/ --rm pre-commit bash
|
docker run -it -v $(pwd):/mnt/ --rm pre-commit bash
|
||||||
# cd /mnt/
|
# cd /mnt/
|
||||||
|
# pip install -r requirements.txt
|
||||||
# pre-commit run --all-files
|
# pre-commit run --all-files
|
||||||
|
# pytest
|
||||||
# exit
|
# exit
|
||||||
```
|
```
|
||||||
|
|
0
companion/__init__.py
Normal file
0
companion/__init__.py
Normal file
|
@ -79,7 +79,7 @@ def main():
|
||||||
last_balance, last_transaction = handler.watch_miner(address=config['miner'],
|
last_balance, last_transaction = handler.watch_miner(address=config['miner'],
|
||||||
last_balance=pool_state.get('balance'),
|
last_balance=pool_state.get('balance'),
|
||||||
last_transaction=pool_state.get('payment'))
|
last_transaction=pool_state.get('payment'))
|
||||||
if last_balance:
|
if last_balance is not None:
|
||||||
logger.debug(f'saving {pool} miner balance to state file')
|
logger.debug(f'saving {pool} miner balance to state file')
|
||||||
state.write(pool_name=pool, miner_balance=last_balance)
|
state.write(pool_name=pool, miner_balance=last_balance)
|
||||||
if last_transaction:
|
if last_transaction:
|
|
@ -30,22 +30,20 @@ class Handler:
|
||||||
return miner.raw_balance
|
return miner.raw_balance
|
||||||
|
|
||||||
def _watch_miner_payments(self, miner, last_transaction=None):
|
def _watch_miner_payments(self, miner, last_transaction=None):
|
||||||
if miner.last_transaction and miner.last_transaction.txid != last_transaction:
|
logger.debug('watching miner payments')
|
||||||
logger.debug('watching miner payments')
|
if miner.last_transaction and (not last_transaction or miner.last_transaction.txid != last_transaction):
|
||||||
# send notifications for recent payements only
|
# send notifications for last payment only
|
||||||
for transaction in miner.transactions[MAX_NOTIFICATIONS_COUNT:]:
|
logger.info(f'new payment {miner.last_transaction.txid}')
|
||||||
if not last_transaction or transaction.txid > last_transaction:
|
if self.notifier:
|
||||||
logger.info(f'new payment {transaction.txid}')
|
logger.debug('sending payment notification')
|
||||||
if self.notifier:
|
arguments = {'pool': self.pool_name, 'address': miner.address, 'txid': miner.last_transaction.txid,
|
||||||
logger.debug('sending payment notification')
|
'amount': miner.last_transaction.amount, 'amount_fiat': miner.last_transaction.amount_fiat,
|
||||||
arguments = {'pool': self.pool_name, 'address': miner.address, 'txid': transaction.txid,
|
'time': miner.last_transaction.time, 'duration': miner.last_transaction.duration}
|
||||||
'amount': transaction.amount, 'amount_fiat': transaction.amount_fiat,
|
try:
|
||||||
'time': transaction.time, 'duration': transaction.duration}
|
self.notifier.notify_payment(**arguments)
|
||||||
try:
|
logger.info('payment notification sent')
|
||||||
self.notifier.notify_payment(**arguments)
|
except Exception as err:
|
||||||
logger.info('payment notification sent')
|
logger.error('failed to send notification')
|
||||||
except Exception as err:
|
logger.exception(err)
|
||||||
logger.error('failed to send notification')
|
|
||||||
logger.exception(err)
|
|
||||||
if miner.last_transaction and miner.last_transaction.txid:
|
if miner.last_transaction and miner.last_transaction.txid:
|
||||||
return miner.last_transaction.txid
|
return miner.last_transaction.txid
|
|
@ -40,12 +40,13 @@ class Miner:
|
||||||
miner = flexpoolapi.miner(address)
|
miner = flexpoolapi.miner(address)
|
||||||
self.raw_balance = miner.balance()
|
self.raw_balance = miner.balance()
|
||||||
self.balance = convert_weis(self.raw_balance)
|
self.balance = convert_weis(self.raw_balance)
|
||||||
|
self.balance_fiat = None
|
||||||
if exchange_rate and currency:
|
if exchange_rate and currency:
|
||||||
self.balance_fiat = convert_fiat(amount=self.raw_balance, exchange_rate=exchange_rate, currency=currency)
|
self.balance_fiat = convert_fiat(amount=self.raw_balance, exchange_rate=exchange_rate, currency=currency)
|
||||||
payout_threshold = self.get_payout_threshold(miner)
|
payout_threshold = self.get_payout_threshold(miner)
|
||||||
self.balance_percentage = self.format_balance_percentage(payout_threshold=payout_threshold,
|
self.balance_percentage = self.format_balance_percentage(payout_threshold=payout_threshold,
|
||||||
balance=self.raw_balance)
|
balance=self.raw_balance)
|
||||||
self.transactions = self.get_payements(miner)
|
self.transactions = self.get_payements(miner, exchange_rate=exchange_rate, currency=currency)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
@ -130,7 +131,8 @@ class FlexpoolHandler(Handler):
|
||||||
blocks = self.get_blocks(exchange_rate=self.exchange_rate, currency=self.currency)
|
blocks = self.get_blocks(exchange_rate=self.exchange_rate, currency=self.currency)
|
||||||
if blocks:
|
if blocks:
|
||||||
# don't spam block notification at initialization
|
# don't spam block notification at initialization
|
||||||
for block in blocks[MAX_NOTIFICATIONS_COUNT:]:
|
notification_slice = MAX_NOTIFICATIONS_COUNT if len(blocks) > MAX_NOTIFICATIONS_COUNT else 0
|
||||||
|
for block in blocks[notification_slice:]:
|
||||||
if not last_block or last_block < block.number:
|
if not last_block or last_block < block.number:
|
||||||
logger.info(f'new block {block.number}')
|
logger.info(f'new block {block.number}')
|
||||||
if self.notifier:
|
if self.notifier:
|
||||||
|
@ -144,35 +146,45 @@ class FlexpoolHandler(Handler):
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error('failed to send notification')
|
logger.error('failed to send notification')
|
||||||
logger.exception(err)
|
logger.exception(err)
|
||||||
last_remote_block = block
|
last_remote_block = block
|
||||||
if last_remote_block and last_remote_block.number:
|
if last_remote_block and last_remote_block.number:
|
||||||
return last_remote_block.number
|
return last_remote_block.number
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_blocks(exchange_rate=None, currency=None):
|
def get_blocks(exchange_rate=None, currency=None):
|
||||||
remote_blocks = flexpoolapi.pool.last_blocks(count=MAX_BLOCKS_COUNT)
|
try:
|
||||||
# convert to blocks
|
remote_blocks = flexpoolapi.pool.last_blocks(count=MAX_BLOCKS_COUNT)
|
||||||
blocks = []
|
# convert to blocks
|
||||||
for remote_block in remote_blocks:
|
blocks = []
|
||||||
block = Block(number=remote_block.number, hash=remote_block.hash, time=remote_block.time,
|
if remote_blocks:
|
||||||
round_time=remote_block.round_time, reward=remote_block.total_rewards, luck=remote_block.luck,
|
for remote_block in remote_blocks:
|
||||||
exchange_rate=exchange_rate, currency=currency)
|
block = Block(number=remote_block.number, hash=remote_block.hash, time=remote_block.time,
|
||||||
blocks.append(block)
|
round_time=remote_block.round_time, reward=remote_block.total_rewards,
|
||||||
# sort by block number
|
luck=remote_block.luck, exchange_rate=exchange_rate, currency=currency)
|
||||||
return sorted(blocks)
|
blocks.append(block)
|
||||||
|
# sort by block number
|
||||||
|
return sorted(blocks)
|
||||||
|
except flexpoolapi.exceptions.APIError as err:
|
||||||
|
logger.warning('failed to get blocks from Flexpool API')
|
||||||
|
logger.debug(err)
|
||||||
|
|
||||||
def watch_miner(self, address, last_balance=None, last_transaction=None):
|
def watch_miner(self, address, last_balance=None, last_transaction=None):
|
||||||
logger.debug(f'watching miner {address}')
|
logger.debug(f'watching miner {address}')
|
||||||
try:
|
try:
|
||||||
miner = Miner(address=address, exchange_rate=self.exchange_rate, currency=self.currency)
|
miner = Miner(address=address, exchange_rate=self.exchange_rate, currency=self.currency)
|
||||||
except Exception as err:
|
logger.debug(miner)
|
||||||
|
|
||||||
|
last_balance = self._watch_miner_balance(miner=miner, last_balance=last_balance)
|
||||||
|
last_transaction = self._watch_miner_payments(miner=miner, last_transaction=last_transaction)
|
||||||
|
|
||||||
|
return last_balance, last_transaction
|
||||||
|
except flexpoolapi.exceptions.InvalidMinerAddress as err:
|
||||||
|
logger.error(f'miner address {address} is invalid')
|
||||||
|
logger.debug(err)
|
||||||
|
except flexpoolapi.exceptions.MinerDoesNotExist as err:
|
||||||
logger.error(f'miner {address} not found')
|
logger.error(f'miner {address} not found')
|
||||||
logger.exception(err)
|
logger.debug(err)
|
||||||
return
|
except flexpoolapi.exceptions.APIError as err:
|
||||||
|
logger.warning('failed to get miner from Flexpool API')
|
||||||
logger.debug(miner)
|
logger.debug(err)
|
||||||
|
return None, None
|
||||||
last_balance = self._watch_miner_balance(miner=miner, last_balance=last_balance)
|
|
||||||
last_transaction = self._watch_miner_payments(miner=miner, last_transaction=last_transaction)
|
|
||||||
|
|
||||||
return last_balance, last_transaction
|
|
|
@ -20,9 +20,9 @@ class State:
|
||||||
content = self.read()
|
content = self.read()
|
||||||
if pool_name not in content:
|
if pool_name not in content:
|
||||||
content[pool_name] = {}
|
content[pool_name] = {}
|
||||||
if block_number:
|
if block_number is not None:
|
||||||
content[pool_name]['block'] = block_number
|
content[pool_name]['block'] = block_number
|
||||||
if miner_balance:
|
if miner_balance is not None:
|
||||||
content[pool_name]['balance'] = miner_balance
|
content[pool_name]['balance'] = miner_balance
|
||||||
if miner_payment:
|
if miner_payment:
|
||||||
content[pool_name]['payment'] = miner_payment
|
content[pool_name]['payment'] = miner_payment
|
|
@ -18,7 +18,7 @@ class TelegramNotifier:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _markdown_escape(text):
|
def _markdown_escape(text):
|
||||||
text = str(text)
|
text = str(text)
|
||||||
for special_char in ['\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '#', '+', '-', '.', '!']:
|
for special_char in ['\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '#', '+', '-', '.', '!', '=']:
|
||||||
text = text.replace(special_char, fr'\{special_char}')
|
text = text.replace(special_char, fr'\{special_char}')
|
||||||
return text
|
return text
|
||||||
|
|
2
pytest.ini
Normal file
2
pytest.ini
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[pytest]
|
||||||
|
addopts = --cov=companion tests/
|
|
@ -1,3 +1,4 @@
|
||||||
-r requirements/base.txt
|
-r requirements/base.txt
|
||||||
-r requirements/ethermine.txt
|
-r requirements/ethermine.txt
|
||||||
-r requirements/flexpool.txt
|
-r requirements/flexpool.txt
|
||||||
|
-r requirements/tests.txt
|
||||||
|
|
3
requirements/tests.txt
Normal file
3
requirements/tests.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pytest==6.2.2
|
||||||
|
pytest-cov==2.11.1
|
||||||
|
pytest-mock==3.5.1
|
4
tests/__init__.py
Normal file
4
tests/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'companion'))
|
142
tests/test_flexpool.py
Normal file
142
tests/test_flexpool.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from companion.pools.flexpool import FlexpoolHandler, Transaction
|
||||||
|
from flexpoolapi.shared import Block as BlockApi
|
||||||
|
|
||||||
|
|
||||||
|
class TestFlexpoolHandler:
|
||||||
|
def test_init(self):
|
||||||
|
handler = FlexpoolHandler()
|
||||||
|
assert handler.pool_name == 'flexpool'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'old_balance,new_balance,should_notify',
|
||||||
|
[
|
||||||
|
pytest.param(1, 2, True, id='new_balance_with_notification'),
|
||||||
|
pytest.param(1, 0, True, id='new_balance_after_payment_with_notification'),
|
||||||
|
pytest.param(None, 1, True, id='very_new_balance_with_notification'),
|
||||||
|
pytest.param(1, 1, False, id='same_balance_without_notification'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_balance(self, mocker, old_balance, new_balance, should_notify):
|
||||||
|
notifier = mocker.Mock()
|
||||||
|
notifier.notify_balance = mocker.Mock()
|
||||||
|
handler = FlexpoolHandler(notifier=notifier)
|
||||||
|
miner = mocker.patch('flexpoolapi.miner')
|
||||||
|
miner().balance.return_value = new_balance
|
||||||
|
mocker.patch('companion.pools.flexpool.FlexpoolHandler._watch_miner_payments')
|
||||||
|
mocker.patch('companion.pools.flexpool.Miner.get_payements')
|
||||||
|
last_balance, last_transaction = handler.watch_miner(address='addr', last_balance=old_balance)
|
||||||
|
assert last_balance == new_balance
|
||||||
|
if should_notify:
|
||||||
|
notifier.notify_balance.assert_called_once()
|
||||||
|
else:
|
||||||
|
notifier.notify_balance.assert_not_called()
|
||||||
|
|
||||||
|
def test_balance_with_api_failure(self, mocker):
|
||||||
|
"""An API failure should not send a balance notification"""
|
||||||
|
notifier = mocker.Mock()
|
||||||
|
notifier.notify_balance = mocker.Mock()
|
||||||
|
handler = FlexpoolHandler(notifier=notifier)
|
||||||
|
request_get = mocker.patch('requests.get')
|
||||||
|
request_get.return_value.status_code = 503
|
||||||
|
mocker.patch('companion.pools.flexpool.FlexpoolHandler._watch_miner_payments')
|
||||||
|
mocker.patch('companion.pools.flexpool.Miner.get_payements')
|
||||||
|
last_balance, last_transaction = handler.watch_miner(address='0000000000000000000000000000000000000001',
|
||||||
|
last_balance=1)
|
||||||
|
assert last_balance is None
|
||||||
|
notifier.notify_balance.assert_not_called()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_transactions(names):
|
||||||
|
if names:
|
||||||
|
return [Transaction(txid=n, amount=1, time=datetime.now(), duration=timedelta(minutes=1)) for n in names]
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'old_transaction,new_transactions,should_notify',
|
||||||
|
[
|
||||||
|
pytest.param('trx1', ['trx1', 'trx2'], True, id='new_payment_with_notification'),
|
||||||
|
pytest.param(None, ['trx1'], True, id='very_new_payment_with_notification'),
|
||||||
|
pytest.param('trx1', ['trx1'], False, id='same_payment_without_notification'),
|
||||||
|
pytest.param(None, None, False, id='zero_payment_without_notification'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_payments(self, mocker, old_transaction, new_transactions, should_notify):
|
||||||
|
notifier = mocker.Mock()
|
||||||
|
notifier.notify_payment = mocker.Mock()
|
||||||
|
handler = FlexpoolHandler(notifier=notifier)
|
||||||
|
mocker.patch('flexpoolapi.miner')
|
||||||
|
mocker.patch('companion.pools.flexpool.FlexpoolHandler._watch_miner_balance')
|
||||||
|
get_payements = mocker.patch('companion.pools.flexpool.Miner.get_payements')
|
||||||
|
get_payements.return_value = self._create_transactions(new_transactions)
|
||||||
|
last_balance, last_transaction = handler.watch_miner(address='addr', last_transaction=old_transaction)
|
||||||
|
if new_transactions:
|
||||||
|
assert last_transaction == new_transactions[-1]
|
||||||
|
else:
|
||||||
|
assert last_transaction is None
|
||||||
|
if should_notify:
|
||||||
|
notifier.notify_payment.assert_called_once()
|
||||||
|
else:
|
||||||
|
notifier.notify_payment.assert_not_called()
|
||||||
|
|
||||||
|
def test_payment_with_api_failure(self, mocker):
|
||||||
|
"""An API failure should not send a payment notification"""
|
||||||
|
notifier = mocker.Mock()
|
||||||
|
notifier.notify_payment = mocker.Mock()
|
||||||
|
handler = FlexpoolHandler(notifier=notifier)
|
||||||
|
request_get = mocker.patch('requests.get')
|
||||||
|
request_get.return_value.status_code = 503
|
||||||
|
mocker.patch('companion.pools.flexpool.FlexpoolHandler._watch_miner_balance')
|
||||||
|
last_balance, last_transaction = handler.watch_miner(address='0000000000000000000000000000000000000001',
|
||||||
|
last_transaction=1)
|
||||||
|
assert last_transaction is None
|
||||||
|
notifier.notify_payment.assert_not_called()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_blocks(numbers):
|
||||||
|
if numbers:
|
||||||
|
blocks = []
|
||||||
|
for number in numbers:
|
||||||
|
blocks.append(BlockApi(number=number, blockhash='h', block_type='bt', miner='m', difficulty=1,
|
||||||
|
timestamp=1, is_confirmed=True, round_time=1, luck=1.0, server_name='s',
|
||||||
|
block_reward=1, block_fees=1, uncle_inclusion_rewards=1, total_rewards=1))
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'last_block,remote_blocks,should_notify',
|
||||||
|
[
|
||||||
|
pytest.param(1, [1, 2], True, id='new_block_with_notification'),
|
||||||
|
pytest.param(None, [1], True, id='very_new_block_with_notification'),
|
||||||
|
pytest.param(1, [1], False, id='same_block_without_notification'),
|
||||||
|
pytest.param(9, range(1, 11), True, id='new_block_with_count_over_max_notification'),
|
||||||
|
pytest.param(10, range(1, 11), False, id='same_block_with_count_over_max_notification'),
|
||||||
|
pytest.param(None, None, False, id='zero_block_without_notification'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_block(self, mocker, last_block, remote_blocks, should_notify):
|
||||||
|
notifier = mocker.Mock()
|
||||||
|
notifier.notify_block = mocker.Mock()
|
||||||
|
handler = FlexpoolHandler(notifier=notifier)
|
||||||
|
last_blocks = mocker.patch('flexpoolapi.pool.last_blocks')
|
||||||
|
last_blocks.return_value = self._create_blocks(remote_blocks)
|
||||||
|
block = handler.watch_blocks(last_block=last_block)
|
||||||
|
if remote_blocks:
|
||||||
|
assert block == remote_blocks[-1]
|
||||||
|
else:
|
||||||
|
assert block is None
|
||||||
|
if should_notify:
|
||||||
|
notifier.notify_block.assert_called_once()
|
||||||
|
else:
|
||||||
|
notifier.notify_block.assert_not_called()
|
||||||
|
|
||||||
|
def test_block_with_api_failure(self, mocker):
|
||||||
|
"""An API failure should not send a block notification"""
|
||||||
|
notifier = mocker.Mock()
|
||||||
|
notifier.notify_block = mocker.Mock()
|
||||||
|
handler = FlexpoolHandler(notifier=notifier)
|
||||||
|
request_get = mocker.patch('requests.get')
|
||||||
|
request_get.return_value.status_code = 503
|
||||||
|
block = handler.watch_blocks(last_block=1)
|
||||||
|
assert block is None
|
||||||
|
notifier.notify_block.assert_not_called()
|
100
tests/test_state.py
Normal file
100
tests/test_state.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from companion.state import State
|
||||||
|
|
||||||
|
|
||||||
|
class TestState:
|
||||||
|
FILENAME = 'test_state.json'
|
||||||
|
POOL_NAME = 'testpool'
|
||||||
|
CONTENT = {
|
||||||
|
'testpool': {
|
||||||
|
'block': 1234,
|
||||||
|
'balance': 1234,
|
||||||
|
'payment': '0x0000000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def state(self):
|
||||||
|
return State(self.FILENAME)
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def create_state(self):
|
||||||
|
with open(self.FILENAME, 'w') as fd:
|
||||||
|
json.dump(self.CONTENT, fd, indent=2)
|
||||||
|
yield
|
||||||
|
if os.path.isfile(self.FILENAME):
|
||||||
|
os.unlink(self.FILENAME)
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def remove_state(self):
|
||||||
|
yield
|
||||||
|
if os.path.isfile(self.FILENAME):
|
||||||
|
os.unlink(self.FILENAME)
|
||||||
|
|
||||||
|
def test_init(self, state, remove_state):
|
||||||
|
assert os.path.isfile(self.FILENAME)
|
||||||
|
with open(self.FILENAME, 'r') as fd:
|
||||||
|
assert json.load(fd) == {}
|
||||||
|
|
||||||
|
def test_read(self, state, create_state):
|
||||||
|
content = state.read()
|
||||||
|
for pool in self.CONTENT:
|
||||||
|
assert pool in content
|
||||||
|
for key in self.CONTENT[pool]:
|
||||||
|
assert key in content[pool] and content[pool][key] == self.CONTENT[pool][key]
|
||||||
|
|
||||||
|
def test_write(self, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME] == {}
|
||||||
|
|
||||||
|
def test_write_block(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, block_number=5678)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['block'] == 5678
|
||||||
|
|
||||||
|
def test_write_empty_block(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, block_number=None)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['block'] == self.CONTENT[self.POOL_NAME]['block'] # not changed
|
||||||
|
|
||||||
|
def test_write_zero_block(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, block_number=0)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['block'] == 0
|
||||||
|
|
||||||
|
def test_write_balance(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, miner_balance=5678)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['balance'] == 5678
|
||||||
|
|
||||||
|
def test_write_empty_balance(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, miner_balance=None)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['balance'] == self.CONTENT[self.POOL_NAME]['balance'] # not changed
|
||||||
|
|
||||||
|
def test_write_zero_balance(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, miner_balance=0)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['balance'] == 0
|
||||||
|
|
||||||
|
def test_write_payment(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, miner_payment='0x1111111')
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['payment'] == '0x1111111'
|
||||||
|
|
||||||
|
def test_write_empty_payment(self, create_state, state):
|
||||||
|
state.write(pool_name=self.POOL_NAME, miner_payment=None)
|
||||||
|
content = state.read()
|
||||||
|
assert content[self.POOL_NAME]['payment'] == self.CONTENT[self.POOL_NAME]['payment'] # not changed
|
||||||
|
|
||||||
|
def test_get(self, create_state):
|
||||||
|
state = State(filename=self.FILENAME)
|
||||||
|
assert state.get(self.POOL_NAME) == self.CONTENT[self.POOL_NAME]
|
||||||
|
|
||||||
|
def test_get_missing_key(self, create_state):
|
||||||
|
state = State(filename=self.FILENAME)
|
||||||
|
assert state.get('UNKNOWN_POOL') == {}
|
Reference in a new issue