diff --git a/cli/integration/test_commands.py b/cli/integration/test_commands.py new file mode 100644 index 000000000000..517acd28eaa7 --- /dev/null +++ b/cli/integration/test_commands.py @@ -0,0 +1,29 @@ +import io + +import pytest +from openbb_cli.cli import main + + +@pytest.mark.parametrize( + "input_values", + [ + "/equity/price/historical --symbol aapl --provider fmp", + "/equity/price/historical --symbol msft --provider yfinance", + "/equity/price/historical --symbol goog --provider polygon", + "/crypto/price/historical --symbol btc --provider fmp", + "/currency/price/historical --symbol eur --provider fmp", + "/derivatives/futures/historical --symbol cl --provider fmp", + "/etf/price/historical --symbol spy --provider fmp", + "/economy", + ], +) +@pytest.mark.integration +def test_launch_with_cli_input(monkeypatch, input_values): + """Test launching the CLI and providing input via stdin with multiple parameters.""" + stdin = io.StringIO(input_values) + monkeypatch.setattr("sys.stdin", stdin) + + try: + main() + except Exception as e: + pytest.fail(f"Main function raised an exception: {e}") diff --git a/cli/integration/test_integration_base_controller.py b/cli/integration/test_integration_base_controller.py new file mode 100644 index 000000000000..efab88d1b303 --- /dev/null +++ b/cli/integration/test_integration_base_controller.py @@ -0,0 +1,89 @@ +"""Integration tests for the base_controller module.""" + +from unittest.mock import Mock, patch + +import pytest +from openbb_cli.controllers.base_controller import BaseController +from openbb_cli.session import Session + +# pylint: disable=unused-variable, redefined-outer-name + + +class TestController(BaseController): + """Test controller for the BaseController.""" + + PATH = "/test/" + + def print_help(self): + """Print help message.""" + + +@pytest.fixture +def base_controller(): + """Set up the environment for each test function.""" + session = Session() # noqa: F841 + controller = TestController() + return controller + + +@pytest.mark.integration +def test_check_path_valid(base_controller): + """Test that check_path does not raise an error for a valid path.""" + base_controller.PATH = "/equity/" + try: + base_controller.check_path() + except ValueError: + pytest.fail("check_path raised ValueError unexpectedly!") + + +@pytest.mark.integration +def test_check_path_invalid(base_controller): + """Test that check_path raises an error for an invalid path.""" + with pytest.raises(ValueError): + base_controller.PATH = "invalid_path" # Missing leading '/' + base_controller.check_path() + + with pytest.raises(ValueError): + base_controller.PATH = "/invalid_path" # Missing trailing '/' + base_controller.check_path() + + +@pytest.mark.integration +def test_parse_input(base_controller): + """Test the parse_input method.""" + input_str = "/equity/price/help" + expected_output = ["", "equity", "price", "help"] + assert ( + base_controller.parse_input(input_str) == expected_output + ), "Input parsing failed" + + +@pytest.mark.integration +def test_switch_command_execution(base_controller): + """Test the switch method.""" + base_controller.queue = [] + base_controller.switch("/home/../reset/") + assert base_controller.queue == [ + "home", + "..", + "reset", + ], "Switch did not update the queue correctly" + + +@patch("openbb_cli.controllers.base_controller.BaseController.call_help") +@pytest.mark.integration +def test_command_routing(mock_call_help, base_controller): + """Test the command routing.""" + base_controller.switch("help") + mock_call_help.assert_called_once() + + +@pytest.mark.integration +def test_custom_reset(base_controller): + """Test the custom reset method.""" + base_controller.custom_reset = Mock(return_value=["custom", "reset"]) + base_controller.call_reset(None) + expected_queue = ["quit", "reset", "custom", "reset"] + assert ( + base_controller.queue == expected_queue + ), f"Expected queue to be {expected_queue}, but was {base_controller.queue}" diff --git a/cli/integration/test_integration_base_platform_controller.py b/cli/integration/test_integration_base_platform_controller.py new file mode 100644 index 000000000000..2d645f14129d --- /dev/null +++ b/cli/integration/test_integration_base_platform_controller.py @@ -0,0 +1,81 @@ +"""Test the base platform controller.""" + +from unittest.mock import MagicMock, Mock, patch + +import pytest +from openbb_cli.controllers.base_platform_controller import ( + PlatformController, + Session, +) + +# pylint: disable=protected-access, unused-variable, redefined-outer-name + + +@pytest.fixture +def platform_controller(): + """Return a platform controller.""" + session = Session() # noqa: F841 + translators = {"test_command": MagicMock(), "test_menu": MagicMock()} # noqa: F841 + translators["test_command"]._parser = Mock( + _actions=[Mock(dest="data", choices=[], type=str, nargs=None)] + ) + translators["test_command"].execute_func = Mock(return_value=Mock()) + translators["test_menu"]._parser = Mock( + _actions=[Mock(dest="data", choices=[], type=str, nargs=None)] + ) + translators["test_menu"].execute_func = Mock(return_value=Mock()) + + controller = PlatformController( + name="test", parent_path=["platform"], translators=translators + ) + return controller + + +@pytest.mark.integration +def test_platform_controller_initialization(platform_controller): + """Test the initialization of the platform controller.""" + expected_path = "/platform/test/" + assert ( + expected_path == platform_controller.PATH + ), "Controller path was not set correctly" + + +@pytest.mark.integration +def test_command_generation(platform_controller): + """Test the generation of commands.""" + command_name = "test_command" + mock_execute_func = Mock(return_value=(Mock(), None)) + platform_controller.translators[command_name].execute_func = mock_execute_func + + platform_controller._generate_command_call( + name=command_name, translator=platform_controller.translators[command_name] + ) + command_method_name = f"call_{command_name}" + assert hasattr( + platform_controller, command_method_name + ), "Command method was not created" + + +@patch( + "openbb_cli.controllers.base_platform_controller.PlatformController._link_obbject_to_data_processing_commands" +) +@patch( + "openbb_cli.controllers.base_platform_controller.PlatformController._generate_commands" +) +@patch( + "openbb_cli.controllers.base_platform_controller.PlatformController._generate_sub_controllers" +) +@pytest.mark.integration +def test_platform_controller_calls( + mock_sub_controllers, mock_commands, mock_link_commands +): + """Test the calls of the platform controller.""" + translators = {"test_command": Mock()} + translators["test_command"].parser = Mock() + translators["test_command"].execute_func = Mock() + _ = PlatformController( + name="test", parent_path=["platform"], translators=translators + ) + mock_sub_controllers.assert_called_once() + mock_commands.assert_called_once() + mock_link_commands.assert_called_once() diff --git a/cli/integration/test_integration_cli_controller.py b/cli/integration/test_integration_cli_controller.py new file mode 100644 index 000000000000..8b76a8499f30 --- /dev/null +++ b/cli/integration/test_integration_cli_controller.py @@ -0,0 +1,26 @@ +"""Test the CLI controller integration.""" + +from openbb_cli.controllers.cli_controller import ( + CLIController, +) + + +def test_parse_input_valid_commands(): + """Test parse_input method.""" + controller = CLIController() + input_string = "exe --file test.openbb" + expected_output = [ + "exe --file test.openbb" + ] # Adjust based on actual expected behavior + assert controller.parse_input(input_string) == expected_output + + +def test_parse_input_invalid_commands(): + """Test parse_input method.""" + controller = CLIController() + input_string = "nonexistentcommand args" + expected_output = ["nonexistentcommand args"] + actual_output = controller.parse_input(input_string) + assert ( + actual_output == expected_output + ), f"Expected {expected_output}, got {actual_output}" diff --git a/cli/integration/test_integration_hub_service.py b/cli/integration/test_integration_hub_service.py new file mode 100644 index 000000000000..9413372b7867 --- /dev/null +++ b/cli/integration/test_integration_hub_service.py @@ -0,0 +1,62 @@ +"""Integration tests for the hub_service module.""" + +from unittest.mock import create_autospec, patch + +import pytest +import requests +from openbb_cli.controllers.hub_service import upload_routine +from openbb_core.app.model.hub.hub_session import HubSession + +# pylint: disable=unused-argument, redefined-outer-name, unused-variable + + +@pytest.fixture +def auth_header(): + """Return a fake auth header.""" + return "Bearer fake_token" + + +@pytest.fixture +def hub_session_mock(): + """Return a mock HubSession.""" + mock = create_autospec(HubSession, instance=True) + mock.username = "TestUser" + return mock + + +# Fixture for routine data +@pytest.fixture +def routine_data(): + """Return a dictionary with routine data.""" + return { + "name": "Test Routine", + "description": "A test routine", + "routine": "print('Hello World')", + "override": False, + "tags": "test", + "public": True, + } + + +@pytest.mark.integration +def test_upload_routine_timeout(auth_header, routine_data): + """Test upload_routine with a timeout exception.""" + with patch( + "requests.post", side_effect=requests.exceptions.Timeout + ) as mocked_post: # noqa: F841 + + response = upload_routine(auth_header, **routine_data) + + assert response is None + + +@pytest.mark.integration +def test_upload_routine_connection_error(auth_header, routine_data): + """Test upload_routine with a connection error.""" + with patch( + "requests.post", side_effect=requests.exceptions.ConnectionError + ) as mocked_post: # noqa: F841 + + response = upload_routine(auth_header, **routine_data) + + assert response is None diff --git a/cli/integration/test_integration_obbject_registry.py b/cli/integration/test_integration_obbject_registry.py new file mode 100644 index 000000000000..ec12dd0e1599 --- /dev/null +++ b/cli/integration/test_integration_obbject_registry.py @@ -0,0 +1,55 @@ +"""Test the obbject registry.""" + +import pytest +from openbb_cli.argparse_translator.obbject_registry import Registry +from openbb_core.app.model.obbject import OBBject + +# pylint: disable=unused-variable +# ruff: noqa: disable=F841 + + +def test_registry_operations(): + """Test the registry operations.""" + registry = Registry() + obbject1 = OBBject( + id="1", results=True, extra={"register_key": "key1", "command": "cmd1"} + ) + obbject2 = OBBject( + id="2", results=True, extra={"register_key": "key2", "command": "cmd2"} + ) + obbject3 = OBBject( # noqa: F841 + id="3", results=True, extra={"register_key": "key3", "command": "cmd3"} + ) + + # Add obbjects to the registry + assert registry.register(obbject1) is True + assert registry.register(obbject2) is True + # Attempt to add the same object again + assert registry.register(obbject1) is False + # Ensure the registry size is correct + assert len(registry.obbjects) == 2 + + # Get by index + assert registry.get(0) == obbject2 + assert registry.get(1) == obbject1 + # Get by key + assert registry.get("key1") == obbject1 + assert registry.get("key2") == obbject2 + # Invalid index/key + assert registry.get(2) is None + assert registry.get("invalid_key") is None + + # Remove an object + registry.remove(0) + assert len(registry.obbjects) == 1 + assert registry.get("key2") is None + + # Validate the 'all' property + all_obbjects = registry.all + assert "command" in all_obbjects[0] + assert all_obbjects[0]["command"] == "cmd1" + + # Clean up by removing all objects + registry.remove() + assert len(registry.obbjects) == 0 + assert registry.get("key1") is None