mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 12:40:35 +00:00
[Feature] - OpenBB Platform CLI Unit tests (#6397)
* Unit test batch 1 * CLI controller * Test batch 3 * Test batch 4 * Test batch 5 * clean some workflows and setup actions * test * rename wfs * rename * update action * Skip * fix cli tests --------- Co-authored-by: Henrique Joaquim <henriquecjoaquim@gmail.com> Co-authored-by: Diogo Sousa <montezdesousa@gmail.com> Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
This commit is contained in:
94
cli/tests/test_argparse_translator.py
Normal file
94
cli/tests/test_argparse_translator.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""Test the Argparse Translator."""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
import pytest
|
||||
from openbb_cli.argparse_translator.argparse_translator import (
|
||||
ArgparseTranslator,
|
||||
CustomArgument,
|
||||
CustomArgumentGroup,
|
||||
)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
|
||||
def test_custom_argument_action_validation():
|
||||
"""Test that CustomArgument raises an error for invalid actions."""
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
CustomArgument(
|
||||
name="test",
|
||||
type=bool,
|
||||
dest="test",
|
||||
default=False,
|
||||
required=True,
|
||||
action="store",
|
||||
help="Test argument",
|
||||
nargs=None,
|
||||
choices=None,
|
||||
)
|
||||
assert 'action must be "store_true"' in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_argument_remove_props_on_store_true():
|
||||
"""Test that CustomArgument removes type, nargs, and choices on store_true."""
|
||||
argument = CustomArgument(
|
||||
name="verbose",
|
||||
type=None,
|
||||
dest="verbose",
|
||||
default=None,
|
||||
required=False,
|
||||
action="store_true",
|
||||
help="Verbose output",
|
||||
nargs=None,
|
||||
choices=None,
|
||||
)
|
||||
assert argument.type is None
|
||||
assert argument.nargs is None
|
||||
assert argument.choices is None
|
||||
|
||||
|
||||
def test_custom_argument_group():
|
||||
"""Test the CustomArgumentGroup class."""
|
||||
args = [
|
||||
CustomArgument(
|
||||
name="test",
|
||||
type=int,
|
||||
dest="test",
|
||||
default=1,
|
||||
required=True,
|
||||
action="store",
|
||||
help="Test argument",
|
||||
nargs=None,
|
||||
choices=None,
|
||||
)
|
||||
]
|
||||
group = CustomArgumentGroup(name="Test Group", arguments=args)
|
||||
assert group.name == "Test Group"
|
||||
assert len(group.arguments) == 1
|
||||
assert group.arguments[0].name == "test"
|
||||
|
||||
|
||||
def test_argparse_translator_setup():
|
||||
"""Test the ArgparseTranslator setup."""
|
||||
|
||||
def test_function(test_arg: int):
|
||||
"""A test function."""
|
||||
return test_arg * 2
|
||||
|
||||
translator = ArgparseTranslator(func=test_function)
|
||||
parser = translator.parser
|
||||
assert isinstance(parser, ArgumentParser)
|
||||
assert "--test_arg" in parser._option_string_actions
|
||||
|
||||
|
||||
def test_argparse_translator_execution():
|
||||
"""Test the ArgparseTranslator execution."""
|
||||
|
||||
def test_function(test_arg: int) -> int:
|
||||
"""A test function."""
|
||||
return test_arg * 2
|
||||
|
||||
translator = ArgparseTranslator(func=test_function)
|
||||
parsed_args = translator.parser.parse_args(["--test_arg", "3"])
|
||||
result = translator.execute_func(parsed_args)
|
||||
assert result == 6
|
||||
89
cli/tests/test_argparse_translator_obbject_registry.py
Normal file
89
cli/tests/test_argparse_translator_obbject_registry.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""Test OBBject Registry."""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from openbb_cli.argparse_translator.obbject_registry import Registry
|
||||
from openbb_core.app.model.obbject import OBBject
|
||||
|
||||
# pylint: disable=redefined-outer-name, protected-access
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def registry():
|
||||
"""Fixture to create a Registry instance for testing."""
|
||||
return Registry()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_obbject():
|
||||
"""Fixture to create a mock OBBject for testing."""
|
||||
|
||||
class MockModel:
|
||||
"""Mock model for testing."""
|
||||
|
||||
def __init__(self, value):
|
||||
self.mock_value = value
|
||||
self._model_json_schema = "mock_json_schema"
|
||||
|
||||
def model_json_schema(self):
|
||||
return self._model_json_schema
|
||||
|
||||
obb = Mock(spec=OBBject)
|
||||
obb.id = "123"
|
||||
obb.provider = "test_provider"
|
||||
obb.extra = {"command": "test_command"}
|
||||
obb._route = "/test/route"
|
||||
obb._standard_params = Mock()
|
||||
obb._standard_params.__dict__ = {}
|
||||
obb.results = [MockModel(1), MockModel(2)]
|
||||
return obb
|
||||
|
||||
|
||||
def test_listing_all_obbjects(registry, mock_obbject):
|
||||
"""Test listing all obbjects with additional properties."""
|
||||
registry.register(mock_obbject)
|
||||
|
||||
all_obbjects = registry.all
|
||||
assert len(all_obbjects) == 1
|
||||
assert all_obbjects[0]["command"] == "test_command"
|
||||
assert all_obbjects[0]["provider"] == "test_provider"
|
||||
|
||||
|
||||
def test_registry_initialization(registry):
|
||||
"""Test the Registry is initialized correctly."""
|
||||
assert registry.obbjects == []
|
||||
|
||||
|
||||
def test_register_new_obbject(registry, mock_obbject):
|
||||
"""Test registering a new OBBject."""
|
||||
registry.register(mock_obbject)
|
||||
assert mock_obbject in registry.obbjects
|
||||
|
||||
|
||||
def test_register_duplicate_obbject(registry, mock_obbject):
|
||||
"""Test that duplicate OBBjects are not added."""
|
||||
registry.register(mock_obbject)
|
||||
registry.register(mock_obbject)
|
||||
assert len(registry.obbjects) == 1
|
||||
|
||||
|
||||
def test_get_obbject_by_index(registry, mock_obbject):
|
||||
"""Test retrieving an obbject by its index."""
|
||||
registry.register(mock_obbject)
|
||||
retrieved = registry.get(0)
|
||||
assert retrieved == mock_obbject
|
||||
|
||||
|
||||
def test_remove_obbject_by_index(registry, mock_obbject):
|
||||
"""Test removing an obbject by index."""
|
||||
registry.register(mock_obbject)
|
||||
registry.remove(0)
|
||||
assert mock_obbject not in registry.obbjects
|
||||
|
||||
|
||||
def test_remove_last_obbject_by_default(registry, mock_obbject):
|
||||
"""Test removing the last obbject by default."""
|
||||
registry.register(mock_obbject)
|
||||
registry.remove()
|
||||
assert not registry.obbjects
|
||||
45
cli/tests/test_cli.py
Normal file
45
cli/tests/test_cli.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""Test the CLI module."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from openbb_cli.cli import main
|
||||
|
||||
|
||||
@patch("openbb_cli.cli.bootstrap")
|
||||
@patch("openbb_cli.cli.launch")
|
||||
@patch("sys.argv", ["openbb", "--dev", "--debug"])
|
||||
def test_main_with_dev_and_debug(mock_launch, mock_bootstrap):
|
||||
"""Test the main function with dev and debug flags."""
|
||||
main()
|
||||
mock_bootstrap.assert_called_once()
|
||||
mock_launch.assert_called_once_with(True, True)
|
||||
|
||||
|
||||
@patch("openbb_cli.cli.bootstrap")
|
||||
@patch("openbb_cli.cli.launch")
|
||||
@patch("sys.argv", ["openbb"])
|
||||
def test_main_without_arguments(mock_launch, mock_bootstrap):
|
||||
"""Test the main function without arguments."""
|
||||
main()
|
||||
mock_bootstrap.assert_called_once()
|
||||
mock_launch.assert_called_once_with(False, False)
|
||||
|
||||
|
||||
@patch("openbb_cli.cli.bootstrap")
|
||||
@patch("openbb_cli.cli.launch")
|
||||
@patch("sys.argv", ["openbb", "--dev"])
|
||||
def test_main_with_dev_only(mock_launch, mock_bootstrap):
|
||||
"""Test the main function with dev flag only."""
|
||||
main()
|
||||
mock_bootstrap.assert_called_once()
|
||||
mock_launch.assert_called_once_with(True, False)
|
||||
|
||||
|
||||
@patch("openbb_cli.cli.bootstrap")
|
||||
@patch("openbb_cli.cli.launch")
|
||||
@patch("sys.argv", ["openbb", "--debug"])
|
||||
def test_main_with_debug_only(mock_launch, mock_bootstrap):
|
||||
"""Test the main function with debug flag only."""
|
||||
main()
|
||||
mock_bootstrap.assert_called_once()
|
||||
mock_launch.assert_called_once_with(False, True)
|
||||
78
cli/tests/test_config_completer.py
Normal file
78
cli/tests/test_config_completer.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Test the Config completer."""
|
||||
|
||||
import pytest
|
||||
from openbb_cli.config.completer import WordCompleter
|
||||
from prompt_toolkit.completion import CompleteEvent
|
||||
from prompt_toolkit.document import Document
|
||||
|
||||
# pylint: disable=redefined-outer-name, import-outside-toplevel
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def word_completer():
|
||||
"""Return a simple word completer."""
|
||||
words = ["test", "example", "demo"]
|
||||
return WordCompleter(words, ignore_case=True)
|
||||
|
||||
|
||||
def test_word_completer_simple(word_completer):
|
||||
"""Test the word completer with a simple word list."""
|
||||
doc = Document(text="ex", cursor_position=2)
|
||||
completions = list(word_completer.get_completions(doc, CompleteEvent()))
|
||||
assert len(completions) == 1
|
||||
assert completions[0].text == "example"
|
||||
|
||||
|
||||
def test_word_completer_case_insensitive(word_completer):
|
||||
"""Test the word completer with case-insensitive matching."""
|
||||
doc = Document(text="Ex", cursor_position=2)
|
||||
completions = list(word_completer.get_completions(doc, CompleteEvent()))
|
||||
assert len(completions) == 1
|
||||
assert completions[0].text == "example"
|
||||
|
||||
|
||||
def test_word_completer_no_match(word_completer):
|
||||
"""Test the word completer with no matches."""
|
||||
doc = Document(text="xyz", cursor_position=3)
|
||||
completions = list(word_completer.get_completions(doc, CompleteEvent()))
|
||||
assert len(completions) == 0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nested_completer():
|
||||
"""Return a nested completer."""
|
||||
from openbb_cli.config.completer import NestedCompleter
|
||||
|
||||
data = {
|
||||
"show": {
|
||||
"version": None,
|
||||
"interfaces": None,
|
||||
"clock": None,
|
||||
"ip": {"interface": {"brief": None}},
|
||||
},
|
||||
"exit": None,
|
||||
"enable": None,
|
||||
}
|
||||
return NestedCompleter.from_nested_dict(data)
|
||||
|
||||
|
||||
def test_nested_completer_root_command(nested_completer):
|
||||
"""Test the nested completer with a root command."""
|
||||
doc = Document(text="sh", cursor_position=2)
|
||||
completions = list(nested_completer.get_completions(doc, CompleteEvent()))
|
||||
assert "show" in [c.text for c in completions]
|
||||
|
||||
|
||||
def test_nested_completer_sub_command(nested_completer):
|
||||
"""Test the nested completer with a sub-command."""
|
||||
doc = Document(text="show ", cursor_position=5)
|
||||
completions = list(nested_completer.get_completions(doc, CompleteEvent()))
|
||||
assert "version" in [c.text for c in completions]
|
||||
assert "interfaces" in [c.text for c in completions]
|
||||
|
||||
|
||||
def test_nested_completer_no_match(nested_completer):
|
||||
"""Test the nested completer with no matches."""
|
||||
doc = Document(text="random ", cursor_position=7)
|
||||
completions = list(nested_completer.get_completions(doc, CompleteEvent()))
|
||||
assert len(completions) == 0
|
||||
47
cli/tests/test_config_console.py
Normal file
47
cli/tests/test_config_console.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Test Config Console."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.config.console import Console
|
||||
from rich.text import Text
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument, unused-variable, protected-access
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_settings():
|
||||
"""Mock settings to inject into Console."""
|
||||
|
||||
class MockSettings:
|
||||
TEST_MODE = False
|
||||
ENABLE_RICH_PANEL = True
|
||||
SHOW_VERSION = True
|
||||
VERSION = "1.0"
|
||||
|
||||
return MockSettings()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def console(mock_settings):
|
||||
"""Create a Console instance with mocked settings."""
|
||||
with patch("rich.console.Console") as MockRichConsole: # noqa: F841
|
||||
return Console(settings=mock_settings)
|
||||
|
||||
|
||||
def test_print_without_panel(console, mock_settings):
|
||||
"""Test printing without a rich panel when disabled."""
|
||||
mock_settings.ENABLE_RICH_PANEL = False
|
||||
with patch.object(console._console, "print") as mock_print:
|
||||
console.print(text="Hello, world!", menu="Home Menu")
|
||||
mock_print.assert_called_once_with("Hello, world!")
|
||||
|
||||
|
||||
def test_blend_text():
|
||||
"""Test blending text colors."""
|
||||
message = "Hello"
|
||||
color1 = (255, 0, 0) # Red
|
||||
color2 = (0, 0, 255) # Blue
|
||||
blended_text = Console._blend_text(message, color1, color2)
|
||||
assert isinstance(blended_text, Text)
|
||||
assert "Hello" in blended_text.plain
|
||||
68
cli/tests/test_config_menu_text.py
Normal file
68
cli/tests/test_config_menu_text.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Test Config Menu Text."""
|
||||
|
||||
import pytest
|
||||
from openbb_cli.config.menu_text import MenuText
|
||||
|
||||
# pylint: disable=redefined-outer-name, protected-access
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def menu_text():
|
||||
"""Fixture to create a MenuText instance for testing."""
|
||||
return MenuText(path="/test/path")
|
||||
|
||||
|
||||
def test_initialization(menu_text):
|
||||
"""Test initialization of the MenuText class."""
|
||||
assert menu_text.menu_text == ""
|
||||
assert menu_text.menu_path == "/test/path"
|
||||
assert menu_text.warnings == []
|
||||
|
||||
|
||||
def test_add_raw(menu_text):
|
||||
"""Test adding raw text."""
|
||||
menu_text.add_raw("Example raw text")
|
||||
assert "Example raw text" in menu_text.menu_text
|
||||
|
||||
|
||||
def test_add_info(menu_text):
|
||||
"""Test adding informational text."""
|
||||
menu_text.add_info("Info text")
|
||||
assert "[info]Info text:[/info]" in menu_text.menu_text
|
||||
|
||||
|
||||
def test_add_cmd(menu_text):
|
||||
"""Test adding a command."""
|
||||
menu_text.add_cmd("command", "Performs an action")
|
||||
assert "command" in menu_text.menu_text
|
||||
assert "Performs an action" in menu_text.menu_text
|
||||
|
||||
|
||||
def test_format_cmd_name(menu_text):
|
||||
"""Test formatting of command names that are too long."""
|
||||
long_name = "x" * 50 # Assuming CMD_NAME_LENGTH is 23
|
||||
formatted_name = menu_text._format_cmd_name(long_name)
|
||||
assert len(formatted_name) <= menu_text.CMD_NAME_LENGTH
|
||||
assert menu_text.warnings # Check that a warning was added
|
||||
|
||||
|
||||
def test_format_cmd_description(menu_text):
|
||||
"""Test truncation of long descriptions."""
|
||||
long_description = "y" * 100 # Assuming CMD_DESCRIPTION_LENGTH is 65
|
||||
formatted_description = menu_text._format_cmd_description("cmd", long_description)
|
||||
assert len(formatted_description) <= menu_text.CMD_DESCRIPTION_LENGTH
|
||||
|
||||
|
||||
def test_add_menu(menu_text):
|
||||
"""Test adding a menu item."""
|
||||
menu_text.add_menu("Settings", "Configure your settings")
|
||||
assert "Settings" in menu_text.menu_text
|
||||
assert "Configure your settings" in menu_text.menu_text
|
||||
|
||||
|
||||
def test_add_setting(menu_text):
|
||||
"""Test adding a setting."""
|
||||
menu_text.add_setting("Enable Feature", True, "Feature description")
|
||||
assert "Enable Feature" in menu_text.menu_text
|
||||
assert "Feature description" in menu_text.menu_text
|
||||
assert "[green]" in menu_text.menu_text
|
||||
49
cli/tests/test_config_setup.py
Normal file
49
cli/tests/test_config_setup.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Test the Config Setup."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.config.setup import bootstrap
|
||||
|
||||
# pylint: disable=unused-variable
|
||||
|
||||
|
||||
def test_bootstrap_creates_directory_and_file():
|
||||
"""Test that bootstrap creates the settings directory and environment file."""
|
||||
with patch("pathlib.Path.mkdir") as mock_mkdir, patch(
|
||||
"pathlib.Path.touch"
|
||||
) as mock_touch:
|
||||
bootstrap()
|
||||
mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
|
||||
mock_touch.assert_called_once_with(exist_ok=True)
|
||||
|
||||
|
||||
def test_bootstrap_directory_exists():
|
||||
"""Test bootstrap when the directory already exists."""
|
||||
with patch("pathlib.Path.mkdir") as mock_mkdir, patch(
|
||||
"pathlib.Path.touch"
|
||||
) as mock_touch:
|
||||
bootstrap()
|
||||
mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
|
||||
mock_touch.assert_called_once_with(exist_ok=True)
|
||||
|
||||
|
||||
def test_bootstrap_file_exists():
|
||||
"""Test bootstrap when the environment file already exists."""
|
||||
with patch("pathlib.Path.mkdir") as mock_mkdir, patch(
|
||||
"pathlib.Path.touch"
|
||||
) as mock_touch:
|
||||
bootstrap()
|
||||
mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
|
||||
mock_touch.assert_called_once_with(exist_ok=True)
|
||||
|
||||
|
||||
def test_bootstrap_permission_error():
|
||||
"""Test bootstrap handles permission errors gracefully."""
|
||||
with patch("pathlib.Path.mkdir") as mock_mkdir, patch(
|
||||
"pathlib.Path.touch"
|
||||
) as mock_touch, pytest.raises( # noqa: F841
|
||||
PermissionError
|
||||
):
|
||||
mock_mkdir.side_effect = PermissionError("No permission to create directory")
|
||||
bootstrap() # Expecting to raise a PermissionError and be caught by pytest.raises
|
||||
60
cli/tests/test_config_style.py
Normal file
60
cli/tests/test_config_style.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Test Config Style."""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.config.style import Style
|
||||
|
||||
# pylint: disable=redefined-outer-name, protected-access
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_style_directory(tmp_path):
|
||||
"""Fixture to create a mock styles directory."""
|
||||
(tmp_path / "styles" / "default").mkdir(parents=True, exist_ok=True)
|
||||
return tmp_path / "styles"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def style(mock_style_directory):
|
||||
"""Fixture to create a Style instance for testing."""
|
||||
return Style(directory=mock_style_directory)
|
||||
|
||||
|
||||
def test_initialization(style):
|
||||
"""Test that Style class initializes with default properties."""
|
||||
assert style.line_width == 1.5
|
||||
assert isinstance(style.console_style, dict)
|
||||
|
||||
|
||||
@patch("pathlib.Path.exists", MagicMock(return_value=True))
|
||||
@patch("pathlib.Path.rglob")
|
||||
def test_load_styles(mock_rglob, style, mock_style_directory):
|
||||
"""Test loading styles from directories."""
|
||||
mock_rglob.return_value = [mock_style_directory / "default" / "dark.richstyle.json"]
|
||||
style._load(mock_style_directory)
|
||||
assert "dark" in style.console_styles_available
|
||||
|
||||
|
||||
@patch("builtins.open", new_callable=MagicMock)
|
||||
@patch("json.load", MagicMock(return_value={"background": "black"}))
|
||||
def test_from_json(mock_open, style, mock_style_directory):
|
||||
"""Test loading style from a JSON file."""
|
||||
json_file = mock_style_directory / "dark.richstyle.json"
|
||||
result = style._from_json(json_file)
|
||||
assert result == {"background": "black"}
|
||||
mock_open.assert_called_once_with(json_file)
|
||||
|
||||
|
||||
def test_apply_invalid_style(style, mock_style_directory, capsys):
|
||||
"""Test applying an invalid style and falling back to default."""
|
||||
style.apply("nonexistent", mock_style_directory)
|
||||
captured = capsys.readouterr()
|
||||
assert "Invalid console style" in captured.out
|
||||
|
||||
|
||||
def test_available_styles(style):
|
||||
"""Test listing available styles."""
|
||||
style.console_styles_available = {"dark": Path("/path/to/dark.richstyle.json")}
|
||||
assert "dark" in style.available_styles
|
||||
79
cli/tests/test_controllers_base_controller.py
Normal file
79
cli/tests/test_controllers_base_controller.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Test the base controller."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.base_controller import BaseController
|
||||
|
||||
# pylint: disable=unused-argument, unused-variable
|
||||
|
||||
|
||||
class TestableBaseController(BaseController):
|
||||
"""Testable Base Controller."""
|
||||
|
||||
def __init__(self, queue=None):
|
||||
"""Initialize the TestableBaseController."""
|
||||
self.PATH = "/valid/path/"
|
||||
super().__init__(queue=queue)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help."""
|
||||
|
||||
|
||||
def test_base_controller_initialization():
|
||||
"""Test the initialization of the base controller."""
|
||||
with patch.object(TestableBaseController, "check_path", return_value=None):
|
||||
controller = TestableBaseController()
|
||||
assert controller.path == ["valid", "path"] # Checking for correct path split
|
||||
|
||||
|
||||
def test_path_validation():
|
||||
"""Test the path validation method."""
|
||||
controller = TestableBaseController()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
controller.PATH = "invalid/path"
|
||||
controller.check_path()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
controller.PATH = "/invalid/path"
|
||||
controller.check_path()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
controller.PATH = "/Invalid/Path/"
|
||||
controller.check_path()
|
||||
|
||||
controller.PATH = "/valid/path/"
|
||||
|
||||
|
||||
def test_parse_input():
|
||||
"""Test the parse input method."""
|
||||
controller = TestableBaseController()
|
||||
input_str = "cmd1/cmd2/cmd3"
|
||||
expected = ["cmd1", "cmd2", "cmd3"]
|
||||
result = controller.parse_input(input_str)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_switch():
|
||||
"""Test the switch method."""
|
||||
controller = TestableBaseController()
|
||||
with patch.object(controller, "call_exit", MagicMock()) as mock_exit:
|
||||
controller.queue = ["exit"]
|
||||
controller.switch("exit")
|
||||
mock_exit.assert_called_once()
|
||||
|
||||
|
||||
def test_call_help():
|
||||
"""Test the call help method."""
|
||||
controller = TestableBaseController()
|
||||
with patch("openbb_cli.controllers.base_controller.session.console.print"):
|
||||
controller.call_help(None)
|
||||
|
||||
|
||||
def test_call_exit():
|
||||
"""Test the call exit method."""
|
||||
controller = TestableBaseController()
|
||||
with patch.object(controller, "save_class", MagicMock()):
|
||||
controller.queue = ["quit"]
|
||||
controller.call_exit(None)
|
||||
70
cli/tests/test_controllers_base_platform_controller.py
Normal file
70
cli/tests/test_controllers_base_platform_controller.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Test the BasePlatformController."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.base_platform_controller import PlatformController, Session
|
||||
|
||||
# pylint: disable=redefined-outer-name, protected-access, unused-argument, unused-variable
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_session():
|
||||
"""Mock session fixture."""
|
||||
with patch(
|
||||
"openbb_cli.controllers.base_platform_controller.session",
|
||||
MagicMock(spec=Session),
|
||||
) as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
def test_initialization_with_valid_params(mock_session):
|
||||
"""Test the initialization of the BasePlatformController."""
|
||||
translators = {"dummy_translator": MagicMock()}
|
||||
controller = PlatformController(
|
||||
name="test", parent_path=["parent"], translators=translators
|
||||
)
|
||||
assert controller._name == "test"
|
||||
assert controller.translators == translators
|
||||
|
||||
|
||||
def test_initialization_without_required_params():
|
||||
"""Test the initialization of the BasePlatformController without required params."""
|
||||
with pytest.raises(ValueError):
|
||||
PlatformController(name="test", parent_path=["parent"])
|
||||
|
||||
|
||||
def test_command_generation(mock_session):
|
||||
"""Test the command generation method."""
|
||||
translator = MagicMock()
|
||||
translators = {"test_command": translator}
|
||||
controller = PlatformController(
|
||||
name="test", parent_path=["parent"], translators=translators
|
||||
)
|
||||
|
||||
# Check if command function is correctly linked
|
||||
assert "test_command" in controller.translators
|
||||
|
||||
|
||||
def test_print_help(mock_session):
|
||||
"""Test the print help method."""
|
||||
translators = {"test_command": MagicMock()}
|
||||
controller = PlatformController(
|
||||
name="test", parent_path=["parent"], translators=translators
|
||||
)
|
||||
|
||||
with patch(
|
||||
"openbb_cli.controllers.base_platform_controller.MenuText"
|
||||
) as mock_menu_text:
|
||||
controller.print_help()
|
||||
mock_menu_text.assert_called_once_with("/parent/test/")
|
||||
|
||||
|
||||
def test_sub_controller_generation(mock_session):
|
||||
"""Test the sub controller generation method."""
|
||||
translators = {"test_menu_item": MagicMock()}
|
||||
controller = PlatformController(
|
||||
name="test", parent_path=["parent"], translators=translators
|
||||
)
|
||||
|
||||
assert "test_menu_item" in controller.translators
|
||||
51
cli/tests/test_controllers_choices.py
Normal file
51
cli/tests/test_controllers_choices.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Test the choices controller."""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.choices import (
|
||||
build_controller_choice_map,
|
||||
)
|
||||
|
||||
# pylint: disable=redefined-outer-name, protected-access, unused-argument, unused-variable
|
||||
|
||||
|
||||
class MockController:
|
||||
"""Mock controller class for testing."""
|
||||
|
||||
CHOICES_COMMANDS = ["test_command"]
|
||||
controller_choices = ["test_command", "help"]
|
||||
|
||||
def call_test_command(self, args):
|
||||
"""Mock function for test_command."""
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--example", choices=["option1", "option2"], help="Example argument."
|
||||
)
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_controller():
|
||||
"""Mock controller fixture."""
|
||||
return MockController()
|
||||
|
||||
|
||||
def test_build_command_choice_map(mock_controller):
|
||||
"""Test the building of a command choice map."""
|
||||
with patch(
|
||||
"openbb_cli.controllers.choices._get_argument_parser"
|
||||
) as mock_get_parser:
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--option", choices=["opt1", "opt2"], help="A choice option."
|
||||
)
|
||||
mock_get_parser.return_value = parser
|
||||
|
||||
choice_map = build_controller_choice_map(controller=mock_controller)
|
||||
|
||||
assert "test_command" in choice_map
|
||||
assert "--option" in choice_map["test_command"]
|
||||
assert "opt1" in choice_map["test_command"]["--option"]
|
||||
assert "opt2" in choice_map["test_command"]["--option"]
|
||||
79
cli/tests/test_controllers_cli_controller.py
Normal file
79
cli/tests/test_controllers_cli_controller.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Test the CLI controller."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.cli_controller import (
|
||||
CLIController,
|
||||
handle_job_cmds,
|
||||
parse_and_split_input,
|
||||
run_cli,
|
||||
)
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument
|
||||
|
||||
|
||||
def test_parse_and_split_input_custom_filters():
|
||||
"""Test the parse_and_split_input function with custom filters."""
|
||||
input_cmd = "query -q AAPL/P"
|
||||
result = parse_and_split_input(
|
||||
input_cmd, custom_filters=[r"((\ -q |\ --question|\ ).*?(/))"]
|
||||
)
|
||||
assert (
|
||||
"AAPL/P" not in result
|
||||
), "Should filter out terms that look like a sorting parameter"
|
||||
|
||||
|
||||
@patch("openbb_cli.controllers.cli_controller.CLIController.print_help")
|
||||
def test_cli_controller_print_help(mock_print_help):
|
||||
"""Test the CLIController print_help method."""
|
||||
controller = CLIController()
|
||||
controller.print_help()
|
||||
mock_print_help.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"controller_input, expected_output",
|
||||
[
|
||||
("settings", True),
|
||||
("random_command", False),
|
||||
],
|
||||
)
|
||||
def test_CLIController_has_command(controller_input, expected_output):
|
||||
"""Test the CLIController has_command method."""
|
||||
controller = CLIController()
|
||||
assert hasattr(controller, f"call_{controller_input}") == expected_output
|
||||
|
||||
|
||||
def test_handle_job_cmds_with_export_path():
|
||||
"""Test the handle_job_cmds function with an export path."""
|
||||
jobs_cmds = ["export /path/to/export some_command"]
|
||||
result = handle_job_cmds(jobs_cmds)
|
||||
expected = "some_command"
|
||||
assert expected in result[0] # type: ignore
|
||||
|
||||
|
||||
@patch("openbb_cli.controllers.cli_controller.CLIController.switch", return_value=[])
|
||||
@patch("openbb_cli.controllers.cli_controller.print_goodbye")
|
||||
def test_run_cli_quit_command(mock_print_goodbye, mock_switch):
|
||||
"""Test the run_cli function with the quit command."""
|
||||
run_cli(["quit"], test_mode=True)
|
||||
mock_print_goodbye.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.skip("This test is not working as expected")
|
||||
def test_execute_openbb_routine_with_mocked_requests():
|
||||
"""Test the call_exe function with mocked requests."""
|
||||
with patch("requests.get") as mock_get:
|
||||
response = MagicMock()
|
||||
response.status_code = 200
|
||||
response.json.return_value = {"script": "print('Hello World')"}
|
||||
mock_get.return_value = response
|
||||
# Here we need to call the correct function, assuming it's something like `call_exe` for URL-based scripts
|
||||
controller = CLIController()
|
||||
controller.call_exe(
|
||||
["--url", "https://my.openbb.co/u/test/routine/test.openbb"]
|
||||
)
|
||||
mock_get.assert_called_with(
|
||||
"https://my.openbb.co/u/test/routine/test.openbb?raw=true", timeout=10
|
||||
)
|
||||
61
cli/tests/test_controllers_controller_factory.py
Normal file
61
cli/tests/test_controllers_controller_factory.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""Test the Controller Factory."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.platform_controller_factory import (
|
||||
PlatformControllerFactory,
|
||||
)
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_processor():
|
||||
"""Fixture to mock ArgparseClassProcessor."""
|
||||
with patch(
|
||||
"openbb_cli.controllers.platform_controller_factory.ArgparseClassProcessor"
|
||||
) as mock:
|
||||
instance = mock.return_value
|
||||
instance.paths = {"settings": "menu"}
|
||||
instance.translators = {"test_router_settings": MagicMock()}
|
||||
yield instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def platform_router():
|
||||
"""Fixture to provide a mock platform_router class."""
|
||||
|
||||
class MockRouter:
|
||||
pass
|
||||
|
||||
return MockRouter
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def factory(platform_router, mock_processor):
|
||||
"""Fixture to create a PlatformControllerFactory with mocked dependencies."""
|
||||
return PlatformControllerFactory(
|
||||
platform_router=platform_router, reference={"test": "ref"}
|
||||
)
|
||||
|
||||
|
||||
def test_init(mock_processor):
|
||||
"""Test the initialization of the PlatformControllerFactory."""
|
||||
factory = PlatformControllerFactory(
|
||||
platform_router=MagicMock(), reference={"test": "ref"}
|
||||
)
|
||||
assert factory.router_name.lower() == "magicmock"
|
||||
assert factory.controller_name == "MagicmockController"
|
||||
|
||||
|
||||
def test_create_controller(factory):
|
||||
"""Test the creation of a controller class."""
|
||||
ControllerClass = factory.create()
|
||||
|
||||
assert "PlatformController" in [base.__name__ for base in ControllerClass.__bases__]
|
||||
assert ControllerClass.CHOICES_GENERATION
|
||||
assert "settings" in ControllerClass.CHOICES_MENUS
|
||||
assert "test_router_settings" not in [
|
||||
cmd.replace("test_router_", "") for cmd in ControllerClass.CHOICES_COMMANDS
|
||||
]
|
||||
106
cli/tests/test_controllers_script_parser.py
Normal file
106
cli/tests/test_controllers_script_parser.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Test Script parser."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.script_parser import (
|
||||
match_and_return_openbb_keyword_date,
|
||||
parse_openbb_script,
|
||||
)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-variable, line-too-long
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command, expected",
|
||||
[
|
||||
("reset", True),
|
||||
("r", True),
|
||||
("r\n", True),
|
||||
("restart", False),
|
||||
],
|
||||
)
|
||||
def test_is_reset(command, expected):
|
||||
"""Test the is_reset function."""
|
||||
from openbb_cli.controllers.script_parser import is_reset
|
||||
|
||||
assert is_reset(command) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"keyword, expected_date",
|
||||
[
|
||||
(
|
||||
"$LASTFRIDAY",
|
||||
(
|
||||
datetime.now()
|
||||
- timedelta(days=((datetime.now().weekday() - 4) % 7 + 7) % 7)
|
||||
).strftime("%Y-%m-%d"),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_match_and_return_openbb_keyword_date(keyword, expected_date):
|
||||
"""Test the match_and_return_openbb_keyword_date function."""
|
||||
result = match_and_return_openbb_keyword_date(keyword)
|
||||
assert result == expected_date
|
||||
|
||||
|
||||
def test_parse_openbb_script_basic():
|
||||
"""Test the parse_openbb_script function."""
|
||||
raw_lines = ["echo 'Hello World'"]
|
||||
error, script = parse_openbb_script(raw_lines)
|
||||
assert error == ""
|
||||
assert script == "/echo 'Hello World'"
|
||||
|
||||
|
||||
def test_parse_openbb_script_with_variable():
|
||||
"""Test the parse_openbb_script function."""
|
||||
raw_lines = ["$VAR = 2022-01-01", "echo $VAR"]
|
||||
error, script = parse_openbb_script(raw_lines)
|
||||
assert error == ""
|
||||
assert script == "/echo 2022-01-01"
|
||||
|
||||
|
||||
def test_parse_openbb_script_with_foreach_loop():
|
||||
"""Test the parse_openbb_script function."""
|
||||
raw_lines = ["foreach $$DATE in 2022-01-01,2022-01-02", "echo $$DATE", "end"]
|
||||
error, script = parse_openbb_script(raw_lines)
|
||||
assert error == ""
|
||||
assert script == "/echo 2022-01-01/echo 2022-01-02"
|
||||
|
||||
|
||||
def test_parse_openbb_script_with_error():
|
||||
"""Test the parse_openbb_script function."""
|
||||
raw_lines = ["$VAR = ", "echo $VAR"]
|
||||
error, script = parse_openbb_script(raw_lines)
|
||||
assert "Variable $VAR not given" in error
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"line, expected",
|
||||
[
|
||||
(
|
||||
"foreach $$VAR in 2022-01-01",
|
||||
"[red]The script has a foreach loop that doesn't terminate. Add the keyword 'end' to explicitly terminate loop[/red]", # noqa: E501
|
||||
),
|
||||
("echo Hello World", ""),
|
||||
(
|
||||
"end",
|
||||
"[red]The script has a foreach loop that terminates before it gets started. Add the keyword 'foreach' to explicitly start loop[/red]", # noqa: E501
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_parse_openbb_script_foreach_errors(line, expected):
|
||||
"""Test the parse_openbb_script function."""
|
||||
error, script = parse_openbb_script([line])
|
||||
assert error == expected
|
||||
|
||||
|
||||
def test_date_keyword_last_friday():
|
||||
"""Test the match_and_return_openbb_keyword_date function."""
|
||||
today = datetime.now()
|
||||
last_friday = today - timedelta(days=(today.weekday() - 4 + 7) % 7)
|
||||
if last_friday > today:
|
||||
last_friday -= timedelta(days=7)
|
||||
expected_date = last_friday.strftime("%Y-%m-%d")
|
||||
assert match_and_return_openbb_keyword_date("$LASTFRIDAY") == expected_date
|
||||
135
cli/tests/test_controllers_settings_controller.py
Normal file
135
cli/tests/test_controllers_settings_controller.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""Test the SettingsController class."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.settings_controller import SettingsController
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_session():
|
||||
with patch("openbb_cli.controllers.settings_controller.session") as mock:
|
||||
|
||||
mock.settings.USE_INTERACTIVE_DF = False
|
||||
mock.settings.ALLOWED_NUMBER_OF_ROWS = 20
|
||||
mock.settings.TIMEZONE = "UTC"
|
||||
|
||||
mock.settings.set_item = MagicMock()
|
||||
|
||||
yield mock
|
||||
|
||||
|
||||
def test_call_interactive(mock_session):
|
||||
controller = SettingsController()
|
||||
controller.call_interactive(None)
|
||||
mock_session.settings.set_item.assert_called_once_with("USE_INTERACTIVE_DF", True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_rows, expected",
|
||||
[
|
||||
(10, 10),
|
||||
(15, 15),
|
||||
],
|
||||
)
|
||||
def test_call_n_rows(input_rows, expected, mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--rows", str(input_rows)]
|
||||
controller.call_n_rows(args)
|
||||
mock_session.settings.set_item.assert_called_with(
|
||||
"ALLOWED_NUMBER_OF_ROWS", expected
|
||||
)
|
||||
|
||||
|
||||
def test_call_n_rows_no_args_provided(mock_session):
|
||||
controller = SettingsController()
|
||||
controller.call_n_rows([])
|
||||
mock_session.console.print.assert_called_with("Current number of rows: 20")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"timezone, valid",
|
||||
[
|
||||
("UTC", True),
|
||||
("Mars/Phobos", False),
|
||||
],
|
||||
)
|
||||
def test_call_timezone(timezone, valid, mock_session):
|
||||
with patch(
|
||||
"openbb_cli.controllers.settings_controller.is_timezone_valid",
|
||||
return_value=valid,
|
||||
):
|
||||
controller = SettingsController()
|
||||
args = ["--timezone", timezone]
|
||||
controller.call_timezone(args)
|
||||
if valid:
|
||||
mock_session.settings.set_item.assert_called_with("TIMEZONE", timezone)
|
||||
else:
|
||||
mock_session.settings.set_item.assert_not_called()
|
||||
|
||||
|
||||
def test_call_console_style(mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--style", "dark"]
|
||||
controller.call_console_style(args)
|
||||
mock_session.console.print.assert_called()
|
||||
|
||||
|
||||
def test_call_console_style_no_args(mock_session):
|
||||
mock_session.settings.RICH_STYLE = "default"
|
||||
controller = SettingsController()
|
||||
controller.call_console_style([])
|
||||
mock_session.console.print.assert_called_with("Current console style: default")
|
||||
|
||||
|
||||
def test_call_flair(mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--flair", "rocket"]
|
||||
controller.call_flair(args)
|
||||
|
||||
|
||||
def test_call_flair_no_args(mock_session):
|
||||
mock_session.settings.FLAIR = "star"
|
||||
controller = SettingsController()
|
||||
controller.call_flair([])
|
||||
mock_session.console.print.assert_called_with("Current flair: star")
|
||||
|
||||
|
||||
def test_call_obbject_display(mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--number", "5"]
|
||||
controller.call_obbject_display(args)
|
||||
mock_session.settings.set_item.assert_called_once_with(
|
||||
"N_TO_DISPLAY_OBBJECT_REGISTRY", 5
|
||||
)
|
||||
|
||||
|
||||
def test_call_obbject_display_no_args(mock_session):
|
||||
mock_session.settings.N_TO_DISPLAY_OBBJECT_REGISTRY = 10
|
||||
controller = SettingsController()
|
||||
controller.call_obbject_display([])
|
||||
mock_session.console.print.assert_called_with(
|
||||
"Current number of results to display from the OBBject registry: 10"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"args, expected",
|
||||
[
|
||||
(["--rows", "50"], 50),
|
||||
(["--rows", "100"], 100),
|
||||
([], 20),
|
||||
],
|
||||
)
|
||||
def test_call_n_rows_v2(args, expected, mock_session):
|
||||
mock_session.settings.ALLOWED_NUMBER_OF_ROWS = 20
|
||||
controller = SettingsController()
|
||||
controller.call_n_rows(args)
|
||||
if args:
|
||||
mock_session.settings.set_item.assert_called_with(
|
||||
"ALLOWED_NUMBER_OF_ROWS", expected
|
||||
)
|
||||
else:
|
||||
mock_session.console.print.assert_called_with("Current number of rows: 20")
|
||||
157
cli/tests/test_controllers_utils.py
Normal file
157
cli/tests/test_controllers_utils.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""Test the Controller utils."""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.controllers.utils import (
|
||||
check_non_negative,
|
||||
check_positive,
|
||||
get_flair_and_username,
|
||||
get_user_agent,
|
||||
parse_and_split_input,
|
||||
print_goodbye,
|
||||
print_guest_block_msg,
|
||||
remove_file,
|
||||
reset,
|
||||
welcome_message,
|
||||
)
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_session():
|
||||
"""Mock the session and its dependencies."""
|
||||
with patch("openbb_cli.controllers.utils.Session", autospec=True) as mock:
|
||||
mock.return_value.console.print = MagicMock()
|
||||
mock.return_value.is_local = MagicMock(return_value=True)
|
||||
mock.return_value.settings.VERSION = "1.0"
|
||||
mock.return_value.user.profile.hub_session.username = "testuser"
|
||||
mock.return_value.settings.FLAIR = "rocket"
|
||||
yield mock
|
||||
|
||||
|
||||
def test_remove_file_existing_file():
|
||||
"""Test removing an existing file."""
|
||||
with patch("os.path.isfile", return_value=True), patch("os.remove") as mock_remove:
|
||||
assert remove_file(Path("/path/to/file"))
|
||||
mock_remove.assert_called_once()
|
||||
|
||||
|
||||
def test_remove_file_directory():
|
||||
"""Test removing a directory."""
|
||||
with patch("os.path.isfile", return_value=False), patch(
|
||||
"os.path.isdir", return_value=True
|
||||
), patch("shutil.rmtree") as mock_rmtree:
|
||||
assert remove_file(Path("/path/to/directory"))
|
||||
mock_rmtree.assert_called_once()
|
||||
|
||||
|
||||
def test_remove_file_failure(mock_session):
|
||||
"""Test removing a file that fails."""
|
||||
with patch("os.path.isfile", return_value=True), patch(
|
||||
"os.remove", side_effect=Exception("Error")
|
||||
):
|
||||
assert not remove_file(Path("/path/to/file"))
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
|
||||
|
||||
def test_print_goodbye(mock_session):
|
||||
"""Test printing the goodbye message."""
|
||||
print_goodbye()
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
|
||||
|
||||
def test_reset(mock_session):
|
||||
"""Test resetting the CLI."""
|
||||
with patch("openbb_cli.controllers.utils.remove_file"), patch(
|
||||
"sys.modules", new_callable=dict
|
||||
):
|
||||
reset()
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
|
||||
|
||||
def test_parse_and_split_input():
|
||||
"""Test parsing and splitting user input."""
|
||||
user_input = "ls -f /home/user/docs/document.xlsx"
|
||||
result = parse_and_split_input(user_input, [])
|
||||
assert "ls" in result[0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_command, expected_output",
|
||||
[
|
||||
("/", ["home"]),
|
||||
("ls -f /path/to/file.txt", ["ls -f ", "path", "to", "file.txt"]),
|
||||
("rm -f /home/user/docs", ["rm -f ", "home", "user", "docs"]),
|
||||
],
|
||||
)
|
||||
def test_parse_and_split_input_special_cases(input_command, expected_output):
|
||||
"""Test parsing and splitting user input with special cases."""
|
||||
result = parse_and_split_input(input_command, [])
|
||||
assert result == expected_output
|
||||
|
||||
|
||||
def test_print_guest_block_msg(mock_session):
|
||||
"""Test printing the guest block message."""
|
||||
print_guest_block_msg()
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
|
||||
|
||||
def test_welcome_message(mock_session):
|
||||
"""Test printing the welcome message."""
|
||||
welcome_message()
|
||||
mock_session.return_value.console.print.assert_called_with(
|
||||
"\nWelcome to OpenBB Platform CLI v1.0"
|
||||
)
|
||||
|
||||
|
||||
def test_get_flair_and_username(mock_session):
|
||||
"""Test getting the flair and username."""
|
||||
result = get_flair_and_username()
|
||||
assert "testuser" in result
|
||||
assert "rocket" in result #
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value, expected",
|
||||
[
|
||||
("10", 10),
|
||||
("0", 0),
|
||||
("-1", pytest.raises(argparse.ArgumentTypeError)),
|
||||
("text", pytest.raises(ValueError)),
|
||||
],
|
||||
)
|
||||
def test_check_non_negative(value, expected):
|
||||
"""Test checking for a non-negative value."""
|
||||
if isinstance(expected, int):
|
||||
assert check_non_negative(value) == expected
|
||||
else:
|
||||
with expected:
|
||||
check_non_negative(value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value, expected",
|
||||
[
|
||||
("1", 1),
|
||||
("0", pytest.raises(argparse.ArgumentTypeError)),
|
||||
("-1", pytest.raises(argparse.ArgumentTypeError)),
|
||||
("text", pytest.raises(ValueError)),
|
||||
],
|
||||
)
|
||||
def test_check_positive(value, expected):
|
||||
"""Test checking for a positive value."""
|
||||
if isinstance(expected, int):
|
||||
assert check_positive(value) == expected
|
||||
else:
|
||||
with expected:
|
||||
check_positive(value)
|
||||
|
||||
|
||||
def test_get_user_agent():
|
||||
"""Test getting the user agent."""
|
||||
result = get_user_agent()
|
||||
assert result.startswith("Mozilla/5.0")
|
||||
72
cli/tests/test_models_settings.py
Normal file
72
cli/tests/test_models_settings.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""Test the Models Settings module."""
|
||||
|
||||
from unittest.mock import mock_open, patch
|
||||
|
||||
from openbb_cli.models.settings import Settings
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
|
||||
def test_default_values():
|
||||
"""Test the default values of the settings model."""
|
||||
settings = Settings()
|
||||
assert settings.VERSION == "1.0.0"
|
||||
assert settings.TEST_MODE is False
|
||||
assert settings.DEBUG_MODE is False
|
||||
assert settings.DEV_BACKEND is False
|
||||
assert settings.FILE_OVERWRITE is False
|
||||
assert settings.SHOW_VERSION is True
|
||||
assert settings.USE_INTERACTIVE_DF is True
|
||||
assert settings.USE_CLEAR_AFTER_CMD is False
|
||||
assert settings.USE_DATETIME is True
|
||||
assert settings.USE_PROMPT_TOOLKIT is True
|
||||
assert settings.ENABLE_EXIT_AUTO_HELP is True
|
||||
assert settings.REMEMBER_CONTEXTS is True
|
||||
assert settings.ENABLE_RICH_PANEL is True
|
||||
assert settings.TOOLBAR_HINT is True
|
||||
assert settings.SHOW_MSG_OBBJECT_REGISTRY is False
|
||||
assert settings.TIMEZONE == "America/New_York"
|
||||
assert settings.FLAIR == ":openbb"
|
||||
assert settings.PREVIOUS_USE is False
|
||||
assert settings.N_TO_KEEP_OBBJECT_REGISTRY == 10
|
||||
assert settings.N_TO_DISPLAY_OBBJECT_REGISTRY == 5
|
||||
assert settings.RICH_STYLE == "dark"
|
||||
assert settings.ALLOWED_NUMBER_OF_ROWS == 20
|
||||
assert settings.ALLOWED_NUMBER_OF_COLUMNS == 5
|
||||
assert settings.HUB_URL == "https://my.openbb.co"
|
||||
assert settings.BASE_URL == "https://payments.openbb.co"
|
||||
|
||||
|
||||
# Test __repr__ output
|
||||
def test_repr():
|
||||
"""Test the __repr__ method of the settings model."""
|
||||
settings = Settings()
|
||||
repr_str = settings.__repr__() # pylint: disable=C2801
|
||||
assert "Settings\n\n" in repr_str
|
||||
assert "VERSION: 1.0.0" in repr_str
|
||||
|
||||
|
||||
# Test loading from environment variables
|
||||
@patch(
|
||||
"openbb_cli.models.settings.dotenv_values",
|
||||
return_value={"OPENBB_TEST_MODE": "True", "OPENBB_VERSION": "2.0.0"},
|
||||
)
|
||||
def test_from_env(mock_dotenv_values):
|
||||
"""Test loading settings from environment variables."""
|
||||
settings = Settings.from_env({}) # type: ignore
|
||||
assert settings["TEST_MODE"] == "True"
|
||||
assert settings["VERSION"] == "2.0.0"
|
||||
|
||||
|
||||
# Test setting an item and updating .env
|
||||
@patch("openbb_cli.models.settings.set_key")
|
||||
@patch(
|
||||
"openbb_cli.models.settings.open",
|
||||
new_callable=mock_open,
|
||||
read_data="TEST_MODE=False\n",
|
||||
)
|
||||
def test_set_item(mock_file, mock_set_key):
|
||||
"""Test setting an item and updating the .env file."""
|
||||
settings = Settings()
|
||||
settings.set_item("TEST_MODE", True)
|
||||
assert settings.TEST_MODE is True
|
||||
44
cli/tests/test_session.py
Normal file
44
cli/tests/test_session.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"Test the Session class."
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from openbb_cli.models.settings import Settings
|
||||
from openbb_cli.session import Session, sys
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument, protected-access
|
||||
|
||||
|
||||
def mock_isatty(return_value):
|
||||
"""Mock the isatty method."""
|
||||
original_isatty = sys.stdin.isatty
|
||||
sys.stdin.isatty = MagicMock(return_value=return_value) # type: ignore
|
||||
return original_isatty
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def session():
|
||||
"""Session fixture."""
|
||||
return Session()
|
||||
|
||||
|
||||
def test_session_initialization(session):
|
||||
"""Test the initialization of the Session class."""
|
||||
assert session.settings is not None
|
||||
assert session.style is not None
|
||||
assert session.console is not None
|
||||
assert session.obbject_registry is not None
|
||||
assert isinstance(session.settings, Settings)
|
||||
|
||||
|
||||
@patch("sys.stdin.isatty", return_value=True)
|
||||
def test_get_prompt_session_true(mock_isatty, session):
|
||||
"Test get_prompt_session method."
|
||||
prompt_session = session._get_prompt_session()
|
||||
assert prompt_session is not None
|
||||
|
||||
|
||||
@patch("sys.stdin.isatty", return_value=False)
|
||||
def test_get_prompt_session_false(mock_isatty, session):
|
||||
"Test get_prompt_session method."
|
||||
prompt_session = session._get_prompt_session()
|
||||
assert prompt_session is None
|
||||
Reference in New Issue
Block a user