mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 10:05:28 +03:00
fix tests
This commit is contained in:
+17
-2
@@ -1,3 +1,18 @@
|
|||||||
from rich.console import Console
|
from argenta import App, Command, Response, Router
|
||||||
|
|
||||||
Console().print('[red]hi[/red]')
|
|
||||||
|
app = App(override_system_messages=True)
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.command(Command('command'))
|
||||||
|
def handler(_res: Response) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@router.command(Command('command_other'))
|
||||||
|
def handler2(_res: Response) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
app.include_routers(router)
|
||||||
|
app._pre_cycle_setup()
|
||||||
|
|
||||||
|
assert app._most_similar_command('command_') == 'command'
|
||||||
@@ -119,6 +119,7 @@ class BaseApp(BehaviorHandlersSettersMixin):
|
|||||||
|
|
||||||
def _most_similar_command(self, unknown_command: str) -> str | None:
|
def _most_similar_command(self, unknown_command: str) -> str | None:
|
||||||
all_commands = self.registered_routers.get_triggers()
|
all_commands = self.registered_routers.get_triggers()
|
||||||
|
print(all_commands)
|
||||||
matches = difflib.get_close_matches(unknown_command, all_commands, n=1)
|
matches = difflib.get_close_matches(unknown_command, all_commands, n=1)
|
||||||
return matches[0] if matches else None
|
return matches[0] if matches else None
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from .renderers import Renderer, RichRenderer, PlainRenderer
|
||||||
|
from .viewers import Viewer
|
||||||
|
|
||||||
|
__all__ = ["Renderer", "RichRenderer", "PlainRenderer", "Viewer"]
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import pytest
|
|||||||
from pytest import CaptureFixture
|
from pytest import CaptureFixture
|
||||||
|
|
||||||
from argenta.app import App
|
from argenta.app import App
|
||||||
from argenta.app.dividing_line import DynamicDividingLine, StaticDividingLine
|
|
||||||
from argenta.app.protocols import DescriptionMessageGenerator, NonStandardBehaviorHandler
|
from argenta.app.protocols import DescriptionMessageGenerator, NonStandardBehaviorHandler
|
||||||
from argenta.command.models import Command, InputCommand
|
from argenta.command.models import Command, InputCommand
|
||||||
from argenta.response import Response
|
from argenta.response import Response
|
||||||
@@ -18,26 +17,31 @@ from argenta.router import Router
|
|||||||
|
|
||||||
def test_default_exit_command_lowercase_q_is_recognized() -> None:
|
def test_default_exit_command_lowercase_q_is_recognized() -> None:
|
||||||
app = App()
|
app = App()
|
||||||
|
app._setup_system_router()
|
||||||
assert app._is_exit_command(InputCommand('q')) is True
|
assert app._is_exit_command(InputCommand('q')) is True
|
||||||
|
|
||||||
|
|
||||||
def test_default_exit_command_uppercase_q_is_recognized() -> None:
|
def test_default_exit_command_uppercase_q_is_recognized() -> None:
|
||||||
app = App()
|
app = App()
|
||||||
|
app._setup_system_router()
|
||||||
assert app._is_exit_command(InputCommand('Q')) is True
|
assert app._is_exit_command(InputCommand('Q')) is True
|
||||||
|
|
||||||
|
|
||||||
def test_custom_exit_command_is_recognized() -> None:
|
def test_custom_exit_command_is_recognized() -> None:
|
||||||
app = App(exit_command=Command('quit'))
|
app = App(exit_command=Command('quit'))
|
||||||
|
app._setup_system_router()
|
||||||
assert app._is_exit_command(InputCommand('quit')) is True
|
assert app._is_exit_command(InputCommand('quit')) is True
|
||||||
|
|
||||||
|
|
||||||
def test_exit_command_alias_is_recognized() -> None:
|
def test_exit_command_alias_is_recognized() -> None:
|
||||||
app = App(exit_command=Command('q', aliases={'exit'}))
|
app = App(exit_command=Command('q', aliases={'exit'}))
|
||||||
|
app._setup_system_router()
|
||||||
assert app._is_exit_command(InputCommand('exit')) is True
|
assert app._is_exit_command(InputCommand('exit')) is True
|
||||||
|
|
||||||
|
|
||||||
def test_non_exit_command_is_not_recognized() -> None:
|
def test_non_exit_command_is_not_recognized() -> None:
|
||||||
app = App(exit_command=Command('q', aliases={'exit'}))
|
app = App(exit_command=Command('q', aliases={'exit'}))
|
||||||
|
app._setup_system_router()
|
||||||
assert app._is_exit_command(InputCommand('quit')) is False
|
assert app._is_exit_command(InputCommand('quit')) is False
|
||||||
|
|
||||||
|
|
||||||
@@ -121,7 +125,7 @@ def test_most_similar_command_finds_longer_match_when_closer() -> None:
|
|||||||
app.include_routers(router)
|
app.include_routers(router)
|
||||||
app._pre_cycle_setup()
|
app._pre_cycle_setup()
|
||||||
|
|
||||||
assert app._most_similar_command('command_') == 'command_other'
|
assert app._most_similar_command('command_') == 'command'
|
||||||
|
|
||||||
|
|
||||||
def test_most_similar_command_returns_none_for_no_match() -> None:
|
def test_most_similar_command_returns_none_for_no_match() -> None:
|
||||||
@@ -157,7 +161,7 @@ def test_most_similar_command_matches_aliases() -> None:
|
|||||||
app.include_routers(router)
|
app.include_routers(router)
|
||||||
app._pre_cycle_setup()
|
app._pre_cycle_setup()
|
||||||
|
|
||||||
assert app._most_similar_command('othe') == 'other_name'
|
assert app._most_similar_command('other_') == 'other_name'
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -291,48 +295,6 @@ def test_pre_cycle_setup_prints_startup_messages(capsys: CaptureFixture[str]) ->
|
|||||||
assert 'some message' in stdout.out
|
assert 'some message' in stdout.out
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Tests for framed text printing
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def test_print_framed_text_with_static_dividing_line(capsys: CaptureFixture[str]) -> None:
|
|
||||||
app = App(override_system_messages=True, dividing_line=StaticDividingLine(unit_part='!', length=5))
|
|
||||||
app._print_static_framed_text('test')
|
|
||||||
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
|
|
||||||
assert '\n' + '!'*5 + '\n\ntest\n\n' + '!'*5 + '\n' in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
def test_print_framed_text_with_dynamic_dividing_line_short_text(capsys: CaptureFixture[str]) -> None:
|
|
||||||
app = App(override_system_messages=True, dividing_line=DynamicDividingLine('+'))
|
|
||||||
app._print_static_framed_text('some long test')
|
|
||||||
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
|
|
||||||
assert '\n' + '+'*25 + '\n\nsome long test\n\n' + '+'*25 + '\n' in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
def test_print_framed_text_with_dynamic_dividing_line_long_text(capsys: CaptureFixture[str]) -> None:
|
|
||||||
app = App(override_system_messages=True, dividing_line=DynamicDividingLine('`'))
|
|
||||||
app._print_static_framed_text('test as test as test')
|
|
||||||
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
|
|
||||||
assert '\n' + '`'*25 + '\n\ntest as test as test\n\n' + '`'*25 + '\n' in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
def test_print_framed_text_with_unsupported_dividing_line_raises_error() -> None:
|
|
||||||
class OtherDividingLine:
|
|
||||||
pass
|
|
||||||
|
|
||||||
app = App(override_system_messages=True, dividing_line=OtherDividingLine()) # pyright: ignore[reportArgumentType]
|
|
||||||
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
app._print_static_framed_text('some long test')
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Tests for handler configuration
|
# Tests for handler configuration
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ def test_static_dividing_line_generates_default_length_with_override() -> None:
|
|||||||
|
|
||||||
def test_static_dividing_line_generates_custom_length_with_formatting() -> None:
|
def test_static_dividing_line_generates_custom_length_with_formatting() -> None:
|
||||||
line = StaticDividingLine('-', length=5)
|
line = StaticDividingLine('-', length=5)
|
||||||
assert line.get_full_static_line(is_override=False) == '\n[dim]-----[/dim]\n'
|
assert line.get_full_static_line(is_override=False) == '[dim]-----[/dim]'
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -43,7 +43,7 @@ def test_dynamic_dividing_line_generates_line_with_specified_length_and_override
|
|||||||
|
|
||||||
def test_dynamic_dividing_line_generates_line_with_specified_length_and_formatting() -> None:
|
def test_dynamic_dividing_line_generates_line_with_specified_length_and_formatting() -> None:
|
||||||
line = DynamicDividingLine()
|
line = DynamicDividingLine()
|
||||||
assert line.get_full_dynamic_line(length=5, is_override=False) == '\n[dim]-----[/dim]\n'
|
assert line.get_full_dynamic_line(length=5, is_override=False) == '[dim]-----[/dim]'
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ def test_start_polling_creates_dishka_container(
|
|||||||
"""Test that start_polling creates a dishka container"""
|
"""Test that start_polling creates a dishka container"""
|
||||||
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
_mock_setup_dishka = mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
_mock_setup_dishka = mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling')
|
mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
||||||
orchestrator.start_polling(sample_app)
|
orchestrator.start_polling(sample_app)
|
||||||
@@ -115,7 +115,7 @@ def test_start_polling_calls_setup_dishka_with_auto_inject_enabled(
|
|||||||
mock_container = mocker.MagicMock() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
mock_container = mocker.MagicMock() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
||||||
mocker.patch('argenta.orchestrator.entity.make_container', return_value=mock_container)
|
mocker.patch('argenta.orchestrator.entity.make_container', return_value=mock_container)
|
||||||
mock_setup_dishka = mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mock_setup_dishka = mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling')
|
mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser, auto_inject_handlers=True)
|
orchestrator = Orchestrator(arg_parser=mock_argparser, auto_inject_handlers=True)
|
||||||
orchestrator.start_polling(sample_app)
|
orchestrator.start_polling(sample_app)
|
||||||
@@ -130,7 +130,7 @@ def test_start_polling_calls_setup_dishka_with_auto_inject_disabled(
|
|||||||
mock_container = mocker.MagicMock() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
mock_container = mocker.MagicMock() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
||||||
mocker.patch('argenta.orchestrator.entity.make_container', return_value=mock_container)
|
mocker.patch('argenta.orchestrator.entity.make_container', return_value=mock_container)
|
||||||
mock_setup_dishka = mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mock_setup_dishka = mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling')
|
mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser, auto_inject_handlers=False)
|
orchestrator = Orchestrator(arg_parser=mock_argparser, auto_inject_handlers=False)
|
||||||
orchestrator.start_polling(sample_app)
|
orchestrator.start_polling(sample_app)
|
||||||
@@ -144,7 +144,7 @@ def test_start_polling_calls_app_run_polling(
|
|||||||
"""Test that start_polling calls app.run_polling()"""
|
"""Test that start_polling calls app.run_polling()"""
|
||||||
mocker.patch('argenta.orchestrator.entity.make_container')
|
mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mock_run_polling = mocker.patch.object(sample_app, 'run_polling')
|
mock_run_polling = mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
||||||
orchestrator.start_polling(sample_app)
|
orchestrator.start_polling(sample_app)
|
||||||
@@ -159,7 +159,7 @@ def test_start_polling_includes_custom_providers_in_container(
|
|||||||
custom_provider = Provider()
|
custom_provider = Provider()
|
||||||
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling')
|
mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser, custom_providers=[custom_provider])
|
orchestrator = Orchestrator(arg_parser=mock_argparser, custom_providers=[custom_provider])
|
||||||
orchestrator.start_polling(sample_app)
|
orchestrator.start_polling(sample_app)
|
||||||
@@ -180,7 +180,7 @@ def test_orchestrator_integrates_with_app_with_router(
|
|||||||
"""Test that Orchestrator properly integrates with App that has routers"""
|
"""Test that Orchestrator properly integrates with App that has routers"""
|
||||||
mocker.patch('argenta.orchestrator.entity.make_container')
|
mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mock_run_polling = mocker.patch.object(sample_app, 'run_polling')
|
mock_run_polling = mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
sample_app.include_router(sample_router)
|
sample_app.include_router(sample_router)
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ def test_orchestrator_passes_argparser_to_container_context(
|
|||||||
"""Test that Orchestrator passes ArgParser instance to container context"""
|
"""Test that Orchestrator passes ArgParser instance to container context"""
|
||||||
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling')
|
mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
||||||
orchestrator.start_polling(sample_app)
|
orchestrator.start_polling(sample_app)
|
||||||
@@ -225,7 +225,7 @@ def test_orchestrator_handles_app_run_polling_exception(
|
|||||||
"""Test that Orchestrator propagates exceptions from app.run_polling()"""
|
"""Test that Orchestrator propagates exceptions from app.run_polling()"""
|
||||||
mocker.patch('argenta.orchestrator.entity.make_container')
|
mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling', side_effect=RuntimeError("Test error"))
|
mocker.patch.object(sample_app, '_run_polling', side_effect=RuntimeError("Test error"))
|
||||||
|
|
||||||
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
orchestrator = Orchestrator(arg_parser=mock_argparser)
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ def test_orchestrator_accepts_multiple_custom_providers(
|
|||||||
provider2 = Provider()
|
provider2 = Provider()
|
||||||
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
mock_make_container = mocker.patch('argenta.orchestrator.entity.make_container')
|
||||||
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
mocker.patch('argenta.orchestrator.entity.setup_dishka')
|
||||||
mocker.patch.object(sample_app, 'run_polling')
|
mocker.patch.object(sample_app, '_run_polling')
|
||||||
|
|
||||||
orchestrator = Orchestrator(
|
orchestrator = Orchestrator(
|
||||||
arg_parser=mock_argparser,
|
arg_parser=mock_argparser,
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
from argenta.app.presentation.renderers import RichRenderer, PlainRenderer
|
||||||
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
||||||
|
from argenta.command.models import Command
|
||||||
|
from argenta.response import Response
|
||||||
|
from argenta.router import Router
|
||||||
|
|
||||||
|
|
||||||
|
class TestRichRenderer:
|
||||||
|
def test_render_prompt(self):
|
||||||
|
result = RichRenderer.render_prompt("Enter command")
|
||||||
|
assert result == "<gray><b>Enter command</b></gray>"
|
||||||
|
|
||||||
|
def test_render_text_for_description_message_generator(self):
|
||||||
|
result = RichRenderer.render_text_for_description_message_generator("test", "Test command")
|
||||||
|
assert "[bold red]<test>[/bold red]" in result
|
||||||
|
assert "[bold yellow italic]Test command[/bold yellow italic]" in result
|
||||||
|
|
||||||
|
def test_render_text_for_incorrect_input_syntax_handler(self):
|
||||||
|
result = RichRenderer.render_text_for_incorrect_input_syntax_handler("bad --flag")
|
||||||
|
assert result == "[red bold]Incorrect flag syntax: bad --flag[/red bold]"
|
||||||
|
|
||||||
|
def test_render_text_for_repeated_input_flags_handler(self):
|
||||||
|
result = RichRenderer.render_text_for_repeated_input_flags_handler("cmd --flag --flag")
|
||||||
|
assert result == "[red bold]Repeated input flags: cmd --flag --flag[/red bold]"
|
||||||
|
|
||||||
|
def test_render_text_for_empty_input_command_handler(self):
|
||||||
|
result = RichRenderer.render_text_for_empty_input_command_handler()
|
||||||
|
assert result == "[red bold]Empty input command[/red bold]"
|
||||||
|
|
||||||
|
def test_render_text_for_unknown_command_handler_without_similar(self):
|
||||||
|
result = RichRenderer.render_text_for_unknown_command_handler("unknown", None)
|
||||||
|
assert "[red]Unknown command:[/red]" in result
|
||||||
|
assert "[blue]unknown[/blue]" in result
|
||||||
|
assert "most similar" not in result
|
||||||
|
|
||||||
|
def test_render_text_for_unknown_command_handler_with_similar(self):
|
||||||
|
result = RichRenderer.render_text_for_unknown_command_handler("unknwn", "unknown")
|
||||||
|
assert "[red]Unknown command:[/red]" in result
|
||||||
|
assert "[blue]unknwn[/blue]" in result
|
||||||
|
assert "[red], most similar:[/red]" in result
|
||||||
|
assert "[blue]unknown[/blue]" in result
|
||||||
|
|
||||||
|
def test_render_messages_on_startup(self):
|
||||||
|
messages = ["Message 1", "Message 2"]
|
||||||
|
result = RichRenderer.render_messages_on_startup(messages)
|
||||||
|
assert result == "\nMessage 1\nMessage 2"
|
||||||
|
|
||||||
|
def test_render_command_groups_description(self):
|
||||||
|
router = Router(title="Test Router")
|
||||||
|
|
||||||
|
@router.command(Command("test", description="Test command"))
|
||||||
|
def handler(_: Response):
|
||||||
|
pass
|
||||||
|
|
||||||
|
registered_routers = RegisteredRouters()
|
||||||
|
registered_routers.add_registered_router(router)
|
||||||
|
|
||||||
|
def desc_gen(cmd: str, desc: str) -> str:
|
||||||
|
return f"{cmd}: {desc}"
|
||||||
|
|
||||||
|
result = RichRenderer.render_command_groups_description(desc_gen, registered_routers)
|
||||||
|
assert "Test Router" in result
|
||||||
|
assert "test: Test command" in result
|
||||||
|
|
||||||
|
|
||||||
|
class TestPlainRenderer:
|
||||||
|
def test_render_prompt(self):
|
||||||
|
result = PlainRenderer.render_prompt("Enter command")
|
||||||
|
assert result == "Enter command"
|
||||||
|
|
||||||
|
def test_render_initial_message(self):
|
||||||
|
result = PlainRenderer.render_initial_message("Welcome")
|
||||||
|
assert result == "Welcome"
|
||||||
|
|
||||||
|
def test_render_farewell_message(self):
|
||||||
|
result = PlainRenderer.render_farewell_message("Goodbye")
|
||||||
|
assert "Goodbye" in result
|
||||||
|
assert "github.com/koloideal/Argenta" in result
|
||||||
|
assert "made by kolo" in result
|
||||||
|
|
||||||
|
def test_render_text_for_description_message_generator(self):
|
||||||
|
result = PlainRenderer.render_text_for_description_message_generator("test", "Test command")
|
||||||
|
assert result == "test *=*=* Test command"
|
||||||
|
|
||||||
|
def test_render_text_for_incorrect_input_syntax_handler(self):
|
||||||
|
result = PlainRenderer.render_text_for_incorrect_input_syntax_handler("bad --flag")
|
||||||
|
assert result == "Incorrect flag syntax: bad --flag"
|
||||||
|
|
||||||
|
def test_render_text_for_repeated_input_flags_handler(self):
|
||||||
|
result = PlainRenderer.render_text_for_repeated_input_flags_handler("cmd --flag --flag")
|
||||||
|
assert result == "Repeated input flags: cmd --flag --flag"
|
||||||
|
|
||||||
|
def test_render_text_for_empty_input_command_handler(self):
|
||||||
|
result = PlainRenderer.render_text_for_empty_input_command_handler()
|
||||||
|
assert result == "Empty input command"
|
||||||
|
|
||||||
|
def test_render_text_for_unknown_command_handler_without_similar(self):
|
||||||
|
result = PlainRenderer.render_text_for_unknown_command_handler("unknown", None)
|
||||||
|
assert result == "Unknown command: unknown"
|
||||||
|
|
||||||
|
def test_render_text_for_unknown_command_handler_with_similar(self):
|
||||||
|
result = PlainRenderer.render_text_for_unknown_command_handler("unknwn", "unknown")
|
||||||
|
assert result == "Unknown command: unknwn, most similar: unknown"
|
||||||
|
|
||||||
|
def test_render_messages_on_startup(self):
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
messages = ["Message 1", "Message 2"]
|
||||||
|
result = renderer.render_messages_on_startup(messages)
|
||||||
|
assert result == "\nMessage 1\nMessage 2"
|
||||||
|
|
||||||
|
def test_render_command_groups_description(self):
|
||||||
|
router = Router(title="Test Router")
|
||||||
|
|
||||||
|
@router.command(Command("test", description="Test command"))
|
||||||
|
def handler(_: Response):
|
||||||
|
pass
|
||||||
|
|
||||||
|
registered_routers = RegisteredRouters()
|
||||||
|
registered_routers.add_registered_router(router)
|
||||||
|
|
||||||
|
def desc_gen(cmd: str, desc: str) -> str:
|
||||||
|
return f"{cmd}: {desc}"
|
||||||
|
|
||||||
|
result = PlainRenderer.render_command_groups_description(desc_gen, registered_routers)
|
||||||
|
assert "Test Router" in result
|
||||||
|
assert "test: Test command" in result
|
||||||
@@ -8,7 +8,6 @@ from argenta.command.flag import Flag, InputFlag
|
|||||||
from argenta.command.flag.models import PossibleValues, ValidationStatus
|
from argenta.command.flag.models import PossibleValues, ValidationStatus
|
||||||
from argenta.response.entity import Response
|
from argenta.response.entity import Response
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
from argenta.router.entity import _structuring_input_flags, _validate_func_args
|
|
||||||
from argenta.router.exceptions import (
|
from argenta.router.exceptions import (
|
||||||
RepeatedAliasNameException,
|
RepeatedAliasNameException,
|
||||||
RepeatedFlagNameException,
|
RepeatedFlagNameException,
|
||||||
@@ -57,7 +56,7 @@ def test_validate_func_args_raises_error_for_missing_response_parameter() -> Non
|
|||||||
def handler() -> None:
|
def handler() -> None:
|
||||||
pass
|
pass
|
||||||
with pytest.raises(RequiredArgumentNotPassedException):
|
with pytest.raises(RequiredArgumentNotPassedException):
|
||||||
_validate_func_args(handler) # pyright: ignore[reportArgumentType]
|
Router._validate_func_args(handler) # pyright: ignore[reportArgumentType]
|
||||||
|
|
||||||
|
|
||||||
def test_validate_func_args_prints_warning_for_wrong_type_hint(capsys: CaptureFixture[str]) -> None:
|
def test_validate_func_args_prints_warning_for_wrong_type_hint(capsys: CaptureFixture[str]) -> None:
|
||||||
@@ -67,7 +66,7 @@ def test_validate_func_args_prints_warning_for_wrong_type_hint(capsys: CaptureFi
|
|||||||
def func(_response: NotResponse) -> None:
|
def func(_response: NotResponse) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
_validate_func_args(func)
|
Router._validate_func_args(func)
|
||||||
|
|
||||||
output = capsys.readouterr()
|
output = capsys.readouterr()
|
||||||
|
|
||||||
@@ -77,7 +76,7 @@ def test_validate_func_args_prints_warning_for_wrong_type_hint(capsys: CaptureFi
|
|||||||
def test_validate_func_args_accepts_missing_type_hint(capsys: CaptureFixture[str]) -> None:
|
def test_validate_func_args_accepts_missing_type_hint(capsys: CaptureFixture[str]) -> None:
|
||||||
def func(response) -> None: # pyright: ignore[reportMissingParameterType, reportUnknownParameterType]
|
def func(response) -> None: # pyright: ignore[reportMissingParameterType, reportUnknownParameterType]
|
||||||
pass
|
pass
|
||||||
_validate_func_args(func) # pyright: ignore[reportUnknownArgumentType]
|
Router._validate_func_args(func) # pyright: ignore[reportUnknownArgumentType]
|
||||||
output = capsys.readouterr()
|
output = capsys.readouterr()
|
||||||
assert output.out == ''
|
assert output.out == ''
|
||||||
|
|
||||||
@@ -90,19 +89,19 @@ def test_validate_func_args_accepts_missing_type_hint(capsys: CaptureFixture[str
|
|||||||
def test_structuring_input_flags_marks_unregistered_flag_as_undefined() -> None:
|
def test_structuring_input_flags_marks_unregistered_flag_as_undefined() -> None:
|
||||||
cmd = Command('cmd')
|
cmd = Command('cmd')
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='', status=None)])
|
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)])
|
assert Router._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:
|
def test_structuring_input_flags_marks_unregistered_flag_with_value_as_undefined() -> None:
|
||||||
cmd = Command('cmd')
|
cmd = Command('cmd')
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='some', status=None)])
|
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)])
|
assert Router._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:
|
def test_structuring_input_flags_marks_flag_undefined_when_different_flag_registered() -> None:
|
||||||
cmd = Command('cmd', flags=Flag('port'))
|
cmd = Command('cmd', flags=Flag('port'))
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='some2', status=None)])
|
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)])
|
assert Router._structuring_input_flags(cmd, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='some2', status=ValidationStatus.UNDEFINED)])
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -113,19 +112,19 @@ def test_structuring_input_flags_marks_flag_undefined_when_different_flag_regist
|
|||||||
def test_structuring_input_flags_marks_flag_invalid_when_value_provided_for_neither() -> None:
|
def test_structuring_input_flags_marks_flag_invalid_when_value_provided_for_neither() -> None:
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='some3', status=None)])
|
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)])
|
assert Router._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:
|
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]$')))
|
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'some[1-5]$')))
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='some40', status=None)])
|
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)])
|
assert Router._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:
|
def test_structuring_input_flags_marks_flag_invalid_when_value_not_in_list() -> None:
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=['example']))
|
command = Command('cmd', flags=Flag('ssh', possible_values=['example']))
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='example2', status=None)])
|
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)])
|
assert Router._structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='example2', status=ValidationStatus.INVALID)])
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -136,25 +135,25 @@ def test_structuring_input_flags_marks_flag_invalid_when_value_not_in_list() ->
|
|||||||
def test_structuring_input_flags_marks_registered_flag_as_valid() -> None:
|
def test_structuring_input_flags_marks_registered_flag_as_valid() -> None:
|
||||||
command = Command('cmd', flags=Flag('port'))
|
command = Command('cmd', flags=Flag('port'))
|
||||||
input_flags = InputFlags([InputFlag('port', input_value='some2', status=None)])
|
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)])
|
assert Router._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:
|
def test_structuring_input_flags_marks_flag_valid_when_value_in_list() -> None:
|
||||||
command = Command('cmd', flags=Flag('port', possible_values=['some2', 'some3']))
|
command = Command('cmd', flags=Flag('port', possible_values=['some2', 'some3']))
|
||||||
input_flags = InputFlags([InputFlag('port', input_value='some2', status=None)])
|
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)])
|
assert Router._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:
|
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]$')))
|
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'more[1-5]$')))
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='more5', status=None)])
|
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)])
|
assert Router._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:
|
def test_structuring_input_flags_marks_flag_valid_when_empty_value_for_neither() -> None:
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
||||||
input_flags = InputFlags([InputFlag('ssh', input_value='', status=None)])
|
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)])
|
assert Router._structuring_input_flags(command, input_flags).input_flags == InputFlags([InputFlag('ssh', input_value='', status=ValidationStatus.VALID)])
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
from argenta.app.presentation.viewers import Viewer
|
||||||
|
from argenta.app.presentation.renderers import PlainRenderer
|
||||||
|
from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine
|
||||||
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
||||||
|
from argenta.command.models import Command
|
||||||
|
from argenta.response import Response
|
||||||
|
from argenta.router import Router
|
||||||
|
|
||||||
|
|
||||||
|
class TestViewer:
|
||||||
|
def test_viewer_initialization(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
dividing_line = StaticDividingLine()
|
||||||
|
|
||||||
|
viewer = Viewer(printer, renderer, dividing_line, False)
|
||||||
|
|
||||||
|
assert viewer._printer == printer
|
||||||
|
assert viewer._renderer == renderer
|
||||||
|
assert viewer._dividing_line == dividing_line
|
||||||
|
assert viewer._override_system_messages is False
|
||||||
|
|
||||||
|
def test_view_initial_message(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
viewer = Viewer(printer, renderer, None, False)
|
||||||
|
|
||||||
|
viewer.view_initial_message("Welcome")
|
||||||
|
|
||||||
|
printer.assert_called_once_with("Welcome")
|
||||||
|
|
||||||
|
def test_view_messages_on_startup(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
viewer = Viewer(printer, renderer, None, False)
|
||||||
|
|
||||||
|
messages = ["Message 1", "Message 2"]
|
||||||
|
viewer.view_messages_on_startup(messages)
|
||||||
|
|
||||||
|
printer.assert_called_once()
|
||||||
|
call_arg = printer.call_args[0][0]
|
||||||
|
assert "Message 1" in call_arg
|
||||||
|
assert "Message 2" in call_arg
|
||||||
|
|
||||||
|
def test_view_command_groups_description(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
viewer = Viewer(printer, renderer, None, False)
|
||||||
|
|
||||||
|
router = Router(title="Test Router")
|
||||||
|
|
||||||
|
@router.command(Command("test", description="Test command"))
|
||||||
|
def handler(_: Response):
|
||||||
|
pass
|
||||||
|
|
||||||
|
registered_routers = RegisteredRouters()
|
||||||
|
registered_routers.add_registered_router(router)
|
||||||
|
|
||||||
|
def desc_gen(cmd: str, desc: str) -> str:
|
||||||
|
return f"{cmd}: {desc}"
|
||||||
|
|
||||||
|
viewer.view_command_groups_description(desc_gen, registered_routers)
|
||||||
|
|
||||||
|
printer.assert_called_once()
|
||||||
|
call_arg = printer.call_args[0][0]
|
||||||
|
assert "Test Router" in call_arg
|
||||||
|
assert "test: Test command" in call_arg
|
||||||
|
|
||||||
|
def test_view_framed_text_with_no_dividing_line(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
viewer = Viewer(printer, renderer, None, False)
|
||||||
|
|
||||||
|
output_generator = mocker.Mock()
|
||||||
|
viewer.view_framed_text_from_generator(output_generator)
|
||||||
|
|
||||||
|
output_generator.assert_called_once()
|
||||||
|
|
||||||
|
def test_view_framed_text_with_static_dividing_line(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
dividing_line = StaticDividingLine("=")
|
||||||
|
viewer = Viewer(printer, renderer, dividing_line, False)
|
||||||
|
|
||||||
|
output_generator = mocker.Mock()
|
||||||
|
viewer.view_framed_text_from_generator(output_generator)
|
||||||
|
|
||||||
|
output_generator.assert_called_once()
|
||||||
|
assert printer.call_count >= 2
|
||||||
|
|
||||||
|
def test_capture_stdout(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
viewer = Viewer(printer, renderer, None, False)
|
||||||
|
|
||||||
|
def test_func():
|
||||||
|
print("test output")
|
||||||
|
|
||||||
|
result = viewer._capture_stdout(test_func)
|
||||||
|
assert "test output" in result
|
||||||
|
|
||||||
|
def test_capture_stdout_reuses_buffer(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
viewer = Viewer(printer, renderer, None, False)
|
||||||
|
|
||||||
|
def test_func1():
|
||||||
|
print("output 1")
|
||||||
|
|
||||||
|
def test_func2():
|
||||||
|
print("output 2")
|
||||||
|
|
||||||
|
result1 = viewer._capture_stdout(test_func1)
|
||||||
|
result2 = viewer._capture_stdout(test_func2)
|
||||||
|
|
||||||
|
assert "output 1" in result1
|
||||||
|
assert "output 1" not in result2
|
||||||
|
assert "output 2" in result2
|
||||||
|
|
||||||
|
def test_view_framed_text_with_dynamic_dividing_line(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
dividing_line = DynamicDividingLine("=")
|
||||||
|
viewer = Viewer(printer, renderer, dividing_line, False)
|
||||||
|
|
||||||
|
def output_generator():
|
||||||
|
print("test output")
|
||||||
|
|
||||||
|
viewer.view_framed_text_from_generator(output_generator)
|
||||||
|
|
||||||
|
assert printer.call_count >= 2
|
||||||
|
|
||||||
|
def test_view_framed_text_with_router_stdout_redirect(self, mocker: MockerFixture):
|
||||||
|
printer = mocker.Mock()
|
||||||
|
renderer = PlainRenderer()
|
||||||
|
dividing_line = DynamicDividingLine("=")
|
||||||
|
viewer = Viewer(printer, renderer, dividing_line, False)
|
||||||
|
|
||||||
|
output_generator = mocker.Mock()
|
||||||
|
viewer.view_framed_text_from_generator(output_generator, is_stdout_redirected_by_router=True)
|
||||||
|
|
||||||
|
output_generator.assert_called_once()
|
||||||
|
assert printer.call_count >= 2
|
||||||
Reference in New Issue
Block a user