REST API wrapping OpenBB SDK for stock data, sentiment analysis, technical indicators, macro data, and rule-based portfolio analysis. - Stock data via yfinance (quote, profile, metrics, financials, historical, news) - News sentiment via Alpha Vantage (per-article, per-ticker scores) - Analyst data via Finnhub (recommendations, insider trades, upgrades) - Macro data via FRED (Fed rate, CPI, GDP, unemployment, treasury yields) - Technical indicators via openbb-technical (RSI, MACD, SMA, EMA, Bollinger) - Rule-based portfolio analysis engine (BUY_MORE/HOLD/SELL) - Stock discovery (gainers, losers, active, undervalued, growth) - 102 tests, all passing
95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
from unittest.mock import patch, AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
import finnhub_service
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
async def test_not_configured_returns_empty(mock_settings):
|
|
mock_settings.finnhub_api_key = ""
|
|
result = await finnhub_service.get_news_sentiment("AAPL")
|
|
assert result == {}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
async def test_not_configured_returns_message(mock_settings):
|
|
mock_settings.finnhub_api_key = ""
|
|
result = await finnhub_service.get_sentiment_summary("AAPL")
|
|
assert result["configured"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
async def test_insider_not_configured(mock_settings):
|
|
mock_settings.finnhub_api_key = ""
|
|
result = await finnhub_service.get_insider_transactions("AAPL")
|
|
assert result == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
async def test_recommendations_not_configured(mock_settings):
|
|
mock_settings.finnhub_api_key = ""
|
|
result = await finnhub_service.get_recommendation_trends("AAPL")
|
|
assert result == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
async def test_upgrade_downgrade_not_configured(mock_settings):
|
|
mock_settings.finnhub_api_key = ""
|
|
result = await finnhub_service.get_upgrade_downgrade("AAPL")
|
|
assert result == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
@patch("finnhub_service.httpx.AsyncClient")
|
|
async def test_news_sentiment_with_key(mock_client_cls, mock_settings):
|
|
mock_settings.finnhub_api_key = "test_key"
|
|
mock_resp = MagicMock()
|
|
mock_resp.json.return_value = {
|
|
"buzz": {"articlesInLastWeek": 10},
|
|
"sentiment": {"bullishPercent": 0.7, "bearishPercent": 0.3},
|
|
"companyNewsScore": 0.65,
|
|
"symbol": "AAPL",
|
|
}
|
|
mock_resp.raise_for_status = MagicMock()
|
|
|
|
mock_client = AsyncMock()
|
|
mock_client.get.return_value = mock_resp
|
|
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
|
mock_client.__aexit__ = AsyncMock(return_value=False)
|
|
mock_client_cls.return_value = mock_client
|
|
|
|
result = await finnhub_service.get_news_sentiment("AAPL")
|
|
assert result["symbol"] == "AAPL"
|
|
assert result["sentiment"]["bullishPercent"] == 0.7
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("finnhub_service.settings")
|
|
@patch("finnhub_service.httpx.AsyncClient")
|
|
async def test_insider_transactions_with_key(mock_client_cls, mock_settings):
|
|
mock_settings.finnhub_api_key = "test_key"
|
|
mock_resp = MagicMock()
|
|
mock_resp.json.return_value = {
|
|
"data": [
|
|
{"name": "Tim Cook", "share": 100000, "change": -50000, "transactionCode": "S"}
|
|
]
|
|
}
|
|
mock_resp.raise_for_status = MagicMock()
|
|
|
|
mock_client = AsyncMock()
|
|
mock_client.get.return_value = mock_resp
|
|
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
|
mock_client.__aexit__ = AsyncMock(return_value=False)
|
|
mock_client_cls.return_value = mock_client
|
|
|
|
result = await finnhub_service.get_insider_transactions("AAPL")
|
|
assert len(result) == 1
|
|
assert result[0]["name"] == "Tim Cook"
|