mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-06 01:12:27 +00:00
[Release] openbb-v5.0.0 (#7741)
* merge branch develop with main * bump desktop version * fix the merge * sync Cargo.lock to desktop 2.0.0 * fix cli conftest.py --------- Co-authored-by: deeleeramone <>
This commit is contained in:
276
cli/tests/test_controllers_credentials_controller.py
Normal file
276
cli/tests/test_controllers_credentials_controller.py
Normal file
@@ -0,0 +1,276 @@
|
||||
"""Test the CredentialsController class."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from pydantic import SecretStr
|
||||
|
||||
MODULE = "openbb_cli.controllers.credentials_controller"
|
||||
|
||||
|
||||
class TestCredentialStatus:
|
||||
"""Test the _credential_status helper function."""
|
||||
|
||||
def test_secret_str_with_value(self):
|
||||
from openbb_cli.controllers.credentials_controller import _credential_status
|
||||
|
||||
result = _credential_status(SecretStr("abcdef12345"))
|
||||
assert "Set" in result
|
||||
assert "abcd" in result
|
||||
assert "green" in result
|
||||
|
||||
def test_secret_str_empty(self):
|
||||
from openbb_cli.controllers.credentials_controller import _credential_status
|
||||
|
||||
result = _credential_status(SecretStr(""))
|
||||
assert "Not set" in result
|
||||
assert "red" in result
|
||||
|
||||
def test_non_secret_str(self):
|
||||
from openbb_cli.controllers.credentials_controller import _credential_status
|
||||
|
||||
result = _credential_status(None)
|
||||
assert "Not set" in result
|
||||
assert "red" in result
|
||||
|
||||
def test_plain_string(self):
|
||||
from openbb_cli.controllers.credentials_controller import _credential_status
|
||||
|
||||
result = _credential_status("some_string")
|
||||
assert "Not set" in result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_cred_session():
|
||||
"""Create a mock session for CredentialsController."""
|
||||
with patch(f"{MODULE}.session") as sess:
|
||||
sess.console = MagicMock()
|
||||
yield sess
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_obb():
|
||||
"""Mock the obb object with credentials."""
|
||||
with patch(f"{MODULE}.obb") as obb_mock:
|
||||
obb_mock.user.credentials = MagicMock()
|
||||
obb_mock.user.credentials.__class__.model_fields = {
|
||||
"test_api_key": MagicMock(description="test provider"),
|
||||
}
|
||||
obb_mock.user.credentials.test_api_key = SecretStr("mykey1234")
|
||||
yield obb_mock
|
||||
|
||||
|
||||
class TestCredentialsController:
|
||||
def test_init_generates_commands(self, mock_cred_session, mock_obb):
|
||||
from openbb_cli.controllers.credentials_controller import (
|
||||
CredentialsController,
|
||||
)
|
||||
|
||||
ctrl = CredentialsController.__new__(CredentialsController)
|
||||
ctrl.queue = []
|
||||
ctrl.update_completer = MagicMock()
|
||||
|
||||
ctrl._CRED_COMMANDS = {
|
||||
"test_api_key": {
|
||||
"command": "test_api_key",
|
||||
"field_name": "test_api_key",
|
||||
"provider": "test provider",
|
||||
}
|
||||
}
|
||||
ctrl.CHOICES_COMMANDS = list(ctrl._CRED_COMMANDS.keys())
|
||||
ctrl.parse_simple_args = MagicMock()
|
||||
|
||||
for cmd, field in ctrl._CRED_COMMANDS.items():
|
||||
ctrl._generate_credential_command(cmd, field)
|
||||
|
||||
assert hasattr(ctrl, "call_test_api_key")
|
||||
|
||||
def test_init_loop_runs_for_each_class_level_credential(
|
||||
self, mock_cred_session, mock_obb, monkeypatch
|
||||
):
|
||||
"""Real ``__init__`` path: when the class-level ``_CRED_COMMANDS`` is
|
||||
non-empty (the normal case for a populated install), the for-loop
|
||||
body runs once per credential and registers the dynamic
|
||||
``call_<name>`` method. On Windows CI installs without provider
|
||||
credentials the class dict is empty, so the loop body is skipped —
|
||||
this test pins coverage by populating the class attribute itself."""
|
||||
from openbb_cli.controllers.credentials_controller import (
|
||||
CredentialsController,
|
||||
)
|
||||
|
||||
# Patch the class attribute (not just the instance) so __init__ sees it.
|
||||
monkeypatch.setattr(
|
||||
CredentialsController,
|
||||
"_CRED_COMMANDS",
|
||||
{
|
||||
"covered_api_key": {
|
||||
"command": "covered_api_key",
|
||||
"field_name": "covered_api_key",
|
||||
"provider": "covered",
|
||||
}
|
||||
},
|
||||
)
|
||||
with patch.object(CredentialsController, "update_completer"):
|
||||
ctrl = CredentialsController(queue=[])
|
||||
assert hasattr(ctrl, "call_covered_api_key")
|
||||
|
||||
def test_print_help(self, mock_cred_session, mock_obb):
|
||||
from openbb_cli.controllers.credentials_controller import (
|
||||
CredentialsController,
|
||||
)
|
||||
|
||||
ctrl = CredentialsController.__new__(CredentialsController)
|
||||
ctrl.queue = []
|
||||
ctrl.update_completer = MagicMock()
|
||||
ctrl._CRED_COMMANDS = {
|
||||
"test_api_key": {
|
||||
"command": "test_api_key",
|
||||
"field_name": "test_api_key",
|
||||
"provider": "test provider",
|
||||
}
|
||||
}
|
||||
ctrl.print_help()
|
||||
mock_cred_session.console.print.assert_called_once()
|
||||
call_kwargs = mock_cred_session.console.print.call_args[1]
|
||||
assert "Credentials" in call_kwargs.get("menu", "")
|
||||
|
||||
def test_set_credential_via_value(self, mock_cred_session, mock_obb):
|
||||
from openbb_cli.controllers.credentials_controller import (
|
||||
CredentialsController,
|
||||
)
|
||||
|
||||
ctrl = CredentialsController.__new__(CredentialsController)
|
||||
ctrl.queue = []
|
||||
ctrl.update_completer = MagicMock()
|
||||
ctrl._CRED_COMMANDS = {
|
||||
"test_api_key": {
|
||||
"command": "test_api_key",
|
||||
"field_name": "test_api_key",
|
||||
"provider": "test provider",
|
||||
}
|
||||
}
|
||||
|
||||
ns = MagicMock()
|
||||
ns.value = "new_secret"
|
||||
ctrl.parse_simple_args = MagicMock(return_value=(ns, []))
|
||||
|
||||
for cmd, field in ctrl._CRED_COMMANDS.items():
|
||||
ctrl._generate_credential_command(cmd, field)
|
||||
|
||||
ctrl.call_test_api_key(["-v", "new_secret"])
|
||||
mock_cred_session.console.print.assert_called()
|
||||
args = mock_cred_session.console.print.call_args[0][0]
|
||||
assert "Set" in args
|
||||
|
||||
def test_view_credential_no_args(self, mock_cred_session, mock_obb):
|
||||
from openbb_cli.controllers.credentials_controller import (
|
||||
CredentialsController,
|
||||
)
|
||||
|
||||
ctrl = CredentialsController.__new__(CredentialsController)
|
||||
ctrl.queue = []
|
||||
ctrl.update_completer = MagicMock()
|
||||
ctrl._CRED_COMMANDS = {
|
||||
"test_api_key": {
|
||||
"command": "test_api_key",
|
||||
"field_name": "test_api_key",
|
||||
"provider": "test provider",
|
||||
}
|
||||
}
|
||||
|
||||
ns = MagicMock()
|
||||
ns.value = None
|
||||
ctrl.parse_simple_args = MagicMock(return_value=(ns, []))
|
||||
|
||||
for cmd, field in ctrl._CRED_COMMANDS.items():
|
||||
ctrl._generate_credential_command(cmd, field)
|
||||
|
||||
ctrl.call_test_api_key([])
|
||||
mock_cred_session.console.print.assert_called()
|
||||
|
||||
def test_init_runs_super_and_generates_all_commands(
|
||||
self, mock_cred_session, mock_obb
|
||||
):
|
||||
"""Real ``__init__`` runs ``super().__init__()`` and generates one ``call_*`` per credential."""
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
from openbb_cli.controllers.credentials_controller import (
|
||||
CredentialsController,
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"openbb_cli.controllers.base_controller.BaseController.__init__",
|
||||
return_value=None,
|
||||
) as super_init,
|
||||
patch.object(
|
||||
CredentialsController,
|
||||
"choices_default",
|
||||
new_callable=PropertyMock,
|
||||
return_value={},
|
||||
),
|
||||
patch.object(CredentialsController, "update_completer") as update_completer,
|
||||
patch.object(CredentialsController, "_generate_credential_command") as gen,
|
||||
):
|
||||
CredentialsController(queue=["x"])
|
||||
super_init.assert_called_once_with(["x"])
|
||||
assert gen.call_count == len(CredentialsController._CRED_COMMANDS)
|
||||
update_completer.assert_called_once()
|
||||
|
||||
|
||||
def test_class_level_loop_builds_cred_commands_from_model_fields():
|
||||
"""Re-import the module with a populated ``credentials.model_fields`` so the
|
||||
class-body loop (which derives ``_CRED_COMMANDS`` from the live credentials
|
||||
class) is exercised. Without this, environments where no provider extensions
|
||||
are installed leave the loop body uncovered.
|
||||
|
||||
Covers both branches of the provider derivation:
|
||||
* ``description == name`` → suffix-stripping fallback
|
||||
* ``description != name`` → keep description as the provider label
|
||||
"""
|
||||
import importlib
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import openbb
|
||||
|
||||
fmp_field = MagicMock()
|
||||
fmp_field.description = "fmp_api_key"
|
||||
polygon_field = MagicMock()
|
||||
polygon_field.description = "Polygon API token"
|
||||
bare_token_field = MagicMock()
|
||||
bare_token_field.description = "tiingo_token"
|
||||
plain_key_field = MagicMock()
|
||||
plain_key_field.description = "alpaca_key"
|
||||
|
||||
fake_creds_class = type(
|
||||
"FakeCredentials",
|
||||
(),
|
||||
{
|
||||
"model_fields": {
|
||||
"fmp_api_key": fmp_field,
|
||||
"polygon_token": polygon_field,
|
||||
"tiingo_token": bare_token_field,
|
||||
"alpaca_key": plain_key_field,
|
||||
}
|
||||
},
|
||||
)
|
||||
fake_creds_instance = MagicMock()
|
||||
fake_creds_instance.__class__ = fake_creds_class
|
||||
|
||||
fake_obb = MagicMock()
|
||||
fake_obb.user.credentials = fake_creds_instance
|
||||
|
||||
original_obb = openbb.obb
|
||||
openbb.obb = fake_obb
|
||||
sys.modules.pop("openbb_cli.controllers.credentials_controller", None)
|
||||
try:
|
||||
mod = importlib.import_module("openbb_cli.controllers.credentials_controller")
|
||||
cmds = mod.CredentialsController._CRED_COMMANDS
|
||||
assert cmds["fmp_api_key"]["provider"] == "fmp"
|
||||
assert cmds["polygon_token"]["provider"] == "Polygon API token"
|
||||
assert cmds["tiingo_token"]["provider"] == "tiingo"
|
||||
assert cmds["alpaca_key"]["provider"] == "alpaca"
|
||||
finally:
|
||||
openbb.obb = original_obb
|
||||
sys.modules.pop("openbb_cli.controllers.credentials_controller", None)
|
||||
Reference in New Issue
Block a user