Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✅ bot run coverage #609

Merged
merged 54 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4b4e9cd
✅ Improving test coverage
mraniki Jul 25, 2023
87e6217
✅ bot run coverage
mraniki Jul 25, 2023
defe853
Update Requirements
mraniki Jul 25, 2023
cdf0a47
🔧 adding black formatter
mraniki Jul 25, 2023
f2e4f6b
Update Requirements
mraniki Jul 25, 2023
4f333d5
🔊 log config to stdout
mraniki Jul 25, 2023
0bb36f8
📝 comments
mraniki Jul 25, 2023
460e223
🔥 remove iteration
mraniki Jul 25, 2023
724e97d
:white_check_mark: unit test for bot
mraniki Jul 25, 2023
dd6e665
:white_check_mark:
mraniki Jul 25, 2023
fe4f09a
:white_check_mark:
mraniki Jul 25, 2023
273957c
:white_check_mark:
mraniki Jul 25, 2023
73a2d10
:white_check_mark:
mraniki Jul 25, 2023
914056f
:white_check_mark:
mraniki Jul 25, 2023
3c5cdcc
Update Requirements
mraniki Jul 25, 2023
3f0263e
:white_check_mark:
mraniki Jul 25, 2023
c61b807
Merge branch 'dev' of [email protected]:mraniki/tt
mraniki Jul 25, 2023
bfab6c0
:white_check_mark:
mraniki Jul 25, 2023
6ae8ec7
:rotating_light:
mraniki Jul 25, 2023
ea04c67
:white_check_mark:
mraniki Jul 25, 2023
af4267d
:white_check_mark:
mraniki Jul 25, 2023
ab51270
:white_check_mark:
mraniki Jul 25, 2023
64935bb
:white_check_mark:
mraniki Jul 25, 2023
3b9d1b0
✅ start_bot
mraniki Jul 26, 2023
7044ace
Update Requirements
mraniki Jul 26, 2023
d9a852a
♻️ while true vs iteration for start
mraniki Jul 26, 2023
ceef751
✅ Unit Test
mraniki Jul 26, 2023
8d3bb35
:white_check_mark: Unit Test
mraniki Jul 26, 2023
79237b5
:white_check_mark: Unit Test
mraniki Jul 26, 2023
38d41c3
:white_check_mark: Unit Test
mraniki Jul 26, 2023
d6bbbf1
:white_check_mark: Unit Test
mraniki Jul 26, 2023
16cdc3d
:white_check_mark: Unit Test verbosity
mraniki Jul 26, 2023
5331d73
:white_check_mark: Unit Test
mraniki Jul 26, 2023
20bee4b
:recycle: while true vs iteration for start
mraniki Jul 26, 2023
b6d56d5
:white_check_mark: Unit Test
mraniki Jul 26, 2023
be59238
:white_check_mark: Unit Test
mraniki Jul 26, 2023
559320a
:white_check_mark: Unit Test
mraniki Jul 26, 2023
39ca7bd
:white_check_mark:
mraniki Jul 26, 2023
265d24e
:white_check_mark:
mraniki Jul 26, 2023
373ded3
:white_check_mark:
mraniki Jul 26, 2023
462ca4d
:white_check_mark:
mraniki Jul 26, 2023
8bb5c0f
✅ Unit Test
mraniki Jul 26, 2023
60d75dd
✅ Unit Test for scheduling
mraniki Jul 26, 2023
357de60
Update Requirements
mraniki Jul 26, 2023
7b0e86a
✅ Unit Test
mraniki Jul 26, 2023
f90620f
🚨
mraniki Jul 26, 2023
272a82f
✅ Unit Test bot startup
mraniki Jul 26, 2023
b029aa4
✅ Unit Test
mraniki Jul 26, 2023
57171bb
Update Requirements
mraniki Jul 26, 2023
c4a73b5
✅ Unit Test for plugin manager
mraniki Jul 26, 2023
b062ea2
💬 updated comments and example for settings
mraniki Jul 26, 2023
1d06a74
⬆️ asyncz dep
mraniki Jul 26, 2023
7d00e41
⬆️ talkytrend
mraniki Jul 26, 2023
174f3b5
Update Requirements
mraniki Jul 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .requirements/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ click==8.1.6 ; python_version >= "3.10" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
cryptography==41.0.2 ; python_version >= "3.10" and python_version < "4.0"
cytoolz==0.12.2 ; python_version >= "3.10" and python_version < "4" and implementation_name == "cpython"
dxsp==4.2.1 ; python_version >= "3.10" and python_version < "4.0"
dxsp==4.2.2 ; python_version >= "3.10" and python_version < "4.0"
dynaconf==3.2.0 ; python_version >= "3.10" and python_version < "4.0"
emoji==2.7.0 ; python_version >= "3.10" and python_version < "4.0"
eth-abi==4.1.0 ; python_version >= "3.10" and python_version < "4"
Expand All @@ -35,7 +35,7 @@ eth-typing==3.4.0 ; python_version >= "3.10" and python_version < "4"
eth-utils==2.2.0 ; python_version >= "3.10" and python_version < "4"
exceptiongroup==1.1.2 ; python_version >= "3.10" and python_version < "3.11"
fastapi==0.100.0 ; python_version >= "3.10" and python_version < "4.0"
findmyorder==1.7.0 ; python_version >= "3.10" and python_version < "4.0"
findmyorder==1.7.1 ; python_version >= "3.10" and python_version < "4.0"
frozendict==2.3.8 ; python_version >= "3.10" and python_version < "4.0"
frozenlist==1.4.0 ; python_version >= "3.10" and python_version < "4.0"
future==0.18.3 ; python_version >= "3.10" and python_version < "4.0"
Expand All @@ -47,15 +47,15 @@ html5lib==1.1 ; python_version >= "3.10" and python_version < "4.0"
httpcore==0.17.3 ; python_version >= "3.10" and python_version < "4.0"
httpx==0.24.1 ; python_version >= "3.10" and python_version < "4.0"
hyperframe==6.0.1 ; python_version >= "3.10" and python_version < "4.0"
iamlistening==3.0.1 ; python_version >= "3.10" and python_version < "4.0"
iamlistening==3.0.2 ; python_version >= "3.10" and python_version < "4.0"
idna==3.4 ; python_version >= "3.10" and python_version < "4.0"
jsonschema-specifications==2023.7.1 ; python_version >= "3.10" and python_version < "4.0"
jsonschema==4.18.4 ; python_version >= "3.10" and python_version < "4.0"
logbook==1.5.3 ; python_version >= "3.10" and python_version < "4.0"
loguru==0.7.0 ; python_version >= "3.10" and python_version < "4.0"
lru-dict==1.2.0 ; python_version >= "3.10" and python_version < "4.0"
lxml==4.9.3 ; python_version >= "3.10" and python_version < "4.0"
markdown==3.4.3 ; python_version >= "3.10" and python_version < "4.0"
markdown==3.4.4 ; python_version >= "3.10" and python_version < "4.0"
matrix-nio==0.20.2 ; python_version >= "3.10" and python_version < "4.0"
multidict==6.0.4 ; python_version >= "3.10" and python_version < "4.0"
multitasking==0.0.11 ; python_version >= "3.10" and python_version < "4.0"
Expand All @@ -75,8 +75,8 @@ pycares==4.3.0 ; python_version >= "3.10" and python_version < "4.0"
pycoingecko==3.1.0 ; python_version >= "3.10" and python_version < "4.0"
pycparser==2.21 ; python_version >= "3.10" and python_version < "4.0"
pycryptodome==3.18.0 ; python_version >= "3.10" and python_version < "4.0"
pydantic-core==2.3.0 ; python_version >= "3.10" and python_version < "4.0"
pydantic==2.0.3 ; python_version >= "3.10" and python_version < "4.0"
pydantic-core==2.4.0 ; python_version >= "3.10" and python_version < "4.0"
pydantic==2.1.1 ; python_version >= "3.10" and python_version < "4.0"
pyparsing==3.1.0 ; python_version >= "3.10" and python_version < "4.0"
python-cryptography-fernet-wrapper==1.0.4 ; python_version >= "3.10" and python_version < "4.0"
python-dateutil==2.8.2 ; python_version >= "3.10" and python_version < "4.0"
Expand All @@ -101,7 +101,7 @@ six==1.16.0 ; python_version >= "3.10" and python_version < "4.0"
sniffio==1.3.0 ; python_version >= "3.10" and python_version < "4.0"
soupsieve==2.4.1 ; python_version >= "3.10" and python_version < "4.0"
starlette==0.27.0 ; python_version >= "3.10" and python_version < "4.0"
talkytrend==1.12.1 ; python_version >= "3.10" and python_version < "4.0"
talkytrend==1.12.2 ; python_version >= "3.10" and python_version < "4.0"
telethon==1.29.2 ; python_version >= "3.10" and python_version < "4.0"
toml==0.10.2 ; python_version >= "3.10" and python_version < "4.0"
toolz==0.12.0 ; python_version >= "3.10" and python_version < "4" and (implementation_name == "pypy" or implementation_name == "cpython")
Expand Down
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ ping3 = "^4.0.4"
ccxt = "^4.0.0"
dxsp = "^4.1.0"
findmyorder = "^1.6.1"
iamlistening = "^3.0.1"
iamlistening = "^3.0.2"
talkytrend = "^1.12.1"


Expand All @@ -73,6 +73,12 @@ exclude = [
format = "github"
fixable = ["ALL"]

[tool.black]
target-version = ['py39', 'py310', 'py311']
line-length = 88
# exclude = "((.eggs | .git | .pytype | .pytest_cache | build | dist))"


[tool.pylint.exceptions]
overgeneral-exceptions = [
"builtins.BaseException",
Expand Down Expand Up @@ -103,6 +109,7 @@ pythonpath = ". tt"
testpaths = "tests"
python_classes = "Test*"
log_format = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"
addopts = "-v"
log_level = "DEBUG"
filterwarnings = [
" ignore:.*U.*mode is deprecated:DeprecationWarning",
Expand Down
40 changes: 40 additions & 0 deletions tests/test_cex_exchange_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,43 @@ async def test_execute_order(plugin, order_parsed):
print(result)
assert result is not None
assert "⚠️ order execution" in result


# @pytest.mark.asyncio
# async def test_cex_exchange():
# exchange_instance = CexExchange()
# ccxt_client_mock = AsyncMock()
# ccxt_client_mock.uid = '12345'
# ccxt_client_mock.fetchTicker.side_effect = lambda symbol: {'last': 5000}
# ccxt_client_mock.fetchBalance = AsyncMock(return_value={'BTC': {'free': 1}})
# ccxt_client_mock.create_order = AsyncMock(
# return_value={
# 'id': '12345',
# 'amount': 1,
# 'price': 5000,
# 'datetime': '2022-01-01 00:00:00'})
# exchange_instance.cex = ccxt_client_mock

# with patch('ccxt.binance', return_value=ccxt_client_mock):

# result = await exchange_instance.get_info()

# assert result is not None
# assert '💱' in result
# assert '🪪' in result

# order_params = {
# 'action': 'BUY',
# 'instrument': 'BTCUSDT',
# 'quantity': 100
# }

# result = await ccxt_client_mock.execute_order(order_params)
# print(result)
# assert result is not None
# assert '⬆️ BTC/USD' in result

# ccxt_client_mock.fetchTicker.assert_awaited_once_with('BTCUSDT')
# ccxt_client_mock.fetchBalance.assert_awaited_once()
# ccxt_client_mock.create_order.assert_awaited_once_with(
# 'BTCUSDT', settings.cex_ordertype, 'BUY', 0.02, price=None)
56 changes: 56 additions & 0 deletions tests/test_example_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from unittest.mock import AsyncMock

import pytest
from asyncz.triggers import CronTrigger

from tt.config import settings
from tt.plugins.default_plugins.example_plugin import ExamplePlugin
Expand Down Expand Up @@ -88,3 +89,58 @@
send_notification = AsyncMock()
await plugin.handle_message(f"{settings.bot_prefix}{settings.bot_command_help}")
send_notification.assert_awaited_once


@pytest.mark.asyncio
async def test_baseplugins():
plugin = BasePlugin
assert callable(plugin.start)
Dismissed Show dismissed Hide dismissed
assert callable(plugin.stop)
Fixed Show fixed Hide fixed
assert callable(plugin.send_notification)
Fixed Show fixed Hide fixed
assert callable(plugin.send_notification)
Dismissed Show dismissed Hide dismissed
assert callable(plugin.should_handle)

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
assert callable(plugin.handle_message)
Fixed Show fixed Hide fixed


@pytest.mark.asyncio
async def test_plugin_notify_cron_task():
plugin = BasePlugin()
plugin.scheduler = AsyncMock()
plugin.send_notification = AsyncMock()
function_result = 'Test Result'
function_mock = AsyncMock(return_value=function_result)

await plugin.plugin_notify_cron_task(
user_name='Test User',
user_day_of_week='mon-fri',
user_hours='6,12,18',
user_timezone='UTC',
function=function_mock
)

plugin.scheduler.add_task.assert_called_once_with(
name='Test User',
fn=plugin.send_notification,
args=[function_result],
trigger=CronTrigger(
day_of_week='mon-fri',
hour='6,12,18',
timezone='UTC'
),
is_enabled=True
)

@pytest.mark.asyncio
async def test_plugin_notify_schedule_task():
plugin = BasePlugin()
plugin.scheduler = AsyncMock()
plugin.send_notification = AsyncMock()
function_result = 'Test Result'
function_mock = AsyncMock(return_value=function_result)
await plugin.plugin_notify_schedule_task(
user_name='Test User',
frequency=8,
function=function_mock
)

plugin.scheduler.add_task.assert_awaited_once
100 changes: 49 additions & 51 deletions tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
TT test
"""
import asyncio
from unittest.mock import AsyncMock
from unittest.mock import AsyncMock, patch

import pytest
from fastapi.testclient import TestClient
from iamlistening import Listener
from iamlistening import ChatManager, Listener
from iamlistening.platform import TelegramHandler

from tt.bot import app
from tt.bot import app, start_bot_task
from tt.config import settings
from tt.plugins.plugin_manager import BasePlugin, PluginManager
from tt.plugins.plugin_manager import PluginManager
from tt.utils import run_bot, send_notification, start_bot, start_plugins


Expand All @@ -27,6 +28,12 @@
def listener_test():
return Listener()

@pytest.fixture(name="ial_test")
def ial_test():
listener = Listener()
listener.handler = TelegramHandler()
return listener

@pytest.fixture(name="plugin_manager_obj")
def pluginmngr_test():
return PluginManager()
Expand All @@ -35,6 +42,24 @@
def message():
return "Test message"

@pytest.fixture
def mock_listener():
mock_listener = AsyncMock(spec=Listener)
mock_listener.handler = AsyncMock(spec=ChatManager)
return mock_listener

@pytest.fixture
def mock_plugin_manager():
return AsyncMock(spec=PluginManager)



@pytest.mark.asyncio
async def test_start_bot_task():
run_bot = AsyncMock()
await start_bot_task()
assert run_bot.assert_awaited_once
Dismissed Show dismissed Hide dismissed


def test_app_endpoint_main():
client = TestClient(app)
Expand Down Expand Up @@ -80,53 +105,26 @@


@pytest.mark.asyncio
async def test_start_bot(listener_obj, plugin_manager_obj):
start = AsyncMock()
task = asyncio.create_task(start_bot(listener_obj, plugin_manager_obj))
task.cancel()
with pytest.raises(asyncio.CancelledError):
start.assert_awaited
await task


@pytest.mark.asyncio
async def test_run_bot(caplog):
start_bot = AsyncMock()
task = asyncio.create_task(run_bot())
start_bot.assert_awaited
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task


# @pytest.mark.asyncio
# async def test_get_latest_message(message):
# listener = Listener()
# await listener.start()
# assert listener is not None
# assert settings.VALUE == "On Testing"
# await listener.handler.handle_message(message)
# assert await listener.handler.get_latest_message() == message


# @pytest.mark.asyncio
# async def test_listener_handler():
# listener_test = Listener()
# print(listener_test)
# assert listener_test is not None
# assert isinstance(listener_test, Listener)
# await listener_test.start()
# await listener_test.handler.handle_message("hello")
# msg = await listener_test.handler.get_latest_message()
# print(msg)
# assert msg == "hello"
async def test_run_bot():
listener_instance = Listener()
start_bot = AsyncMock(side_effect=[listener_instance])
with patch('tt.utils.start_bot', start_bot):
task = asyncio.create_task(run_bot())
await asyncio.gather(task, asyncio.sleep(2))
start_bot.assert_awaited
listener_created = listener_instance
assert isinstance(listener_created, Listener)
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.


@pytest.mark.asyncio
async def test_baseplugins():
plugin = BasePlugin
assert callable(plugin.start)
assert callable(plugin.stop)
assert callable(plugin.send_notification)
assert callable(plugin.should_handle)
assert callable(plugin.handle_message)
async def test_start_bot():

listener = AsyncMock(spec=Listener)
listener.handler = AsyncMock(spec=ChatManager)
plugin_manager = AsyncMock(spec=PluginManager)
await start_bot(
listener,
plugin_manager,
max_iterations=1)
listener.start.assert_awaited_once()
listener.handler.get_latest_message.assert_awaited_once()
1 change: 0 additions & 1 deletion tt/ __init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
__version__ = "2.2.2"
2 changes: 1 addition & 1 deletion tt/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def loguru_setup():
}
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
loguru_logger.add(
sink=sys.stderr,
sink=sys.stdout,
level=settings.loglevel,
filter=log_filters,
)
Expand Down
4 changes: 2 additions & 2 deletions tt/plugins/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ async def plugin_notify_cron_task(
self,
user_name=None,
user_day_of_week="mon-fri",
user_hours="8,12,16",
user_hours="6,12,18",
user_timezone="UTC",
function=None):
"""Handles task cron scheduling for notification
monday to Friday at 8AM, 12PM and 4PM UTC based"""
Monday to Friday at 6AM, 12PM and 6PM UTC"""
if function:
self.scheduler.add_task(
name=user_name,
Expand Down
11 changes: 7 additions & 4 deletions tt/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async def send_notification(msg):
body_format=msg_format)


async def run_bot(max_iterations=None):
async def run_bot():
"""
🤖 Run the chat bot & the plugins.
"""
Expand All @@ -43,15 +43,18 @@ async def start_plugins(plugin_manager):
loop.create_task(plugin_manager.start_all_plugins())


async def start_bot(listener, plugin_manager):
async def start_bot(listener, plugin_manager, max_iterations=None):
"""
👂 Listen to the bot and dispatch to plugins
👂 Start the chat listener and dispatch to plugins
"""
await listener.start()
await start_plugins(plugin_manager)
while True:
iteration = 0
while iteration < max_iterations:
msg = await listener.handler.get_latest_message()
if msg and settings.plugin_enabled:
await plugin_manager.process_message(msg)
iteration += 1

await asyncio.sleep(1)