Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] - CLI integration tests #6533

Merged
merged 12 commits into from
Jun 26, 2024
29 changes: 29 additions & 0 deletions cli/integration/test_commands.py
Original file line number Diff line number Diff line change
@@ -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}")
89 changes: 89 additions & 0 deletions cli/integration/test_integration_base_controller.py
Original file line number Diff line number Diff line change
@@ -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}"
81 changes: 81 additions & 0 deletions cli/integration/test_integration_base_platform_controller.py
Original file line number Diff line number Diff line change
@@ -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()
26 changes: 26 additions & 0 deletions cli/integration/test_integration_cli_controller.py
Original file line number Diff line number Diff line change
@@ -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}"
62 changes: 62 additions & 0 deletions cli/integration/test_integration_hub_service.py
Original file line number Diff line number Diff line change
@@ -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
55 changes: 55 additions & 0 deletions cli/integration/test_integration_obbject_registry.py
Original file line number Diff line number Diff line change
@@ -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
Loading