mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 21:42:22 +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 <>
131 lines
4.1 KiB
Python
131 lines
4.1 KiB
Python
"""Tests for the Deribit ticker subscription."""
|
|
|
|
import asyncio
|
|
import json
|
|
|
|
import pytest
|
|
from websockets import ConnectionClosed
|
|
|
|
from openbb_deribit.utils import websocket
|
|
|
|
|
|
class _Socket:
|
|
"""A stand-in for one websocket connection."""
|
|
|
|
def __init__(self, frames):
|
|
self.frames = list(frames)
|
|
self.sent: list = []
|
|
|
|
async def send(self, frame):
|
|
"""Record what the subscription asked for."""
|
|
self.sent.append(json.loads(frame))
|
|
|
|
async def recv(self):
|
|
"""Return the next frame, or stall when there are none left."""
|
|
if not self.frames:
|
|
await asyncio.sleep(10)
|
|
|
|
frame = self.frames.pop(0)
|
|
|
|
if isinstance(frame, Exception):
|
|
raise frame
|
|
|
|
return json.dumps(frame)
|
|
|
|
async def __aenter__(self):
|
|
"""Enter the connection context."""
|
|
return self
|
|
|
|
async def __aexit__(self, *args):
|
|
"""Leave the connection context."""
|
|
return False
|
|
|
|
|
|
def _tick(symbol):
|
|
"""Return one ticker frame for a symbol."""
|
|
return {"params": {"data": {"instrument_name": symbol, "mark_price": 1.0}}}
|
|
|
|
|
|
@pytest.fixture
|
|
def connect(monkeypatch):
|
|
"""Install a canned connection and return what it recorded."""
|
|
|
|
def install(frames):
|
|
socket = _Socket(frames)
|
|
monkeypatch.setattr("websockets.asyncio.client.connect", lambda url: socket)
|
|
|
|
return socket
|
|
|
|
return install
|
|
|
|
|
|
class TestSubscribeTickers:
|
|
"""The subscription returns one ticker per symbol it was asked for."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_collects_every_symbol(self, connect):
|
|
"""The read stops as soon as every symbol has arrived."""
|
|
socket = connect([_tick("A"), _tick("B")])
|
|
messages: set = set()
|
|
received = await websocket.subscribe_tickers(["A", "B"], messages)
|
|
|
|
assert set(received) == {"A", "B"}
|
|
assert messages == set()
|
|
assert socket.sent[0]["params"]["channels"] == [
|
|
"ticker.A.100ms",
|
|
"ticker.B.100ms",
|
|
]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ignores_duplicates_and_noise(self, connect):
|
|
"""Repeats and frames carrying no ticker are skipped."""
|
|
connect([{"id": 1}, _tick("A"), _tick("A"), _tick("B")])
|
|
messages: set = set()
|
|
received = await websocket.subscribe_tickers(["A", "B"], messages)
|
|
|
|
assert set(received) == {"A", "B"}
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reports_an_exchange_error(self, connect):
|
|
"""An error frame stops the read and is reported."""
|
|
connect([{"error": {"message": "no"}}])
|
|
messages: set = set()
|
|
received = await websocket.subscribe_tickers(["A"], messages)
|
|
|
|
assert received == {}
|
|
assert any("returned an error" in message for message in messages)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_closed_connection_returns_what_arrived(self, connect):
|
|
"""A connection that closed early keeps the tickers it did deliver."""
|
|
connect([_tick("A"), ConnectionClosed(None, None)])
|
|
messages: set = set()
|
|
received = await websocket.subscribe_tickers(["A", "B"], messages)
|
|
|
|
assert set(received) == {"A"}
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_timeout_says_how_many_are_missing(self, connect, monkeypatch):
|
|
"""A subscription that stalls names how much did not arrive."""
|
|
monkeypatch.setattr(websocket, "SUBSCRIBE_TIMEOUT", 0.05)
|
|
connect([_tick("A")])
|
|
messages: set = set()
|
|
received = await websocket.subscribe_tickers(["A", "B"], messages)
|
|
|
|
assert set(received) == {"A"}
|
|
assert any("1 of 2 contracts" in message for message in messages)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_failed_connection_is_reported(self, monkeypatch):
|
|
"""A connection that could not be opened is reported, not raised."""
|
|
|
|
def _connect(url):
|
|
raise OSError("no route to host")
|
|
|
|
monkeypatch.setattr("websockets.asyncio.client.connect", _connect)
|
|
messages: set = set()
|
|
received = await websocket.subscribe_tickers(["A"], messages)
|
|
|
|
assert received == {}
|
|
assert any("subscription failed" in message for message in messages)
|