mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 10:05:28 +03:00
ce7e24b924
The entire public api is covered with documentation in two languages - Russian and English. the library now supports the latest three versions of python - 3.12, 3.13 and 3.14 minor design changes: now, when a Boolean flag is entered, its value is an empty string, not None. tests have been adapted to the supported versions of python, readmi has been redesigned in two languages, German is no longer available.
250 lines
10 KiB
Python
250 lines
10 KiB
Python
from argparse import Namespace
|
|
import sys
|
|
from unittest.mock import call
|
|
|
|
import pytest
|
|
|
|
from argenta.orchestrator.argparser.arguments.models import (BaseArgument,
|
|
BooleanArgument,
|
|
InputArgument,
|
|
ValueArgument)
|
|
from argenta.orchestrator.argparser.entity import ArgParser, ArgSpace
|
|
|
|
|
|
class TestArgumentCreation:
|
|
"""Tests for the creation and attribute validation of argument model classes."""
|
|
|
|
def test_value_argument_creation(self):
|
|
"""Ensures ValueArgument instances are created with correct attributes."""
|
|
arg = ValueArgument(
|
|
name="test_arg",
|
|
prefix="--",
|
|
help="A test argument.",
|
|
possible_values=["one", "two"],
|
|
default="one",
|
|
is_required=True,
|
|
is_deprecated=False,
|
|
)
|
|
assert arg.name == "test_arg"
|
|
assert arg.prefix == "--"
|
|
assert arg.help == "A test argument."
|
|
assert arg.possible_values == ["one", "two"]
|
|
assert arg.default == "one"
|
|
assert arg.is_required is True
|
|
assert arg.is_deprecated is False
|
|
assert arg.action == "store"
|
|
assert arg.string_entity == "--test_arg"
|
|
|
|
def test_boolean_argument_creation(self):
|
|
"""Ensures BooleanArgument instances are created with correct attributes."""
|
|
arg = BooleanArgument(
|
|
name="verbose", prefix="-", help="Enable verbose mode.", is_deprecated=True
|
|
)
|
|
assert arg.name == "verbose"
|
|
assert arg.prefix == "-"
|
|
assert arg.help == "Enable verbose mode."
|
|
assert arg.is_deprecated is True
|
|
assert arg.action == "store_true"
|
|
assert arg.string_entity == "-verbose"
|
|
|
|
def test_input_argument_creation(self):
|
|
"""Ensures InputArgument instances are created with correct attributes."""
|
|
arg = InputArgument(
|
|
name="file", value="/path/to/file", founder_class=ValueArgument
|
|
)
|
|
assert arg.name == "file"
|
|
assert arg.value == "/path/to/file"
|
|
assert arg.founder_class is ValueArgument
|
|
|
|
|
|
class TestArgSpace:
|
|
"""Tests for the ArgSpace class, which holds parsed argument values."""
|
|
|
|
@pytest.fixture
|
|
def mock_arguments(self) -> list[InputArgument]:
|
|
"""Provides a list of mock InputArgument objects for testing."""
|
|
return [
|
|
InputArgument(name="arg1", value="val1", founder_class=ValueArgument),
|
|
InputArgument(name="arg2", value=True, founder_class=BooleanArgument),
|
|
InputArgument(name="arg3", value="val3", founder_class=ValueArgument),
|
|
]
|
|
|
|
@pytest.fixture
|
|
def arg_space(self, mock_arguments: list[InputArgument]) -> ArgSpace:
|
|
"""Provides a pre-populated ArgSpace instance."""
|
|
return ArgSpace(all_arguments=mock_arguments)
|
|
|
|
def test_initialization(self, arg_space: ArgSpace, mock_arguments: list[InputArgument]):
|
|
"""Tests if ArgSpace is initialized correctly with a list of arguments."""
|
|
assert len(arg_space.all_arguments) == 3
|
|
assert arg_space.all_arguments == mock_arguments
|
|
|
|
def test_get_by_name(self, arg_space: ArgSpace, mock_arguments: list[InputArgument]):
|
|
"""Tests retrieving an argument by its name."""
|
|
found_arg = arg_space.get_by_name("arg1")
|
|
assert found_arg is not None
|
|
assert found_arg == mock_arguments[0]
|
|
|
|
def test_get_by_name_not_found(self, arg_space: ArgSpace):
|
|
"""Tests that get_by_name returns None for a non-existent argument."""
|
|
found_arg = arg_space.get_by_name("non_existent_arg")
|
|
assert found_arg is None
|
|
|
|
def test_get_by_type(self, arg_space: ArgSpace, mock_arguments: list[InputArgument]):
|
|
"""Tests retrieving arguments based on their founder class type."""
|
|
value_args = arg_space.get_by_type(ValueArgument)
|
|
assert len(value_args) == 2
|
|
assert mock_arguments[0] in value_args
|
|
assert mock_arguments[2] in value_args
|
|
|
|
bool_args = arg_space.get_by_type(BooleanArgument)
|
|
assert len(bool_args) == 1
|
|
assert mock_arguments[1] in bool_args
|
|
|
|
def test_get_by_type_not_found(self, arg_space: ArgSpace):
|
|
"""Tests that get_by_type returns an empty list for an unused argument type."""
|
|
class OtherArgument(BaseArgument):
|
|
pass
|
|
other_args = arg_space.get_by_type(OtherArgument)
|
|
assert other_args == []
|
|
|
|
def test_from_namespace(self):
|
|
"""Tests the class method for creating an ArgSpace from an argparse.Namespace."""
|
|
namespace = Namespace(config="config.json", debug=True, verbose=False)
|
|
processed_args = [
|
|
ValueArgument(name="config", prefix="--"),
|
|
BooleanArgument(name="debug", prefix="-"),
|
|
BooleanArgument(name="verbose", prefix="-"),
|
|
]
|
|
|
|
arg_space = ArgSpace.from_namespace(namespace, processed_args)
|
|
assert len(arg_space.all_arguments) == 3
|
|
|
|
config_arg = arg_space.get_by_name('config')
|
|
debug_arg = arg_space.get_by_name('debug')
|
|
|
|
assert config_arg is not None
|
|
assert config_arg.value == "config.json"
|
|
assert config_arg.founder_class is ValueArgument
|
|
|
|
assert debug_arg is not None
|
|
assert debug_arg.value is True
|
|
assert debug_arg.founder_class is BooleanArgument
|
|
|
|
|
|
class TestArgParser:
|
|
"""Tests for the ArgParser class, which orchestrates argument parsing."""
|
|
|
|
@pytest.fixture
|
|
def value_arg(self) -> ValueArgument:
|
|
"""Provides a sample ValueArgument."""
|
|
return ValueArgument(name="config", help="Path to config file", default="dev.json", is_required=False, possible_values=["dev.json", "prod.json"])
|
|
|
|
@pytest.fixture
|
|
def bool_arg(self) -> BooleanArgument:
|
|
"""Provides a sample BooleanArgument."""
|
|
return BooleanArgument(name="debug", help="Enable debug mode")
|
|
|
|
@pytest.fixture
|
|
def processed_args(self, value_arg: ValueArgument, bool_arg: BooleanArgument) -> list:
|
|
"""Provides a list of processed arguments."""
|
|
return [value_arg, bool_arg]
|
|
|
|
def test_initialization(self, processed_args: list):
|
|
"""Tests that the ArgParser constructor correctly assigns attributes."""
|
|
parser = ArgParser(
|
|
processed_args=processed_args,
|
|
name="TestApp",
|
|
description="A test application.",
|
|
epilog="Test epilog.",
|
|
)
|
|
assert parser.name == "TestApp"
|
|
assert parser.description == "A test application."
|
|
assert parser.epilog == "Test epilog."
|
|
assert parser.processed_args == processed_args
|
|
assert isinstance(parser.parsed_argspace, ArgSpace)
|
|
assert parser.parsed_argspace.all_arguments == []
|
|
|
|
@pytest.mark.skipif(sys.version_info < (3, 13), reason="requires python3.13 or higher")
|
|
def test_register_args(self, mocker, value_arg: ValueArgument, bool_arg: BooleanArgument):
|
|
"""Tests that arguments are correctly registered with the underlying ArgumentParser."""
|
|
mock_add_argument = mocker.patch("argparse.ArgumentParser.add_argument")
|
|
|
|
parser = ArgParser(processed_args=[value_arg, bool_arg])
|
|
|
|
expected_calls = [
|
|
# Call for the ValueArgument
|
|
call(
|
|
value_arg.string_entity,
|
|
action=value_arg.action,
|
|
help=value_arg.help,
|
|
default=value_arg.default,
|
|
choices=value_arg.possible_values,
|
|
required=value_arg.is_required,
|
|
deprecated=value_arg.is_deprecated
|
|
),
|
|
# Call for the BooleanArgument
|
|
call(
|
|
bool_arg.string_entity,
|
|
action=bool_arg.action,
|
|
help=bool_arg.help,
|
|
deprecated=bool_arg.is_deprecated
|
|
)
|
|
]
|
|
mock_add_argument.assert_has_calls(expected_calls, any_order=True)
|
|
|
|
@pytest.mark.skipif(sys.version_info > (3, 12), reason='for more latest python version has been other test')
|
|
def test_register_args(self, mocker, value_arg: ValueArgument, bool_arg: BooleanArgument):
|
|
"""Tests that arguments are correctly registered with the underlying ArgumentParser."""
|
|
mock_add_argument = mocker.patch("argparse.ArgumentParser.add_argument")
|
|
|
|
parser = ArgParser(processed_args=[value_arg, bool_arg])
|
|
|
|
expected_calls = [
|
|
# Call for the ValueArgument
|
|
call(
|
|
value_arg.string_entity,
|
|
action=value_arg.action,
|
|
help=value_arg.help,
|
|
default=value_arg.default,
|
|
choices=value_arg.possible_values,
|
|
required=value_arg.is_required
|
|
),
|
|
# Call for the BooleanArgument
|
|
call(
|
|
bool_arg.string_entity,
|
|
action=bool_arg.action,
|
|
help=bool_arg.help
|
|
)
|
|
]
|
|
mock_add_argument.assert_has_calls(expected_calls, any_order=True)
|
|
|
|
def test_parse_args_populates_argspace(self, mocker, processed_args: list[ValueArgument | BooleanArgument]):
|
|
"""Tests that _parse_args correctly calls the parser and populates the ArgSpace."""
|
|
# 1. Mock the return value of the internal argparse instance
|
|
mock_namespace = Namespace(config='config.json', debug=True)
|
|
mocker.patch(
|
|
'argparse.ArgumentParser.parse_args',
|
|
return_value=mock_namespace
|
|
)
|
|
|
|
# 2. Initialize the parser and call the method under test
|
|
parser = ArgParser(processed_args=processed_args)
|
|
parser._parse_args() # Test the private method that contains the logic
|
|
|
|
# 3. Assert the results
|
|
arg_space = parser.parsed_argspace
|
|
assert isinstance(arg_space, ArgSpace)
|
|
assert len(arg_space.all_arguments) == 2
|
|
|
|
config_arg = arg_space.get_by_name('config')
|
|
debug_arg = arg_space.get_by_name('debug')
|
|
|
|
assert config_arg is not None
|
|
assert config_arg.value == 'config.json'
|
|
assert config_arg.founder_class is ValueArgument
|
|
|
|
assert debug_arg is not None
|
|
assert debug_arg.value is True
|
|
assert debug_arg.founder_class is BooleanArgument
|