mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-28 21:12:16 +00:00
* 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 <>
212 lines
6.2 KiB
Python
212 lines
6.2 KiB
Python
"""Tests for the pooled Deribit HTTP sessions."""
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
|
|
from openbb_deribit.utils import session as sessions
|
|
|
|
pytestmark = pytest.mark.uses_session
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _empty_pools():
|
|
"""Start and finish each test with empty pools."""
|
|
sessions._SESSIONS.clear()
|
|
sessions._CACHED_SESSIONS.clear()
|
|
yield
|
|
sessions._SESSIONS.clear()
|
|
sessions._CACHED_SESSIONS.clear()
|
|
|
|
|
|
class TestGetSession:
|
|
"""One uncached session is held per running loop."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_shared_within_a_loop(self):
|
|
"""Two callers on one loop share a session."""
|
|
first = await sessions.get_session()
|
|
second = await sessions.get_session()
|
|
|
|
try:
|
|
assert first is second
|
|
assert len(sessions._SESSIONS) == 1
|
|
finally:
|
|
await sessions.close_sessions()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_rebuilt_when_closed(self):
|
|
"""A session someone closed is replaced rather than reused."""
|
|
first = await sessions.get_session()
|
|
await first.close()
|
|
second = await sessions.get_session()
|
|
|
|
try:
|
|
assert first is not second
|
|
finally:
|
|
await sessions.close_sessions()
|
|
|
|
|
|
class TestGetCachedSession:
|
|
"""One cache-backed session is held per running loop, swept once."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_shared_and_swept_once(self, monkeypatch, tmp_path):
|
|
"""The cache is swept the first time a session is built."""
|
|
monkeypatch.setattr(
|
|
"openbb_deribit.utils.cache.cache_path", lambda: str(tmp_path / "deribit")
|
|
)
|
|
swept: list = []
|
|
|
|
async def _sweep(backend):
|
|
swept.append(backend)
|
|
|
|
monkeypatch.setattr("openbb_deribit.utils.cache.sweep_cache", _sweep)
|
|
first = await sessions.get_cached_session()
|
|
second = await sessions.get_cached_session()
|
|
|
|
try:
|
|
assert first is second
|
|
assert len(swept) == 1
|
|
finally:
|
|
await sessions.close_sessions()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_failing_sweep_does_not_break_the_session(
|
|
self, monkeypatch, tmp_path
|
|
):
|
|
"""A sweep that raises leaves a usable session behind."""
|
|
monkeypatch.setattr(
|
|
"openbb_deribit.utils.cache.cache_path", lambda: str(tmp_path / "deribit")
|
|
)
|
|
|
|
async def _sweep(backend):
|
|
raise RuntimeError("disk is gone")
|
|
|
|
monkeypatch.setattr("openbb_deribit.utils.cache.sweep_cache", _sweep)
|
|
session = await sessions.get_cached_session()
|
|
|
|
try:
|
|
assert session.closed is False
|
|
finally:
|
|
await sessions.close_sessions()
|
|
|
|
|
|
class TestCloseSessions:
|
|
"""Closing empties the pools and closes what was open."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_closes_everything(self, monkeypatch, tmp_path):
|
|
"""Both pools are emptied and both sessions closed."""
|
|
monkeypatch.setattr(
|
|
"openbb_deribit.utils.cache.cache_path", lambda: str(tmp_path / "deribit")
|
|
)
|
|
|
|
async def _sweep(backend):
|
|
return None
|
|
|
|
monkeypatch.setattr("openbb_deribit.utils.cache.sweep_cache", _sweep)
|
|
plain = await sessions.get_session()
|
|
cached = await sessions.get_cached_session()
|
|
await sessions.close_sessions()
|
|
|
|
assert plain.closed is True
|
|
assert cached.closed is True
|
|
assert not sessions._SESSIONS
|
|
assert not sessions._CACHED_SESSIONS
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_closes_only_the_named_loop(self):
|
|
"""Closing one loop leaves another loop's session alone."""
|
|
mine = await sessions.get_session()
|
|
sessions._SESSIONS["other"] = object()
|
|
await sessions.close_sessions(asyncio.get_running_loop())
|
|
|
|
try:
|
|
assert mine.closed is True
|
|
assert "other" in sessions._SESSIONS
|
|
finally:
|
|
sessions._SESSIONS.clear()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tolerates_a_session_that_will_not_close(self):
|
|
"""A session whose close raises does not stop the rest."""
|
|
|
|
class _Stubborn:
|
|
closed = False
|
|
|
|
async def close(self):
|
|
raise RuntimeError("no")
|
|
|
|
sessions._SESSIONS["stubborn"] = _Stubborn()
|
|
await sessions.close_sessions()
|
|
|
|
assert not sessions._SESSIONS
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_empty_pools(self):
|
|
"""Closing empty pools does nothing at all."""
|
|
await sessions.close_sessions()
|
|
|
|
assert not sessions._SESSIONS
|
|
|
|
|
|
class TestLoopHook:
|
|
"""A loop closes its own sessions when it shuts down."""
|
|
|
|
def test_hooks_once(self):
|
|
"""A loop is hooked the first time and left alone after."""
|
|
loop = asyncio.new_event_loop()
|
|
|
|
try:
|
|
original = loop.close
|
|
sessions._hook_loop_close(loop)
|
|
hooked = loop.close
|
|
sessions._hook_loop_close(loop)
|
|
|
|
assert hooked is not original
|
|
assert loop.close is hooked
|
|
finally:
|
|
loop.close()
|
|
|
|
def test_closes_the_pool_on_shutdown(self):
|
|
"""The sessions a loop opened are closed when the loop is."""
|
|
loop = asyncio.new_event_loop()
|
|
session = loop.run_until_complete(_open(loop))
|
|
|
|
assert session.closed is False
|
|
|
|
loop.close()
|
|
|
|
assert session.closed is True
|
|
assert not sessions._SESSIONS
|
|
|
|
def test_tolerates_a_pool_that_will_not_close_on_shutdown(self, monkeypatch):
|
|
"""A loop whose pool cannot be drained still closes."""
|
|
|
|
async def _close(loop=None):
|
|
raise RuntimeError("the pool is wedged")
|
|
|
|
monkeypatch.setattr(sessions, "close_sessions", _close)
|
|
loop = asyncio.new_event_loop()
|
|
sessions._hook_loop_close(loop)
|
|
loop.close()
|
|
|
|
assert loop.is_closed()
|
|
|
|
def test_tolerates_a_loop_that_rejects_the_hook(self):
|
|
"""A loop whose close cannot be replaced is left to the caller."""
|
|
|
|
class _Frozen:
|
|
__slots__ = ()
|
|
|
|
def close(self):
|
|
return None
|
|
|
|
sessions._hook_loop_close(_Frozen())
|
|
|
|
|
|
async def _open(loop):
|
|
"""Open one pooled session on the given loop."""
|
|
return await sessions.get_session()
|