Files
openbb-invest-api/tests/test_routes_shorts.py
Yaojia Wang 27b131492f test: add 159 tests for all new modules
New test files (171 tests):
- test_routes_shorts.py (16) - short volume, FTD, interest, darkpool
- test_routes_fixed_income.py (34) - treasury, yield curve, SOFR, etc.
- test_routes_economy.py (44) - CPI, GDP, FRED search, Fed holdings
- test_routes_surveys.py (17) - Michigan, SLOOS, NFP, Empire State
- test_routes_regulators.py (20) - COT, SEC litigation, institutions
- test_finnhub_service_social.py (20) - social/reddit sentiment unit tests
- test_routes_sentiment_social.py (20) - social endpoints + composite

Updated:
- test_routes_sentiment.py - match new composite sentiment response shape

Total: 261 tests passing (was 102)
2026-03-19 22:12:27 +01:00

177 lines
5.9 KiB
Python

"""Tests for shorts and dark pool routes."""
from unittest.mock import patch, AsyncMock
import pytest
from httpx import AsyncClient, ASGITransport
from main import app
@pytest.fixture
async def client():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as c:
yield c
# --- Short Volume ---
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_short_volume", new_callable=AsyncMock)
async def test_short_volume_happy_path(mock_fn, client):
mock_fn.return_value = [
{"date": "2026-03-18", "short_volume": 5000000, "short_exempt_volume": 10000, "total_volume": 20000000, "short_volume_percent": 0.25}
]
resp = await client.get("/api/v1/stock/AAPL/shorts/volume")
assert resp.status_code == 200
data = resp.json()
assert data["success"] is True
assert len(data["data"]) == 1
assert data["data"][0]["short_volume"] == 5000000
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_short_volume", new_callable=AsyncMock)
async def test_short_volume_empty(mock_fn, client):
mock_fn.return_value = []
resp = await client.get("/api/v1/stock/GME/shorts/volume")
assert resp.status_code == 200
data = resp.json()
assert data["success"] is True
assert data["data"] == []
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_short_volume", new_callable=AsyncMock)
async def test_short_volume_service_error_returns_502(mock_fn, client):
mock_fn.side_effect = RuntimeError("stockgrid unavailable")
resp = await client.get("/api/v1/stock/AAPL/shorts/volume")
assert resp.status_code == 502
@pytest.mark.asyncio
async def test_short_volume_invalid_symbol(client):
resp = await client.get("/api/v1/stock/AAPL;DROP/shorts/volume")
assert resp.status_code == 400
# --- Fails To Deliver ---
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_fails_to_deliver", new_callable=AsyncMock)
async def test_ftd_happy_path(mock_fn, client):
mock_fn.return_value = [
{"date": "2026-03-01", "cusip": "037833100", "failure_quantity": 50000, "symbol": "AAPL", "price": 175.0}
]
resp = await client.get("/api/v1/stock/AAPL/shorts/ftd")
assert resp.status_code == 200
data = resp.json()
assert data["success"] is True
assert data["data"][0]["symbol"] == "AAPL"
assert data["data"][0]["failure_quantity"] == 50000
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_fails_to_deliver", new_callable=AsyncMock)
async def test_ftd_empty(mock_fn, client):
mock_fn.return_value = []
resp = await client.get("/api/v1/stock/TSLA/shorts/ftd")
assert resp.status_code == 200
assert resp.json()["data"] == []
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_fails_to_deliver", new_callable=AsyncMock)
async def test_ftd_service_error_returns_502(mock_fn, client):
mock_fn.side_effect = RuntimeError("SEC connection failed")
resp = await client.get("/api/v1/stock/AAPL/shorts/ftd")
assert resp.status_code == 502
@pytest.mark.asyncio
async def test_ftd_invalid_symbol(client):
resp = await client.get("/api/v1/stock/BAD!!!/shorts/ftd")
assert resp.status_code == 400
# --- Short Interest ---
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_short_interest", new_callable=AsyncMock)
async def test_short_interest_happy_path(mock_fn, client):
mock_fn.return_value = [
{"settlement_date": "2026-02-28", "symbol": "GME", "short_interest": 20000000, "days_to_cover": 3.5}
]
resp = await client.get("/api/v1/stock/GME/shorts/interest")
assert resp.status_code == 200
data = resp.json()
assert data["success"] is True
assert data["data"][0]["short_interest"] == 20000000
assert data["data"][0]["days_to_cover"] == 3.5
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_short_interest", new_callable=AsyncMock)
async def test_short_interest_empty(mock_fn, client):
mock_fn.return_value = []
resp = await client.get("/api/v1/stock/NVDA/shorts/interest")
assert resp.status_code == 200
assert resp.json()["data"] == []
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_short_interest", new_callable=AsyncMock)
async def test_short_interest_service_error_returns_502(mock_fn, client):
mock_fn.side_effect = RuntimeError("FINRA unavailable")
resp = await client.get("/api/v1/stock/AAPL/shorts/interest")
assert resp.status_code == 502
@pytest.mark.asyncio
async def test_short_interest_invalid_symbol(client):
resp = await client.get("/api/v1/stock/INVALID!!!/shorts/interest")
assert resp.status_code == 400
# --- Dark Pool OTC ---
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_darkpool_otc", new_callable=AsyncMock)
async def test_darkpool_otc_happy_path(mock_fn, client):
mock_fn.return_value = [
{"date": "2026-03-18", "symbol": "AAPL", "shares": 3000000, "percentage": 12.5}
]
resp = await client.get("/api/v1/darkpool/AAPL/otc")
assert resp.status_code == 200
data = resp.json()
assert data["success"] is True
assert data["data"][0]["percentage"] == 12.5
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_darkpool_otc", new_callable=AsyncMock)
async def test_darkpool_otc_empty(mock_fn, client):
mock_fn.return_value = []
resp = await client.get("/api/v1/darkpool/TSLA/otc")
assert resp.status_code == 200
assert resp.json()["data"] == []
@pytest.mark.asyncio
@patch("routes_shorts.shorts_service.get_darkpool_otc", new_callable=AsyncMock)
async def test_darkpool_otc_service_error_returns_502(mock_fn, client):
mock_fn.side_effect = RuntimeError("FINRA connection timeout")
resp = await client.get("/api/v1/darkpool/AAPL/otc")
assert resp.status_code == 502
@pytest.mark.asyncio
async def test_darkpool_otc_invalid_symbol(client):
resp = await client.get("/api/v1/darkpool/BAD!!!/otc")
assert resp.status_code == 400