Files
OpenBB/cli/tests/test_controllers_credentials_controller.py
Danglewood 9a30186174 [V5] CLI 2.0 - Pluggable Backends, Non-TTY Default, HTTP Dispatcher, Spec Files + Codegen (#7433)
* stash some changes

* add more robust testing

* mypy

* point PR at V5

* introduce spec file

* codespell

* test fix

* fix workflow environment setup

* fix workflow environment setup

* fix workflow environment setup

* add pyyaml to dependencies

* split lint jobs

* fix workflow environment setup

* fix workflow environment setup

* workflow env setup

* workflow env setup

* clean up code comments

* add auth hook entrypoints

* codespell

* add codegen feature

* codespell

* move _unpack into dispatchers for consistency with codegen packages

* surface nested models in the response

* fix missing coverage in CI

* socrata updates

* test fix

* detect plotly output

* add --include and --exclude flags from generate-extension command

* cap test matrix at python 3.14

* no useless comments

* platform controller command description split

* merge URL overloads from path params

* exclude none and unset from model dump

---------

Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
2026-06-01 19:14:38 +03:00

277 lines
9.6 KiB
Python

"""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)