Merge branch 'feature/unit-tests' of github.com:OpenBB-finance/OpenBBTerminal into feature/unit-tests

This commit is contained in:
hjoaquim
2023-07-26 11:05:13 +01:00
12 changed files with 523 additions and 24 deletions

View File

@@ -1,3 +1,4 @@
"""App factory."""
# pylint: disable=W0231:super-init-not-called
from openbb_core.app.command_runner import CommandRunnerSession
@@ -8,6 +9,7 @@ from openbb_core.app.static.coverage import Coverage
def create_app():
"""Create the app."""
try:
# pylint: disable=import-outside-toplevel
from openbb_core.app.static.package.__commands__ import Commands

View File

@@ -1,6 +1,9 @@
"""Container class."""
from openbb_core.app.command_runner import CommandRunnerSession
class Container:
"""Container class for the command runner session."""
def __init__(self, command_runner_session: CommandRunnerSession) -> None:
self._command_runner_session = command_runner_session

View File

@@ -1,14 +1,19 @@
"""Coverage module."""
from openbb_core.app.router import CommandMap
class Coverage:
"""Coverage class."""
def __init__(self):
self.__command_map = CommandMap()
@property
def providers(self):
"""Return providers coverage."""
return self.__command_map.provider_coverage
@property
def commands(self):
"""Return commands coverage."""
return self.__command_map.command_coverage

View File

@@ -1,3 +1,5 @@
"""OpenBB filters."""
import builtins
from functools import wraps
@@ -10,10 +12,12 @@ from openbb_core.app.utils import df_to_basemodel
class OpenBBError(Exception):
pass
"""A custom exception for OpenBB errors."""
def filter_call(func):
"""Filter command call."""
@wraps(wrapped=func)
def inner(*args, **kwargs):
self = args[0]
@@ -36,7 +40,7 @@ def filter_call(func):
def filter_inputs(**kwargs) -> dict:
"""Filter command inputs"""
"""Filter command inputs."""
for key, value in kwargs.items():
if isinstance(value, pd.DataFrame):
kwargs[key] = df_to_basemodel(value, index=True)
@@ -45,7 +49,7 @@ def filter_inputs(**kwargs) -> dict:
def filter_output(command_output: CommandOutput) -> CommandOutput:
"""Filter command output"""
"""Filter command output."""
if command_output.warnings:
for w in command_output.warnings:
category = getattr(builtins, w.category, OpenBBWarning)

View File

View File

@@ -2,6 +2,7 @@
# pylint: disable=redefined-outer-name
from dataclasses import dataclass
from unittest.mock import MagicMock, patch
import pytest
from openbb_core.app.model.command_context import CommandContext
@@ -11,6 +12,32 @@ from openbb_core.app.provider_interface import (
StandardParams,
)
from openbb_core.app.query import Query
from pydantic import BaseModel
def create_mock_query():
"""Mock query."""
class StockEOD:
"""Mock StockEOD class."""
start_date = "2020-01-01"
end_date = "2020-01-05"
symbol = "AAPL"
return StockEOD()
def create_mock_extra_params():
"""Mock ExtraParams dataclass."""
@dataclass
class StockEOD:
"""Mock ExtraParams dataclass."""
sort: str = "desc"
return StockEOD()
@pytest.fixture(scope="module")
@@ -31,29 +58,16 @@ def test_init(query):
def test_to_query_params(query):
"""Test to_query_params."""
standard_params = create_mock_query()
class StockEOD:
"""Mock StockEOD class."""
open: float = 0.0
high: float = 0.0
standard_params = StockEOD()
assert query.to_query_params(standard_params)
def test_filter_extra_params(query):
"""Test filter_extra_params."""
@dataclass
class StockEOD:
"""Mock ExtraParams dataclass."""
sort: str = "desc"
extra_params = StockEOD()
extra_params = create_mock_extra_params()
extra_params = query.filter_extra_params(extra_params, "fmp")
print(extra_params)
assert isinstance(extra_params, dict)
assert len(extra_params) == 0
@@ -73,7 +87,40 @@ def test_filter_extra_params_wrong_param(query):
assert not query.filter_extra_params(extra_params, "fmp")
@pytest.mark.skip(reason="TODO: see how to properly mock the execute method")
def test_execute(query):
"""Test execute."""
assert query.execute()
@pytest.fixture
def mock_registry():
"""Mock registry."""
with patch(
"openbb_core.app.provider_interface.get_provider_interface"
) as mock_get_provider_interface:
mock_registry = MagicMock()
mock_get_provider_interface.return_value.build_registry.return_value = (
mock_registry
)
yield mock_registry
@pytest.fixture
def query_instance():
"""Set up query."""
standard_params = create_mock_query()
extra_params = create_mock_extra_params()
cc = CommandContext()
setattr(cc.user_settings.credentials, "fmp_api_key", "1234")
return Query(
cc=cc,
provider_choices=ProviderChoices(provider="fmp"),
standard_params=standard_params,
extra_params=extra_params,
)
def test_execute_method_fake_credentials(query_instance, mock_registry):
"""Test execute method without setting credentials."""
mock_fetch_result = BaseModel() # Replace with an actual instance of BaseModel
mock_registry.fetch.return_value = mock_fetch_result
with pytest.raises(Exception):
query_instance.execute()

View File

@@ -0,0 +1,307 @@
"""Test router.py file."""
# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
import pytest
from openbb_core.app.model.command_context import CommandContext
from openbb_core.app.model.command_output import CommandOutput
from openbb_core.app.provider_interface import (
ExtraParams,
ProviderChoices,
StandardParams,
)
from openbb_core.app.router import (
CommandMap,
CommandValidator,
Router,
RouterLoader,
SignatureInspector,
)
from pydantic import BaseModel
@pytest.fixture(scope="module")
def command_validator():
"""Set up command_validator."""
return CommandValidator()
def test_command_validator_init(command_validator):
"""Test init."""
assert command_validator
@pytest.mark.parametrize(
"type_, expected",
[
(str, True),
(int, True),
(float, True),
(bool, True),
(list, True),
(BaseModel, False),
],
)
def test_is_standard_pydantic_type(command_validator, type_, expected):
"""Test is_standard_pydantic_type."""
# assert command_validator.is_standard_pydantic_type(str)
assert command_validator.is_standard_pydantic_type(type_) == expected
def test_is_valid_pydantic_model_type(command_validator):
"""Test is_valid_pydantic_model_type."""
assert command_validator.is_valid_pydantic_model_type(BaseModel)
assert not command_validator.is_valid_pydantic_model_type(str)
@pytest.mark.parametrize(
"type_, expected",
[
(str, True),
(int, True),
(float, True),
(bool, True),
(list, True),
(BaseModel, True),
],
)
def test_is_serializable_value_type(command_validator, type_, expected):
"""Test is_serializable_value_type."""
assert command_validator.is_serializable_value_type(type_) == expected
def test_is_annotated_dc(command_validator):
"""Test is_annotated_dc."""
assert not command_validator.is_annotated_dc(str)
def test_check_reserved_param(command_validator):
"""Test check_reserved_param."""
assert command_validator.check_reserved_param("name", str, {}, str, str) is None
assert not command_validator.check_reserved_param("name", str, {}, str, int)
def test_check_parameters(command_validator):
"""Test check_parameters."""
def func():
pass
assert command_validator.check_parameters(func) is None
assert not command_validator.check_parameters(BaseModel)
def test_check_return_error(command_validator):
"""Test check_return fail."""
with pytest.raises(TypeError):
def func():
pass
command_validator.check_return(func)
def test_check_return(command_validator):
"""Test check_return."""
def valid_function() -> CommandOutput[int]:
return CommandOutput(results=[1, 2, 3])
assert command_validator.check_return(valid_function) is None
def test_check(command_validator):
"""Test check."""
def valid_function() -> CommandOutput[int]:
return CommandOutput(results=[1, 2, 3])
assert command_validator.check(valid_function) is None
@pytest.fixture(scope="module")
def router():
"""Set up router."""
return Router()
def test_router_init(router):
"""Test init."""
assert router
def test_command(router):
"""Test command."""
@router.command
def valid_function() -> CommandOutput[int]:
return CommandOutput(results=[1, 2, 3])
assert valid_function
def test_include_router(router):
"""Test include_router."""
some_router = Router()
assert router.include_router(some_router) is None
@pytest.fixture(scope="module")
def router_loader():
"""Set up router_loader."""
return RouterLoader()
def test_router_loader_init(router_loader):
"""Test init."""
assert router_loader
def test_from_extensions(router_loader):
"""Test from_extensions."""
assert router_loader.from_extensions()
@pytest.fixture(scope="module")
def signature_inspector():
"""Set up signature_inspector."""
return SignatureInspector()
def test_signature_inspector_init(signature_inspector):
"""Test init."""
assert signature_inspector
def test_complete_signature(signature_inspector):
"""Test complete_signature."""
def sample_function(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> CommandOutput:
pass
model = "StockEOD"
assert signature_inspector.complete_signature(sample_function, model)
def test_complete_signature_error(signature_inspector):
"""Test complete_signature."""
def valid_function() -> CommandOutput[int]:
return CommandOutput(results=[1, 2, 3])
with pytest.raises(AttributeError):
signature_inspector.complete_signature(valid_function, "invalid_model")
def test_validate_signature(signature_inspector):
"""Test validate_signature."""
def sample_function(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> CommandOutput:
pass
expected_signature = {
"cc": CommandContext,
"provider_choices": ProviderChoices,
"standard_params": StandardParams,
"extra_params": ExtraParams,
}
assert (
signature_inspector.validate_signature(sample_function, expected_signature)
is None
)
def test_inject_dependency(signature_inspector):
"""Test inject_dependency."""
def sample_function(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> CommandOutput:
pass
assert signature_inspector.inject_dependency(sample_function, "cc", CommandContext)
def test_get_description(signature_inspector):
"""Test get_description."""
def some_function():
"""Mock function."""
assert signature_inspector.get_description(some_function) == some_function.__doc__
def test_get_description_no_doc(signature_inspector):
"""Test get_description."""
def some_function():
pass
assert signature_inspector.get_description(some_function) == ""
@pytest.fixture(scope="module")
def command_map():
"""Set up command_map."""
return CommandMap()
def test_command_map_init(command_map):
"""Test init."""
assert command_map
def test_map(command_map):
"""Test map."""
assert isinstance(command_map.map, dict)
def test_provider_coverage(command_map):
"""Test provider_coverage."""
assert isinstance(command_map.provider_coverage, dict)
def test_command_coverage(command_map):
"""Test command_coverage."""
assert isinstance(command_map.command_coverage, dict)
def test_get_command_map(command_map, router):
"""Test get_command_map."""
command_map = command_map.get_command_map(router)
assert isinstance(command_map, dict)
assert len(command_map) > 0
def test_get_provider_coverage(command_map, router):
"""Test get_provider_coverage."""
provider_coverage = command_map.get_provider_coverage(router)
assert isinstance(provider_coverage, dict)
def test_get_command_coverage(command_map, router):
"""Test get_command_coverage."""
command_coverage = command_map.get_command_coverage(router)
assert isinstance(command_coverage, dict)
def test_get_command(command_map):
"""Test get_command."""
command = command_map.get_command("stocks/load")
assert command is None

View File

@@ -0,0 +1,48 @@
"""Test static app factory."""
# pylint: disable=redefined-outer-name
import pytest
from openbb_core.app.model.system_settings import SystemSettings
from openbb_core.app.model.user_settings import UserSettings
from openbb_core.app.static.account import Account
from openbb_core.app.static.app_factory import create_app
from openbb_core.app.static.coverage import Coverage
@pytest.fixture(scope="module")
def app_factory():
"""Return app factory."""
return create_app()
def test_app_factory_init(app_factory):
"""Test app factory init."""
assert app_factory
def test_app_system_settings(app_factory):
"""Test app system settings."""
system_settings = app_factory.system
assert system_settings
assert isinstance(system_settings, SystemSettings)
def test_app_user_settings(app_factory):
"""Test app user settings."""
user_settings = app_factory.settings
assert user_settings
assert isinstance(user_settings, UserSettings)
def test_app_account(app_factory):
"""Test app account."""
account = app_factory.account
assert account
assert isinstance(account, Account)
def test_app_coverage(app_factory):
"""Test app coverage."""
coverage = app_factory.coverage
assert coverage
assert isinstance(coverage, Coverage)

View File

@@ -0,0 +1,9 @@
"""Test the app/static/container.py file."""
from openbb_core.app.static.container import Container
def test_container_init():
"""Test container init."""
container = Container(None)
assert container

View File

@@ -0,0 +1,30 @@
"""Test the app/static/coverage.py file."""
# pylint: disable=redefined-outer-name
import pytest
from openbb_core.app.static.coverage import Coverage
@pytest.fixture(scope="module")
def coverage():
"""Return coverage."""
return Coverage()
def test_coverage_init(coverage):
"""Test coverage init."""
assert coverage
def test_coverage_providers(coverage):
"""Test coverage providers."""
provider_coverage = coverage.providers
assert provider_coverage
assert isinstance(provider_coverage, dict)
def test_coverage_commands(coverage):
"""Test coverage commands."""
command_coverage = coverage.commands
assert command_coverage
assert isinstance(command_coverage, dict)

View File

@@ -0,0 +1,44 @@
"""Test the app/static/filters.py file."""
import pytest
from openbb_core.app.model.command_output import CommandOutput
from openbb_core.app.static.filters import filter_call, filter_inputs, filter_output
from pandas import DataFrame
def test_filter_call_error():
"""Test filter_call."""
class Test:
@filter_call
def test(self):
return 1 / 0
test = Test()
with pytest.raises(Exception):
test.test()
def test_filter_inputs_not_df():
"""Test filter_inputs."""
kwargs = {"num": 1}
kwargs = filter_inputs(**kwargs)
assert kwargs["num"] == 1
def test_filter_inputs_df():
"""Test filter_inputs."""
df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
kwargs = {"df": df}
kwargs = filter_inputs(**kwargs)
assert isinstance(kwargs["df"], list)
def test_filter_output():
"""Test filter_output."""
command_output = filter_output(CommandOutput())
assert command_output is not None

2
pyproject.toml vendored
View File

@@ -183,7 +183,7 @@ select = ["E", "W", "F", "Q", "S", "UP", "I", "PLC", "PLE", "PLR", "PLW", "SIM"]
ignore = ["S105", "S106", "S107", "S110", "PLR2004", "PLR0911", "PLR0913", "PLR0912", "PLR0915", "SIM114", "SIM105", "SIM117", "PLC1901", "PLR5501","PLW2901","PLW0603"]
[tool.ruff.per-file-ignores]
"tests/*" = ["S101"]
"*tests/*" = ["S101"]
[tool.ruff.flake8-import-conventions.aliases]
"matplotlib.pyplot" = "plt"