mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 18:15:28 +03:00
400 lines
14 KiB
Python
400 lines
14 KiB
Python
import re
|
|
|
|
import pytest
|
|
from pytest import CaptureFixture
|
|
|
|
from argenta.command import Command, InputCommand
|
|
from argenta.command.flag import Flag, InputFlag
|
|
from argenta.command.flag.flags import Flags, InputFlags
|
|
from argenta.command.flag.models import PossibleValues, ValidationStatus
|
|
from argenta.response.entity import Response
|
|
from argenta.router import Router
|
|
from argenta.router.entity import _structuring_input_flags, _validate_func_args
|
|
from argenta.router.exceptions import (
|
|
RepeatedAliasNameException,
|
|
RepeatedFlagNameException,
|
|
RepeatedTriggerNameException,
|
|
RequiredArgumentNotPassedException,
|
|
TriggerContainSpacesException,
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for command validation
|
|
# ============================================================================
|
|
|
|
|
|
def test_validate_command_raises_error_for_trigger_with_spaces() -> None:
|
|
router = Router()
|
|
with pytest.raises(TriggerContainSpacesException):
|
|
router._validate_command(Command(trigger='command with spaces'))
|
|
|
|
|
|
def test_validate_command_raises_error_for_same_trigger() -> None:
|
|
router = Router()
|
|
|
|
@router.command('comm')
|
|
def handler(res: Response):
|
|
pass
|
|
|
|
with pytest.raises(RepeatedTriggerNameException):
|
|
@router.command('comm')
|
|
def handler2(res: Response):
|
|
pass
|
|
|
|
|
|
def test_validate_command_raises_error_for_repeated_flag_names() -> None:
|
|
router = Router()
|
|
with pytest.raises(RepeatedFlagNameException):
|
|
router._validate_command(Command(trigger='command', flags=Flags([Flag('test'), Flag('test')])))
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for function argument validation
|
|
# ============================================================================
|
|
|
|
|
|
def test_validate_func_args_raises_error_for_missing_response_parameter() -> None:
|
|
def handler() -> None:
|
|
pass
|
|
with pytest.raises(RequiredArgumentNotPassedException):
|
|
_validate_func_args(handler) # pyright: ignore[reportArgumentType]
|
|
|
|
|
|
def test_validate_func_args_prints_warning_for_wrong_type_hint(capsys: CaptureFixture[str]) -> None:
|
|
class NotResponse:
|
|
pass
|
|
|
|
def func(_response: NotResponse) -> None:
|
|
pass
|
|
|
|
_validate_func_args(func)
|
|
|
|
output = capsys.readouterr()
|
|
|
|
assert "WARNING" in output.out
|
|
|
|
|
|
def test_validate_func_args_accepts_missing_type_hint(capsys: CaptureFixture[str]) -> None:
|
|
def func(response) -> None: # pyright: ignore[reportMissingParameterType, reportUnknownParameterType]
|
|
pass
|
|
_validate_func_args(func) # pyright: ignore[reportUnknownArgumentType]
|
|
output = capsys.readouterr()
|
|
assert output.out == ''
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for input flag structuring - undefined flags
|
|
# ============================================================================
|
|
|
|
|
|
def test_structuring_input_flags_marks_unregistered_flag_as_undefined() -> None:
|
|
cmd = Command('cmd')
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='', status=None)])
|
|
assert _structuring_input_flags(cmd, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='', status=ValidationStatus.UNDEFINED)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_unregistered_flag_with_value_as_undefined() -> None:
|
|
cmd = Command('cmd')
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='some', status=None)])
|
|
assert _structuring_input_flags(cmd, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='some', status=ValidationStatus.UNDEFINED)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_undefined_when_different_flag_registered() -> None:
|
|
cmd = Command('cmd', flags=Flag('port'))
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='some2', status=None)])
|
|
assert _structuring_input_flags(cmd, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='some2', status=ValidationStatus.UNDEFINED)])
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for input flag structuring - invalid flags
|
|
# ============================================================================
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_invalid_when_value_provided_for_neither() -> None:
|
|
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='some3', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='some3', status=ValidationStatus.INVALID)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_invalid_when_value_not_matching_regex() -> None:
|
|
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'some[1-5]$')))
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='some40', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='some40', status=ValidationStatus.INVALID)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_invalid_when_value_not_in_list() -> None:
|
|
command = Command('cmd', flags=Flag('ssh', possible_values=['example']))
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='example2', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='example2', status=ValidationStatus.INVALID)])
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for input flag structuring - valid flags
|
|
# ============================================================================
|
|
|
|
|
|
def test_structuring_input_flags_marks_registered_flag_as_valid() -> None:
|
|
command = Command('cmd', flags=Flag('port'))
|
|
input_flags = InputFlags([InputFlag('port', input_value='some2', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('port', input_value='some2', status=ValidationStatus.VALID)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_valid_when_value_in_list() -> None:
|
|
command = Command('cmd', flags=Flag('port', possible_values=['some2', 'some3']))
|
|
input_flags = InputFlags([InputFlag('port', input_value='some2', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('port', input_value='some2', status=ValidationStatus.VALID)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_valid_when_value_matches_regex() -> None:
|
|
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'more[1-5]$')))
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='more5', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='more5', status=ValidationStatus.VALID)])
|
|
|
|
|
|
def test_structuring_input_flags_marks_flag_valid_when_empty_value_for_neither() -> None:
|
|
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
|
input_flags = InputFlags([InputFlag('ssh', input_value='', status=None)])
|
|
assert _structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='', status=ValidationStatus.VALID)])
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for router aliases
|
|
# ============================================================================
|
|
|
|
|
|
def test_router_aliases_returns_command_aliases() -> None:
|
|
router = Router()
|
|
@router.command(Command('some', aliases={'test', 'case'}))
|
|
def handler(_response: Response) -> None:
|
|
pass
|
|
assert router.aliases == {'test', 'case'}
|
|
|
|
|
|
def test_router_aliases_returns_combined_aliases_from_multiple_commands() -> None:
|
|
router = Router()
|
|
@router.command(Command('some', aliases={'test', 'case'}))
|
|
def handler(_response: Response) -> None:
|
|
pass
|
|
@router.command(Command('ext', aliases={'more', 'foo'}))
|
|
def handler2(_response: Response) -> None:
|
|
pass
|
|
assert router.aliases == {'test', 'case', 'more', 'foo'}
|
|
|
|
|
|
def test_router_aliases_returns_empty_set_when_no_aliases() -> None:
|
|
router = Router()
|
|
@router.command(Command('some'))
|
|
def handler(_response: Response) -> None:
|
|
pass
|
|
assert router.aliases == set()
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for handler finding and execution
|
|
# ============================================================================
|
|
|
|
|
|
def test_finds_appropriate_handler_executes_handler_by_alias(capsys: CaptureFixture[str]) -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', aliases={'hi'}))
|
|
def handler(_res: Response) -> None:
|
|
print("Hello World!")
|
|
|
|
router.finds_appropriate_handler(InputCommand('hi'))
|
|
|
|
output = capsys.readouterr()
|
|
|
|
assert "Hello World!" in output.out
|
|
|
|
def test_finds_appropriate_handler_executes_handler_by_alias_case_insensitive(capsys: CaptureFixture[str]) -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', aliases={'hI'}))
|
|
def handler(_res: Response) -> None:
|
|
print("Hello World!")
|
|
|
|
router.finds_appropriate_handler(InputCommand('HI'))
|
|
|
|
output = capsys.readouterr()
|
|
|
|
assert "Hello World!" in output.out
|
|
|
|
|
|
def test_finds_appropriate_handler_executes_handler_by_trigger_case_insensitive(capsys: CaptureFixture[str]) -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('heLLo'))
|
|
def handler(_res: Response) -> None:
|
|
print("Hello World!")
|
|
|
|
router.finds_appropriate_handler(InputCommand('HellO'))
|
|
|
|
output = capsys.readouterr()
|
|
|
|
assert "Hello World!" in output.out
|
|
|
|
|
|
def test_finds_appropriate_handler_executes_handler_with_flags_by_alias(capsys: CaptureFixture[str]) -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', flags=Flag('flag'), aliases={'hi'}))
|
|
def handler(_res: Response) -> None:
|
|
print("Hello World!")
|
|
|
|
router.finds_appropriate_handler(InputCommand('hi'))
|
|
|
|
output = capsys.readouterr()
|
|
|
|
assert "Hello World!" in output.out
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for alias and trigger collision detection
|
|
# ============================================================================
|
|
|
|
|
|
def test_validate_command_raises_error_for_alias_collision_with_existing_trigger() -> None:
|
|
router = Router()
|
|
|
|
@router.command('hello')
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
with pytest.raises(RepeatedAliasNameException):
|
|
@router.command(Command('world', aliases={'hello'}))
|
|
def handler2(_res: Response) -> None:
|
|
pass
|
|
|
|
|
|
def test_validate_command_raises_error_for_alias_collision_with_existing_alias() -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', aliases={'hi'}))
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
with pytest.raises(RepeatedAliasNameException):
|
|
@router.command(Command('world', aliases={'hi'}))
|
|
def handler2(_res: Response) -> None:
|
|
pass
|
|
|
|
|
|
def test_validate_command_raises_error_for_trigger_collision_with_existing_alias() -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', aliases={'hi'}))
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
with pytest.raises(RepeatedAliasNameException):
|
|
@router.command('hi')
|
|
def handler2(_res: Response) -> None:
|
|
pass
|
|
|
|
|
|
def test_validate_command_raises_error_for_alias_collision_case_insensitive() -> None:
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', aliases={'Hi'}))
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
with pytest.raises(RepeatedAliasNameException):
|
|
@router.command(Command('world', aliases={'hI'}))
|
|
def handler2(_res: Response) -> None:
|
|
pass
|
|
|
|
|
|
# ============================================================================
|
|
# Tests for RegisteredRouters
|
|
# ============================================================================
|
|
|
|
|
|
def test_registered_routers_get_router_by_trigger() -> None:
|
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
|
|
|
registered_routers = RegisteredRouters()
|
|
router = Router()
|
|
|
|
@router.command('hello')
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
registered_routers.add_registered_router(router)
|
|
|
|
assert registered_routers.get_router_by_trigger('hello') == router
|
|
|
|
|
|
def test_registered_routers_get_router_by_alias() -> None:
|
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
|
|
|
registered_routers = RegisteredRouters()
|
|
router = Router()
|
|
|
|
@router.command(Command('hello', aliases={'hi'}))
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
registered_routers.add_registered_router(router)
|
|
|
|
assert registered_routers.get_router_by_trigger('hi') == router
|
|
|
|
|
|
def test_registered_routers_get_router_case_insensitive() -> None:
|
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
|
|
|
registered_routers = RegisteredRouters()
|
|
router = Router()
|
|
|
|
@router.command(Command('HeLLo'))
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
registered_routers.add_registered_router(router)
|
|
|
|
# Trigger stored in lowercase, should match regardless of case
|
|
assert registered_routers.get_router_by_trigger('hello') == router
|
|
assert registered_routers.get_router_by_trigger('HELLO') is None # Exact match required in dict
|
|
|
|
|
|
def test_registered_routers_get_triggers_returns_all_triggers_and_aliases() -> None:
|
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
|
|
|
registered_routers = RegisteredRouters()
|
|
router1 = Router()
|
|
router2 = Router()
|
|
|
|
@router1.command(Command('hello', aliases={'hi'}))
|
|
def handler1(_res: Response) -> None:
|
|
pass
|
|
|
|
@router2.command(Command('world', aliases={'w'}))
|
|
def handler2(_res: Response) -> None:
|
|
pass
|
|
|
|
registered_routers.add_registered_router(router1)
|
|
registered_routers.add_registered_router(router2)
|
|
|
|
triggers = registered_routers.get_triggers()
|
|
assert 'hello' in triggers
|
|
assert 'hi' in triggers
|
|
assert 'world' in triggers
|
|
assert 'w' in triggers
|
|
|
|
|
|
def test_registered_routers_returns_none_for_unknown_trigger() -> None:
|
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
|
|
|
registered_routers = RegisteredRouters()
|
|
router = Router()
|
|
|
|
@router.command('hello')
|
|
def handler(_res: Response) -> None:
|
|
pass
|
|
|
|
registered_routers.add_registered_router(router)
|
|
|
|
assert registered_routers.get_router_by_trigger('unknown') is None
|