Merge pull request #4469 from freqtrade/rpc/locks
Add RPC methods to remove locks
This commit is contained in:
commit
6eb253c31e
@ -131,6 +131,7 @@ python3 scripts/rest_client.py --config rest_config.json <command> [optional par
|
|||||||
| `status` | Lists all open trades.
|
| `status` | Lists all open trades.
|
||||||
| `count` | Displays number of trades used and available.
|
| `count` | Displays number of trades used and available.
|
||||||
| `locks` | Displays currently locked pairs.
|
| `locks` | Displays currently locked pairs.
|
||||||
|
| `delete_lock <lock_id>` | Deletes (disables) the lock by id.
|
||||||
| `profit` | Display a summary of your profit/loss from close trades and some stats about your performance.
|
| `profit` | Display a summary of your profit/loss from close trades and some stats about your performance.
|
||||||
| `forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`).
|
| `forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`).
|
||||||
| `forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`).
|
| `forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`).
|
||||||
@ -182,6 +183,11 @@ count
|
|||||||
daily
|
daily
|
||||||
Return the amount of open trades.
|
Return the amount of open trades.
|
||||||
|
|
||||||
|
delete_lock
|
||||||
|
Delete (disable) lock from the database.
|
||||||
|
|
||||||
|
:param lock_id: ID for the lock to delete
|
||||||
|
|
||||||
delete_trade
|
delete_trade
|
||||||
Delete trade from the database.
|
Delete trade from the database.
|
||||||
Tries to close open orders. Requires manual handling of this asset on the exchange.
|
Tries to close open orders. Requires manual handling of this asset on the exchange.
|
||||||
@ -202,6 +208,9 @@ forcesell
|
|||||||
|
|
||||||
:param tradeid: Id of the trade (can be received via status command)
|
:param tradeid: Id of the trade (can be received via status command)
|
||||||
|
|
||||||
|
locks
|
||||||
|
Return current locks
|
||||||
|
|
||||||
logs
|
logs
|
||||||
Show latest logs.
|
Show latest logs.
|
||||||
|
|
||||||
|
@ -146,6 +146,7 @@ official commands. You can ask at any moment for help with `/help`.
|
|||||||
| `/delete <trade_id>` | Delete a specific trade from the Database. Tries to close open orders. Requires manual handling of this trade on the exchange.
|
| `/delete <trade_id>` | Delete a specific trade from the Database. Tries to close open orders. Requires manual handling of this trade on the exchange.
|
||||||
| `/count` | Displays number of trades used and available
|
| `/count` | Displays number of trades used and available
|
||||||
| `/locks` | Show currently locked pairs.
|
| `/locks` | Show currently locked pairs.
|
||||||
|
| `/unlock <pair | lock_id>` | Remove the lock for this pair (or for this lock id).
|
||||||
| `/profit` | Display a summary of your profit/loss from close trades and some stats about your performance
|
| `/profit` | Display a summary of your profit/loss from close trades and some stats about your performance
|
||||||
| `/forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`).
|
| `/forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`).
|
||||||
| `/forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`).
|
| `/forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`).
|
||||||
|
@ -765,6 +765,7 @@ class PairLock(_DECL_BASE):
|
|||||||
|
|
||||||
def to_json(self) -> Dict[str, Any]:
|
def to_json(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
|
'id': self.id,
|
||||||
'pair': self.pair,
|
'pair': self.pair,
|
||||||
'lock_time': self.lock_time.strftime(DATETIME_PRINT_FORMAT),
|
'lock_time': self.lock_time.strftime(DATETIME_PRINT_FORMAT),
|
||||||
'lock_timestamp': int(self.lock_time.replace(tzinfo=timezone.utc).timestamp() * 1000),
|
'lock_timestamp': int(self.lock_time.replace(tzinfo=timezone.utc).timestamp() * 1000),
|
||||||
|
@ -210,6 +210,7 @@ class ForceBuyResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class LockModel(BaseModel):
|
class LockModel(BaseModel):
|
||||||
|
id: int
|
||||||
active: bool
|
active: bool
|
||||||
lock_end_time: str
|
lock_end_time: str
|
||||||
lock_end_timestamp: int
|
lock_end_timestamp: int
|
||||||
@ -224,6 +225,11 @@ class Locks(BaseModel):
|
|||||||
locks: List[LockModel]
|
locks: List[LockModel]
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteLockRequest(BaseModel):
|
||||||
|
pair: Optional[str]
|
||||||
|
lockid: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class Logs(BaseModel):
|
class Logs(BaseModel):
|
||||||
log_count: int
|
log_count: int
|
||||||
logs: List[List]
|
logs: List[List]
|
||||||
|
@ -11,13 +11,14 @@ from freqtrade.data.history import get_datahandler
|
|||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.rpc import RPC
|
from freqtrade.rpc import RPC
|
||||||
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload,
|
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload,
|
||||||
BlacklistResponse, Count, Daily, DeleteTrade,
|
BlacklistResponse, Count, Daily,
|
||||||
ForceBuyPayload, ForceBuyResponse,
|
DeleteLockRequest, DeleteTrade, ForceBuyPayload,
|
||||||
ForceSellPayload, Locks, Logs, OpenTradeSchema,
|
ForceBuyResponse, ForceSellPayload, Locks, Logs,
|
||||||
PairHistory, PerformanceEntry, Ping, PlotConfig,
|
OpenTradeSchema, PairHistory, PerformanceEntry,
|
||||||
Profit, ResultMsg, ShowConfig, Stats, StatusMsg,
|
Ping, PlotConfig, Profit, ResultMsg, ShowConfig,
|
||||||
StrategyListResponse, StrategyResponse,
|
Stats, StatusMsg, StrategyListResponse,
|
||||||
TradeResponse, Version, WhitelistResponse)
|
StrategyResponse, TradeResponse, Version,
|
||||||
|
WhitelistResponse)
|
||||||
from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional
|
from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional
|
||||||
from freqtrade.rpc.rpc import RPCException
|
from freqtrade.rpc.rpc import RPCException
|
||||||
|
|
||||||
@ -136,11 +137,21 @@ def whitelist(rpc: RPC = Depends(get_rpc)):
|
|||||||
return rpc._rpc_whitelist()
|
return rpc._rpc_whitelist()
|
||||||
|
|
||||||
|
|
||||||
@router.get('/locks', response_model=Locks, tags=['info'])
|
@router.get('/locks', response_model=Locks, tags=['info', 'locks'])
|
||||||
def locks(rpc: RPC = Depends(get_rpc)):
|
def locks(rpc: RPC = Depends(get_rpc)):
|
||||||
return rpc._rpc_locks()
|
return rpc._rpc_locks()
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete('/locks/{lockid}', response_model=Locks, tags=['info', 'locks'])
|
||||||
|
def delete_lock(lockid: int, rpc: RPC = Depends(get_rpc)):
|
||||||
|
return rpc._rpc_delete_lock(lockid=lockid)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/locks/delete', response_model=Locks, tags=['info', 'locks'])
|
||||||
|
def delete_lock_pair(payload: DeleteLockRequest, rpc: RPC = Depends(get_rpc)):
|
||||||
|
return rpc._rpc_delete_lock(lockid=payload.lockid, pair=payload.pair)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/logs', response_model=Logs, tags=['info'])
|
@router.get('/logs', response_model=Logs, tags=['info'])
|
||||||
def logs(limit: Optional[int] = None, rpc: RPC = Depends(get_rpc)):
|
def logs(limit: Optional[int] = None, rpc: RPC = Depends(get_rpc)):
|
||||||
return rpc._rpc_get_logs(limit)
|
return rpc._rpc_get_logs(limit)
|
||||||
|
@ -3,7 +3,7 @@ This module contains class to define a RPC communications
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from math import isnan
|
from math import isnan
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
@ -20,6 +20,7 @@ from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs
|
|||||||
from freqtrade.loggers import bufferHandler
|
from freqtrade.loggers import bufferHandler
|
||||||
from freqtrade.misc import shorten_date
|
from freqtrade.misc import shorten_date
|
||||||
from freqtrade.persistence import PairLocks, Trade
|
from freqtrade.persistence import PairLocks, Trade
|
||||||
|
from freqtrade.persistence.models import PairLock
|
||||||
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
|
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
|
||||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
@ -663,7 +664,7 @@ class RPC:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _rpc_locks(self) -> Dict[str, Any]:
|
def _rpc_locks(self) -> Dict[str, Any]:
|
||||||
""" Returns the current locks"""
|
""" Returns the current locks """
|
||||||
|
|
||||||
locks = PairLocks.get_pair_locks(None)
|
locks = PairLocks.get_pair_locks(None)
|
||||||
return {
|
return {
|
||||||
@ -671,6 +672,25 @@ class RPC:
|
|||||||
'locks': [lock.to_json() for lock in locks]
|
'locks': [lock.to_json() for lock in locks]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _rpc_delete_lock(self, lockid: Optional[int] = None,
|
||||||
|
pair: Optional[str] = None) -> Dict[str, Any]:
|
||||||
|
""" Delete specific lock(s) """
|
||||||
|
locks = []
|
||||||
|
|
||||||
|
if pair:
|
||||||
|
locks = PairLocks.get_pair_locks(pair)
|
||||||
|
if lockid:
|
||||||
|
locks = PairLock.query.filter(PairLock.id == lockid).all()
|
||||||
|
|
||||||
|
for lock in locks:
|
||||||
|
lock.active = False
|
||||||
|
lock.lock_end_time = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
# session is always the same
|
||||||
|
PairLock.session.flush()
|
||||||
|
|
||||||
|
return self._rpc_locks()
|
||||||
|
|
||||||
def _rpc_whitelist(self) -> Dict:
|
def _rpc_whitelist(self) -> Dict:
|
||||||
""" Returns the currently active whitelist"""
|
""" Returns the currently active whitelist"""
|
||||||
res = {'method': self._freqtrade.pairlists.name_list,
|
res = {'method': self._freqtrade.pairlists.name_list,
|
||||||
|
@ -6,6 +6,7 @@ This module manage Telegram communication
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from html import escape
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from typing import Any, Callable, Dict, List, Union
|
from typing import Any, Callable, Dict, List, Union
|
||||||
|
|
||||||
@ -144,6 +145,7 @@ class Telegram(RPCHandler):
|
|||||||
CommandHandler('daily', self._daily),
|
CommandHandler('daily', self._daily),
|
||||||
CommandHandler('count', self._count),
|
CommandHandler('count', self._count),
|
||||||
CommandHandler('locks', self._locks),
|
CommandHandler('locks', self._locks),
|
||||||
|
CommandHandler(['unlock', 'delete_locks'], self._delete_locks),
|
||||||
CommandHandler(['reload_config', 'reload_conf'], self._reload_config),
|
CommandHandler(['reload_config', 'reload_conf'], self._reload_config),
|
||||||
CommandHandler(['show_config', 'show_conf'], self._show_config),
|
CommandHandler(['show_config', 'show_conf'], self._show_config),
|
||||||
CommandHandler('stopbuy', self._stopbuy),
|
CommandHandler('stopbuy', self._stopbuy),
|
||||||
@ -719,19 +721,35 @@ class Telegram(RPCHandler):
|
|||||||
Handler for /locks.
|
Handler for /locks.
|
||||||
Returns the currently active locks
|
Returns the currently active locks
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
locks = self._rpc._rpc_locks()
|
locks = self._rpc._rpc_locks()
|
||||||
message = tabulate([[
|
message = tabulate([[
|
||||||
|
lock['id'],
|
||||||
lock['pair'],
|
lock['pair'],
|
||||||
lock['lock_end_time'],
|
lock['lock_end_time'],
|
||||||
lock['reason']] for lock in locks['locks']],
|
lock['reason']] for lock in locks['locks']],
|
||||||
headers=['Pair', 'Until', 'Reason'],
|
headers=['ID', 'Pair', 'Until', 'Reason'],
|
||||||
tablefmt='simple')
|
tablefmt='simple')
|
||||||
message = "<pre>{}</pre>".format(message)
|
message = f"<pre>{escape(message)}</pre>"
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
self._send_msg(message, parse_mode=ParseMode.HTML)
|
self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||||
except RPCException as e:
|
|
||||||
self._send_msg(str(e))
|
@authorized_only
|
||||||
|
def _delete_locks(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
"""
|
||||||
|
Handler for /delete_locks.
|
||||||
|
Returns the currently active locks
|
||||||
|
"""
|
||||||
|
arg = context.args[0] if context.args and len(context.args) > 0 else None
|
||||||
|
lockid = None
|
||||||
|
pair = None
|
||||||
|
if arg:
|
||||||
|
try:
|
||||||
|
lockid = int(arg)
|
||||||
|
except ValueError:
|
||||||
|
pair = arg
|
||||||
|
|
||||||
|
self._rpc._rpc_delete_lock(lockid=lockid, pair=pair)
|
||||||
|
self._locks(update, context)
|
||||||
|
|
||||||
@authorized_only
|
@authorized_only
|
||||||
def _whitelist(self, update: Update, context: CallbackContext) -> None:
|
def _whitelist(self, update: Update, context: CallbackContext) -> None:
|
||||||
@ -850,6 +868,7 @@ class Telegram(RPCHandler):
|
|||||||
"Avg. holding durationsfor buys and sells.`\n"
|
"Avg. holding durationsfor buys and sells.`\n"
|
||||||
"*/count:* `Show number of active trades compared to allowed number of trades`\n"
|
"*/count:* `Show number of active trades compared to allowed number of trades`\n"
|
||||||
"*/locks:* `Show currently locked pairs`\n"
|
"*/locks:* `Show currently locked pairs`\n"
|
||||||
|
"*/unlock <pair|id>:* `Unlock this Pair (or this lock id if it's numeric)`\n"
|
||||||
"*/balance:* `Show account balance per currency`\n"
|
"*/balance:* `Show account balance per currency`\n"
|
||||||
"*/stopbuy:* `Stops buying, but handles open trades gracefully` \n"
|
"*/stopbuy:* `Stops buying, but handles open trades gracefully` \n"
|
||||||
"*/reload_config:* `Reload configuration file` \n"
|
"*/reload_config:* `Reload configuration file` \n"
|
||||||
|
@ -118,6 +118,14 @@ class FtRestClient():
|
|||||||
"""
|
"""
|
||||||
return self._get("locks")
|
return self._get("locks")
|
||||||
|
|
||||||
|
def delete_lock(self, lock_id):
|
||||||
|
"""Delete (disable) lock from the database.
|
||||||
|
|
||||||
|
:param lock_id: ID for the lock to delete
|
||||||
|
:return: json object
|
||||||
|
"""
|
||||||
|
return self._delete("locks/{}".format(lock_id))
|
||||||
|
|
||||||
def daily(self, days=None):
|
def daily(self, days=None):
|
||||||
"""Return the amount of open trades.
|
"""Return the amount of open trades.
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# pragma pylint: disable=missing-docstring, C0103
|
# pragma pylint: disable=missing-docstring, C0103
|
||||||
# pragma pylint: disable=invalid-sequence-index, invalid-name, too-many-arguments
|
# pragma pylint: disable=invalid-sequence-index, invalid-name, too-many-arguments
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta, timezone
|
||||||
from unittest.mock import ANY, MagicMock, PropertyMock
|
from unittest.mock import ANY, MagicMock, PropertyMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -10,6 +10,7 @@ from numpy import isnan
|
|||||||
from freqtrade.edge import PairInfo
|
from freqtrade.edge import PairInfo
|
||||||
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
|
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
from freqtrade.persistence.pairlock_middleware import PairLocks
|
||||||
from freqtrade.rpc import RPC, RPCException
|
from freqtrade.rpc import RPC, RPCException
|
||||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
@ -911,6 +912,24 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None:
|
|||||||
rpc._rpc_forcebuy(pair, None)
|
rpc._rpc_forcebuy(pair, None)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
|
def test_rpc_delete_lock(mocker, default_conf):
|
||||||
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
pair = 'ETH/BTC'
|
||||||
|
|
||||||
|
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=4))
|
||||||
|
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=5))
|
||||||
|
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=10))
|
||||||
|
locks = rpc._rpc_locks()
|
||||||
|
assert locks['lock_count'] == 3
|
||||||
|
locks1 = rpc._rpc_delete_lock(lockid=locks['locks'][0]['id'])
|
||||||
|
assert locks1['lock_count'] == 2
|
||||||
|
|
||||||
|
locks2 = rpc._rpc_delete_lock(pair=pair)
|
||||||
|
assert locks2['lock_count'] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_rpc_whitelist(mocker, default_conf) -> None:
|
def test_rpc_whitelist(mocker, default_conf) -> None:
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
|
||||||
|
@ -418,6 +418,16 @@ def test_api_locks(botclient):
|
|||||||
assert 'randreason' in (rc.json()['locks'][0]['reason'], rc.json()['locks'][1]['reason'])
|
assert 'randreason' in (rc.json()['locks'][0]['reason'], rc.json()['locks'][1]['reason'])
|
||||||
assert 'deadbeef' in (rc.json()['locks'][0]['reason'], rc.json()['locks'][1]['reason'])
|
assert 'deadbeef' in (rc.json()['locks'][0]['reason'], rc.json()['locks'][1]['reason'])
|
||||||
|
|
||||||
|
# Test deletions
|
||||||
|
rc = client_delete(client, f"{BASE_URI}/locks/1")
|
||||||
|
assert_response(rc)
|
||||||
|
assert rc.json()['lock_count'] == 1
|
||||||
|
|
||||||
|
rc = client_post(client, f"{BASE_URI}/locks/delete",
|
||||||
|
data='{"pair": "XRP/BTC"}')
|
||||||
|
assert_response(rc)
|
||||||
|
assert rc.json()['lock_count'] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_api_show_config(botclient, mocker):
|
def test_api_show_config(botclient, mocker):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
|
@ -92,7 +92,8 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
|
|||||||
message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
|
message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
|
||||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['trades'], "
|
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['trades'], "
|
||||||
"['delete'], ['performance'], ['stats'], ['daily'], ['count'], ['locks'], "
|
"['delete'], ['performance'], ['stats'], ['daily'], ['count'], ['locks'], "
|
||||||
"['reload_config', 'reload_conf'], ['show_config', 'show_conf'], ['stopbuy'], "
|
"['unlock', 'delete_locks'], ['reload_config', 'reload_conf'], "
|
||||||
|
"['show_config', 'show_conf'], ['stopbuy'], "
|
||||||
"['whitelist'], ['blacklist'], ['logs'], ['edge'], ['help'], ['version']"
|
"['whitelist'], ['blacklist'], ['logs'], ['edge'], ['help'], ['version']"
|
||||||
"]")
|
"]")
|
||||||
|
|
||||||
@ -981,6 +982,16 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None
|
|||||||
assert 'deadbeef' in msg_mock.call_args_list[0][0][0]
|
assert 'deadbeef' in msg_mock.call_args_list[0][0][0]
|
||||||
assert 'randreason' in msg_mock.call_args_list[0][0][0]
|
assert 'randreason' in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
|
context = MagicMock()
|
||||||
|
context.args = ['XRP/BTC']
|
||||||
|
msg_mock.reset_mock()
|
||||||
|
telegram._delete_locks(update=update, context=context)
|
||||||
|
|
||||||
|
assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0]
|
||||||
|
assert 'randreason' in msg_mock.call_args_list[0][0][0]
|
||||||
|
assert 'XRP/BTC' not in msg_mock.call_args_list[0][0][0]
|
||||||
|
assert 'deadbeef' not in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_whitelist_static(default_conf, update, mocker) -> None:
|
def test_whitelist_static(default_conf, update, mocker) -> None:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user