async def test_execute_view_async(module: Module) -> None: name = 'view' mock = AsyncMock(name='view') module.view(name, function=mock) await module.execute_action(f'view_submission:{name}') mock.assert_awaited_once()
async def test_execute_action_async(module: Module) -> None: name = 'action' mock = AsyncMock(name='action') module.action(name, function=mock) await module.execute_action(f'block_actions:{name}') mock.assert_awaited_once()
def test_add_view(module: Module) -> None: name = 'test' @module.view(name) async def view() -> None: pass assert module._views[name] == view # noqa with pytest.raises(AssertionError): module.view(name, function=view)
def test_add_action(module: Module) -> None: name = 'test' @module.action(name) async def action() -> None: pass assert module._actions[name] == action # noqa with pytest.raises(AssertionError): module.action(name, function=action)
def test_add_converter(module: Module) -> None: assert module._get_converter(int) == int # noqa @module.converter(int) def convert_binary(binary_number: str) -> int: return int(binary_number, 2) assert module._get_converter(int) == convert_binary # noqa with pytest.raises(AssertionError): module.converter(int, converter=convert_binary)
async def test_hearbeat(module: Module, monkeypatch) -> None: payload = module._build_module_payload() # noqa mock_api = AsyncMock(name='register_module_api_modules_post') monkeypatch.setattr(AsyncMetabotApi, 'register_module_api_modules_post', mock_api) module._start_heartbeat() # noqa assert isinstance(module._heartbeat, Task) # noqa await sleep(0) mock_api.assert_awaited_with(payload) module._stop_heartbeat() # noqa assert module._heartbeat is None # noqa
def module() -> Module: return Module( name='example', description='Example module', module_url='http://localhost:8000', metabot_url='http://localhost:8000', heartbeat_delay=0, )
def test_add_command(module: Module) -> None: name = 'test' description = 'description' @module.command( name, description=description, ) async def test() -> None: pass command = module._commands[name] # noqa assert isinstance(command, Command) assert command.name == name assert command.description == description assert command.func == test with pytest.raises(AssertionError): module.command(name, function=test, description=description)
def test_build_module_payload(module: Module) -> None: command_name = 'command' description = 'description' action_name = 'action' view_name = 'view' @module.command(command_name, description=description, arg_descriptions={'req': description}) def command(req: str, opt: str = '') -> None: pass @module.action(action_name) def action() -> None: pass @module.view(view_name) def view() -> None: pass payload = jsonable_encoder(module._build_module_payload()) # noqa assert payload == { 'name': module.name, 'description': module.description, 'url': module.module_url, 'commands': { command_name: { 'name': command_name, 'description': description, 'arguments': [ { 'name': 'req', 'is_optional': False, 'description': description }, { 'name': 'opt', 'is_optional': True, 'description': None }, ] } }, 'actions': [ f'block_actions:{action_name}', f'view_submission:{view_name}', ] }
def test_parse_arguments() -> None: async def func(arg1: str, arg2: int = 1) -> None: pass descriptions = {'arg1': 'HELLOOOOOOOOO'} required, optional = Module._parse_arguments(func, descriptions) # noqa assert required.is_optional is False assert required.type == str assert required.name == 'arg1' assert required.description == descriptions['arg1'] assert optional.is_optional is True assert optional.type == int assert optional.name == 'arg2' assert optional.description is None
def test_parse_arguments_fails() -> None: with pytest.raises(AssertionError): def func_with_args(*args: Any) -> None: pass Module._parse_arguments(func_with_args) # noqa with pytest.raises(AssertionError): def func_with_kwargs(**kwargs: Any) -> None: pass Module._parse_arguments(func_with_kwargs) # noqa with pytest.raises(AssertionError): def func_with_positional_only(a: Any, /, b: Any) -> None: # noqa E225 pass Module._parse_arguments(func_with_positional_only) # noqa
from help.config import ( MODULE_URL, METABOT_URL, COMMANDS_BUTTON_ACTION_ID, HEARTBEAT_DELAY ) from fastapi_metabot.module import Module from help.bl import get_module_name_from_button, send_help log = logging.getLogger(__name__) app = FastAPI() module = Module( name='help', description=':sos: Get info about installed MetaBot modules and commands', module_url=MODULE_URL, metabot_url=METABOT_URL, heartbeat_delay=HEARTBEAT_DELAY, ) @module.command( 'me', description='Displays info about a module and lists available commands.\n' 'When module name is not provided, displays short info about ' 'all modules.', arg_descriptions={ 'module_name': 'Module name', } ) async def get_help(module_name: str = None) -> None:
def app(module: Module) -> FastAPI: app = FastAPI() module.install(app) app.include_router(router, prefix='/fake') return app
from vacations.config import (MODULE_URL, METABOT_URL, REQUEST_VIEW_ID, APPROVE_BUTTON_ACTION_ID, DENY_BUTTON_ACTION_ID, LEAVE_TYPES, HEARTBEAT_DELAY) from vacations.event_handlers import start_app_handler, stop_app_handler from vacations.bl import (send_ephemeral, open_request_view, parse_request_view, get_request_id_from_button, is_admin_channel, send_history, create_request, add_days, approve_request, deny_request) app = FastAPI() app.add_event_handler('startup', start_app_handler(app)) app.add_event_handler('shutdown', stop_app_handler(app)) module = Module( name='vacations', description=':palm_tree: Manage vacations, days off & other leaves', module_url=MODULE_URL, metabot_url=METABOT_URL, heartbeat_delay=HEARTBEAT_DELAY) class UserId(str): pass @module.converter(UserId) async def convert_user_id(user_id: str) -> UserId: user_id_search = search(r'<@(\w+)\|(\w+)>', user_id) if user_id_search: return UserId(user_id_search.group(1)) else: