Files
Danglewood 869cfed125 [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 <>
2026-09-29 10:58:44 -07:00

191 lines
6.7 KiB
Python

"""Unit tests for the ``openbb-sec`` command-line interface."""
import asyncio
import json
import os
import sys
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from openbb_sec import cli
from openbb_sec.utils import cache
@pytest.fixture(autouse=True)
def _isolate_cache_env(monkeypatch):
"""Track the cache-dir env var so writes inside ``main`` are restored."""
monkeypatch.setenv(cache.CACHE_DIR_ENV_VAR, "__placeholder__")
monkeypatch.delenv(cache.CACHE_DIR_ENV_VAR, raising=False)
class TestResolveCacheDirectory:
"""cli.resolve_cache_directory precedence."""
def test_explicit_argument_wins(self):
out = cli.resolve_cache_directory("~/x/sec", discover=False)
assert out.endswith(os.path.join("x", "sec"))
assert os.path.isabs(out)
def test_env_var_override(self, monkeypatch, tmp_path):
monkeypatch.setenv(cache.CACHE_DIR_ENV_VAR, str(tmp_path / "envcache"))
out = cli.resolve_cache_directory(discover=False)
assert out == str((tmp_path / "envcache").resolve())
def test_falls_back_to_preferences(self, monkeypatch):
monkeypatch.delenv(cache.CACHE_DIR_ENV_VAR, raising=False)
prefs = SimpleNamespace(cache_directory="/data/openbb")
settings = SimpleNamespace(preferences=prefs)
monkeypatch.setattr(
"openbb_core.app.service.user_service.UserService",
lambda: SimpleNamespace(default_user_settings=settings),
)
out = cli.resolve_cache_directory(discover=False)
assert out == os.path.join(cli._abs("/data/openbb"), "sec")
def test_discover_loads_layered_config(self, monkeypatch, tmp_path):
called = {}
monkeypatch.setattr(
"openbb_core.app.config.loader.load_layered_config",
lambda: called.setdefault("loaded", True),
)
monkeypatch.setenv(cache.CACHE_DIR_ENV_VAR, str(tmp_path / "c"))
out = cli.resolve_cache_directory(discover=True)
assert called["loaded"] is True
assert out == str((tmp_path / "c").resolve())
class TestCreateApp:
"""cli.create_app factory."""
def test_adds_root_endpoints(self, monkeypatch):
from fastapi import FastAPI
fresh = FastAPI()
monkeypatch.setattr("openbb_core.api.rest_api.app", fresh)
monkeypatch.setattr(
"openbb_sec.sec_router.get_widgets_json",
AsyncMock(return_value={"w": 1}),
)
monkeypatch.setattr(
"openbb_sec.sec_router.get_apps_json",
AsyncMock(return_value=[{"a": 1}]),
)
app = cli.create_app()
routes = {
r.path: r
for r in app.routes
if getattr(r, "path", "")
in (
"/widgets.json",
"/apps.json",
)
}
assert set(routes) == {"/widgets.json", "/apps.json"}
widgets = asyncio.run(routes["/widgets.json"].endpoint())
apps = asyncio.run(routes["/apps.json"].endpoint())
assert json.loads(bytes(widgets.body)) == {"w": 1}
assert json.loads(bytes(apps.body)) == [{"a": 1}]
def test_idempotent(self, monkeypatch):
from fastapi import FastAPI
fresh = FastAPI()
monkeypatch.setattr("openbb_core.api.rest_api.app", fresh)
cli.create_app()
cli.create_app()
count = len(
[r for r in fresh.routes if getattr(r, "path", "") == "/widgets.json"]
)
assert count == 1
class TestPrintInfo:
"""cli._print_info output."""
def test_not_created(self, capsys, tmp_path):
cli._print_info(str(tmp_path / "missing"))
out = capsys.readouterr().out
assert "not created yet" in out
def test_with_usage(self, capsys, tmp_path):
(tmp_path / "a.bin").write_bytes(b"hello")
cli._print_info(str(tmp_path))
out = capsys.readouterr().out
assert "Disk usage" in out
assert "5 bytes" in out
class TestServe:
"""cli.serve delegation to openbb-api."""
def test_invokes_openbb_api(self, monkeypatch):
captured = {}
def fake_run(command, check):
captured["command"] = command
captured["check"] = check
return SimpleNamespace(returncode=0)
monkeypatch.setattr("subprocess.run", fake_run)
monkeypatch.setattr("importlib.util.find_spec", lambda name: object())
rc = cli.serve(["--port", "7000"])
assert rc == 0
assert captured["command"][:3] == [
sys.executable,
"-m",
"openbb_platform_api.main",
]
assert "--app" in captured["command"]
assert cli._APP_FACTORY in captured["command"]
assert "--factory" in captured["command"]
assert captured["command"][-2:] == ["--port", "7000"]
def test_missing_openbb_api(self, monkeypatch, capsys):
monkeypatch.setattr("importlib.util.find_spec", lambda name: None)
rc = cli.serve([])
assert rc == 1
assert "openbb-api is required" in capsys.readouterr().out
class TestMain:
"""cli.main command dispatch."""
def test_path(self, monkeypatch, capsys):
monkeypatch.setattr(cli, "resolve_cache_directory", lambda c: "/tmp/x/sec")
assert cli.main(["path"]) == 0
assert capsys.readouterr().out.strip() == "/tmp/x/sec"
def test_info(self, monkeypatch, capsys):
monkeypatch.setattr(cli, "resolve_cache_directory", lambda c: "/tmp/x/sec")
monkeypatch.setattr(cli, "_print_info", lambda d: print(f"INFO {d}"))
assert cli.main(["info"]) == 0
assert "INFO /tmp/x/sec" in capsys.readouterr().out
def test_clear(self, monkeypatch, capsys):
monkeypatch.setattr(cli, "resolve_cache_directory", lambda c: "/tmp/x/sec")
monkeypatch.setattr(cache, "clear_cache", lambda: 7)
assert cli.main(["clear"]) == 0
assert "Cleared 7 cache entries" in capsys.readouterr().out
def test_serve_default_with_passthrough(self, monkeypatch):
monkeypatch.setattr(cli, "resolve_cache_directory", lambda c: "/tmp/x/sec")
captured = {}
def fake_serve(passthrough):
captured["p"] = passthrough
return 0
monkeypatch.setattr(cli, "serve", fake_serve)
host = "0.0.0.0" # noqa: S104
rc = cli.main(["--host", host, "--port", "8000", "--reload"])
assert rc == 0
assert captured["p"] == ["--host", host, "--port", "8000", "--reload", "true"]
def test_serve_sets_cache_env(self, monkeypatch):
monkeypatch.setattr(cli, "resolve_cache_directory", lambda c: "/tmp/resolved")
monkeypatch.setattr(cli, "serve", lambda p: 0)
cli.main([])
assert cli.os.environ[cache.CACHE_DIR_ENV_VAR] == "/tmp/resolved"