"""
Binance Derivatives Trading Options WebSocket Market Streams

OpenAPI Specification for the Binance Derivatives Trading Options WebSocket Market Streams
The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
"""

import pytest

from unittest.mock import AsyncMock, MagicMock

from binance_common.configuration import ConfigurationWebSocketStreams
from binance_common.errors import RequiredError
from binance_common.utils import ws_streams_placeholder
from binance_common.websocket import WebSocketStreamBase, RequestStreamHandle
from binance_sdk_derivatives_trading_options.websocket_streams.streams import (
    WebsocketMarketStreamsApi,
)


@pytest.fixture
def mock_websocket_client():
    """Fixture to provide a reusable mocked WebSocketStreamBase instance."""
    config = ConfigurationWebSocketStreams(stream_url="wss://mock-url")
    websocket_stream_client = WebSocketStreamBase(config)
    websocket_stream_client.subscribe = AsyncMock()
    websocket_stream_client.unsubscribe = AsyncMock()
    return websocket_stream_client


class TestWebSocketStreams:
    @pytest.fixture(autouse=True)
    def setup(self, mock_websocket_client):
        self.websocket_client = mock_websocket_client
        self.ws_streams = WebsocketMarketStreamsApi(
            websocket_base=self.websocket_client
        )

    @pytest.mark.asyncio
    async def test_index_price_streams_subscription(self):
        """Test that index_price_streams() subscribes to the correct WebSocket stream."""

        params = {
            "symbol": "btcusdt",
        }

        expected_response = {
            "e": "index",
            "E": 1661415480351,
            "s": "ETHUSDT",
            "p": "1707.89008607",
        }
        stream_endpoint = ws_streams_placeholder(
            "/<symbol>@index".replace("/", "", 1),
            params,
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.index_price_streams(**params)
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_index_price_streams_success(self):
        """Test index_price_streams() successfully with required parameters only."""

        params = {
            "symbol": "btcusdt",
        }

        expected_response = {
            "e": "index",
            "E": 1661415480351,
            "s": "ETHUSDT",
            "p": "1707.89008607",
        }
        self.ws_streams.index_price_streams = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.index_price_streams(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_index_price_streams_success_with_optional_params(self):
        """Test index_price_streams() successfully with optional parameters."""

        params = {"symbol": "btcusdt", "id": 532601580}

        expected_response = {
            "e": "index",
            "E": 1661415480351,
            "s": "ETHUSDT",
            "p": "1707.89008607",
        }

        self.ws_streams.index_price_streams = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.index_price_streams(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_index_price_streams_missing_required_param_symbol(self):
        """Test that index_price_streams() raises RequiredError when 'symbol' is missing."""
        params = {
            "symbol": "btcusdt",
        }
        params["symbol"] = None

        with pytest.raises(RequiredError, match="Missing required parameter 'symbol'"):
            await self.ws_streams.index_price_streams(**params)

    @pytest.mark.asyncio
    async def test_index_price_streams_server_error(self):
        """Test that index_price_streams() raises an error when the server returns an error."""

        params = {
            "symbol": "btcusdt",
        }

        mock_error = Exception("ResponseError")
        self.ws_streams.index_price_streams = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.index_price_streams(**params)

    @pytest.mark.asyncio
    async def test_kline_candlestick_streams_subscription(self):
        """Test that kline_candlestick_streams() subscribes to the correct WebSocket stream."""

        params = {
            "symbol": "btcusdt",
            "interval": "1m",
        }

        expected_response = {
            "e": "kline",
            "E": 1638747660000,
            "s": "BTC-200630-9000-P",
            "k": {
                "t": 1638747660000,
                "T": 1638747719999,
                "s": "BTC-200630-9000-P",
                "i": "1m",
                "F": 0,
                "L": 0,
                "o": "1000",
                "c": "1000",
                "h": "1000",
                "l": "1000",
                "v": "0",
                "n": 0,
                "x": False,
                "q": "0",
                "V": "0",
                "Q": "0",
            },
        }
        stream_endpoint = ws_streams_placeholder(
            "/<symbol>@kline_<interval>".replace("/", "", 1),
            params,
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.kline_candlestick_streams(**params)
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_kline_candlestick_streams_success(self):
        """Test kline_candlestick_streams() successfully with required parameters only."""

        params = {
            "symbol": "btcusdt",
            "interval": "1m",
        }

        expected_response = {
            "e": "kline",
            "E": 1638747660000,
            "s": "BTC-200630-9000-P",
            "k": {
                "t": 1638747660000,
                "T": 1638747719999,
                "s": "BTC-200630-9000-P",
                "i": "1m",
                "F": 0,
                "L": 0,
                "o": "1000",
                "c": "1000",
                "h": "1000",
                "l": "1000",
                "v": "0",
                "n": 0,
                "x": False,
                "q": "0",
                "V": "0",
                "Q": "0",
            },
        }
        self.ws_streams.kline_candlestick_streams = AsyncMock(
            return_value=expected_response
        )

        response = await self.ws_streams.kline_candlestick_streams(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_kline_candlestick_streams_success_with_optional_params(self):
        """Test kline_candlestick_streams() successfully with optional parameters."""

        params = {"symbol": "btcusdt", "interval": "1m", "id": 532601580}

        expected_response = {
            "e": "kline",
            "E": 1638747660000,
            "s": "BTC-200630-9000-P",
            "k": {
                "t": 1638747660000,
                "T": 1638747719999,
                "s": "BTC-200630-9000-P",
                "i": "1m",
                "F": 0,
                "L": 0,
                "o": "1000",
                "c": "1000",
                "h": "1000",
                "l": "1000",
                "v": "0",
                "n": 0,
                "x": False,
                "q": "0",
                "V": "0",
                "Q": "0",
            },
        }

        self.ws_streams.kline_candlestick_streams = AsyncMock(
            return_value=expected_response
        )

        response = await self.ws_streams.kline_candlestick_streams(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_kline_candlestick_streams_missing_required_param_symbol(self):
        """Test that kline_candlestick_streams() raises RequiredError when 'symbol' is missing."""
        params = {
            "symbol": "btcusdt",
            "interval": "1m",
        }
        params["symbol"] = None

        with pytest.raises(RequiredError, match="Missing required parameter 'symbol'"):
            await self.ws_streams.kline_candlestick_streams(**params)

    @pytest.mark.asyncio
    async def test_kline_candlestick_streams_missing_required_param_interval(self):
        """Test that kline_candlestick_streams() raises RequiredError when 'interval' is missing."""
        params = {
            "symbol": "btcusdt",
            "interval": "1m",
        }
        params["interval"] = None

        with pytest.raises(
            RequiredError, match="Missing required parameter 'interval'"
        ):
            await self.ws_streams.kline_candlestick_streams(**params)

    @pytest.mark.asyncio
    async def test_kline_candlestick_streams_server_error(self):
        """Test that kline_candlestick_streams() raises an error when the server returns an error."""

        params = {
            "symbol": "btcusdt",
            "interval": "1m",
        }

        mock_error = Exception("ResponseError")
        self.ws_streams.kline_candlestick_streams = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.kline_candlestick_streams(**params)

    @pytest.mark.asyncio
    async def test_mark_price_subscription(self):
        """Test that mark_price() subscribes to the correct WebSocket stream."""

        params = {
            "underlying_asset": "ETH",
        }

        expected_response = [
            {
                "e": "markPrice",
                "E": 1663684594227,
                "s": "ETH-220930-1500-C",
                "mp": "30.3",
            },
            {
                "e": "markPrice",
                "E": 1663684594228,
                "s": "ETH-220923-1000-C",
                "mp": "341.5",
            },
        ]
        stream_endpoint = ws_streams_placeholder(
            "/<underlyingAsset>@markPrice".replace("/", "", 1),
            params,
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.mark_price(**params)
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_mark_price_success(self):
        """Test mark_price() successfully with required parameters only."""

        params = {
            "underlying_asset": "ETH",
        }

        expected_response = [
            {
                "e": "markPrice",
                "E": 1663684594227,
                "s": "ETH-220930-1500-C",
                "mp": "30.3",
            },
            {
                "e": "markPrice",
                "E": 1663684594228,
                "s": "ETH-220923-1000-C",
                "mp": "341.5",
            },
        ]
        self.ws_streams.mark_price = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.mark_price(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_mark_price_success_with_optional_params(self):
        """Test mark_price() successfully with optional parameters."""

        params = {"underlying_asset": "ETH", "id": 532601580}

        expected_response = [
            {
                "e": "markPrice",
                "E": 1663684594227,
                "s": "ETH-220930-1500-C",
                "mp": "30.3",
            },
            {
                "e": "markPrice",
                "E": 1663684594228,
                "s": "ETH-220923-1000-C",
                "mp": "341.5",
            },
        ]

        self.ws_streams.mark_price = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.mark_price(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_mark_price_missing_required_param_underlying_asset(self):
        """Test that mark_price() raises RequiredError when 'underlying_asset' is missing."""
        params = {
            "underlying_asset": "ETH",
        }
        params["underlying_asset"] = None

        with pytest.raises(
            RequiredError, match="Missing required parameter 'underlying_asset'"
        ):
            await self.ws_streams.mark_price(**params)

    @pytest.mark.asyncio
    async def test_mark_price_server_error(self):
        """Test that mark_price() raises an error when the server returns an error."""

        params = {
            "underlying_asset": "ETH",
        }

        mock_error = Exception("ResponseError")
        self.ws_streams.mark_price = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.mark_price(**params)

    @pytest.mark.asyncio
    async def test_new_symbol_info_subscription(self):
        """Test that new_symbol_info() subscribes to the correct WebSocket stream."""

        expected_response = {
            "e": "OPTION_PAIR",
            "E": 1668573571842,
            "u": "BTCUSDT",
            "qa": "USDT",
            "s": "BTC-221116-21000-C",
            "unit": 1,
            "mq": "0.01",
            "d": "CALL",
            "sp": "21000",
            "ed": 1668585600000,
        }
        stream_endpoint = ws_streams_placeholder(
            "/option_pair".replace("/", "", 1),
            {},
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.new_symbol_info()
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_new_symbol_info_success(self):
        """Test new_symbol_info() successfully with required parameters only."""

        expected_response = {
            "e": "OPTION_PAIR",
            "E": 1668573571842,
            "u": "BTCUSDT",
            "qa": "USDT",
            "s": "BTC-221116-21000-C",
            "unit": 1,
            "mq": "0.01",
            "d": "CALL",
            "sp": "21000",
            "ed": 1668585600000,
        }
        self.ws_streams.new_symbol_info = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.new_symbol_info()
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_new_symbol_info_success_with_optional_params(self):
        """Test new_symbol_info() successfully with optional parameters."""

        params = {"id": 532601580}

        expected_response = {
            "e": "OPTION_PAIR",
            "E": 1668573571842,
            "u": "BTCUSDT",
            "qa": "USDT",
            "s": "BTC-221116-21000-C",
            "unit": 1,
            "mq": "0.01",
            "d": "CALL",
            "sp": "21000",
            "ed": 1668585600000,
        }

        self.ws_streams.new_symbol_info = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.new_symbol_info(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_new_symbol_info_server_error(self):
        """Test that new_symbol_info() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.ws_streams.new_symbol_info = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.new_symbol_info()

    @pytest.mark.asyncio
    async def test_open_interest_subscription(self):
        """Test that open_interest() subscribes to the correct WebSocket stream."""

        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
        }

        expected_response = [
            {
                "e": "openInterest",
                "E": 1668759300045,
                "s": "ETH-221125-2700-C",
                "o": "1580.87",
                "h": "1912992.178168204",
            }
        ]
        stream_endpoint = ws_streams_placeholder(
            "/<underlyingAsset>@openInterest@<expirationDate>".replace("/", "", 1),
            params,
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.open_interest(**params)
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_open_interest_success(self):
        """Test open_interest() successfully with required parameters only."""

        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
        }

        expected_response = [
            {
                "e": "openInterest",
                "E": 1668759300045,
                "s": "ETH-221125-2700-C",
                "o": "1580.87",
                "h": "1912992.178168204",
            }
        ]
        self.ws_streams.open_interest = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.open_interest(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_open_interest_success_with_optional_params(self):
        """Test open_interest() successfully with optional parameters."""

        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
            "id": 532601580,
        }

        expected_response = [
            {
                "e": "openInterest",
                "E": 1668759300045,
                "s": "ETH-221125-2700-C",
                "o": "1580.87",
                "h": "1912992.178168204",
            }
        ]

        self.ws_streams.open_interest = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.open_interest(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_open_interest_missing_required_param_underlying_asset(self):
        """Test that open_interest() raises RequiredError when 'underlying_asset' is missing."""
        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
        }
        params["underlying_asset"] = None

        with pytest.raises(
            RequiredError, match="Missing required parameter 'underlying_asset'"
        ):
            await self.ws_streams.open_interest(**params)

    @pytest.mark.asyncio
    async def test_open_interest_missing_required_param_expiration_date(self):
        """Test that open_interest() raises RequiredError when 'expiration_date' is missing."""
        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
        }
        params["expiration_date"] = None

        with pytest.raises(
            RequiredError, match="Missing required parameter 'expiration_date'"
        ):
            await self.ws_streams.open_interest(**params)

    @pytest.mark.asyncio
    async def test_open_interest_server_error(self):
        """Test that open_interest() raises an error when the server returns an error."""

        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
        }

        mock_error = Exception("ResponseError")
        self.ws_streams.open_interest = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.open_interest(**params)

    @pytest.mark.asyncio
    async def test_partial_book_depth_streams_subscription(self):
        """Test that partial_book_depth_streams() subscribes to the correct WebSocket stream."""

        params = {
            "symbol": "btcusdt",
            "levels": 10,
        }

        expected_response = {
            "e": "depth",
            "E": 1591695934010,
            "T": 1591695934000,
            "s": "BTC-200630-9000-P",
            "u": 162,
            "pu": 162,
            "b": [["200", "3"], ["101", "1"], ["100", "2"]],
            "a": [["1000", "89"]],
        }
        stream_endpoint = ws_streams_placeholder(
            "/<symbol>@depth<levels>@<updateSpeed>".replace("/", "", 1),
            params,
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.partial_book_depth_streams(**params)
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_partial_book_depth_streams_success(self):
        """Test partial_book_depth_streams() successfully with required parameters only."""

        params = {
            "symbol": "btcusdt",
            "levels": 10,
        }

        expected_response = {
            "e": "depth",
            "E": 1591695934010,
            "T": 1591695934000,
            "s": "BTC-200630-9000-P",
            "u": 162,
            "pu": 162,
            "b": [["200", "3"], ["101", "1"], ["100", "2"]],
            "a": [["1000", "89"]],
        }
        self.ws_streams.partial_book_depth_streams = AsyncMock(
            return_value=expected_response
        )

        response = await self.ws_streams.partial_book_depth_streams(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_partial_book_depth_streams_success_with_optional_params(self):
        """Test partial_book_depth_streams() successfully with optional parameters."""

        params = {
            "symbol": "btcusdt",
            "levels": 10,
            "id": 532601580,
            "update_speed": "update_speed_example",
        }

        expected_response = {
            "e": "depth",
            "E": 1591695934010,
            "T": 1591695934000,
            "s": "BTC-200630-9000-P",
            "u": 162,
            "pu": 162,
            "b": [["200", "3"], ["101", "1"], ["100", "2"]],
            "a": [["1000", "89"]],
        }

        self.ws_streams.partial_book_depth_streams = AsyncMock(
            return_value=expected_response
        )

        response = await self.ws_streams.partial_book_depth_streams(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_partial_book_depth_streams_missing_required_param_symbol(self):
        """Test that partial_book_depth_streams() raises RequiredError when 'symbol' is missing."""
        params = {
            "symbol": "btcusdt",
            "levels": 10,
        }
        params["symbol"] = None

        with pytest.raises(RequiredError, match="Missing required parameter 'symbol'"):
            await self.ws_streams.partial_book_depth_streams(**params)

    @pytest.mark.asyncio
    async def test_partial_book_depth_streams_missing_required_param_levels(self):
        """Test that partial_book_depth_streams() raises RequiredError when 'levels' is missing."""
        params = {
            "symbol": "btcusdt",
            "levels": 10,
        }
        params["levels"] = None

        with pytest.raises(RequiredError, match="Missing required parameter 'levels'"):
            await self.ws_streams.partial_book_depth_streams(**params)

    @pytest.mark.asyncio
    async def test_partial_book_depth_streams_server_error(self):
        """Test that partial_book_depth_streams() raises an error when the server returns an error."""

        params = {
            "symbol": "btcusdt",
            "levels": 10,
        }

        mock_error = Exception("ResponseError")
        self.ws_streams.partial_book_depth_streams = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.partial_book_depth_streams(**params)

    @pytest.mark.asyncio
    async def test_ticker24_hour_subscription(self):
        """Test that ticker24_hour() subscribes to the correct WebSocket stream."""

        params = {
            "symbol": "btcusdt",
        }

        expected_response = {
            "e": "24hrTicker",
            "E": 1657706425200,
            "T": 1657706425220,
            "s": "BTC-220930-18000-C",
            "o": "2000",
            "h": "2020",
            "l": "2000",
            "c": "2020",
            "V": "1.42",
            "A": "2841",
            "P": "0.01",
            "p": "20",
            "Q": "0.01",
            "F": "27",
            "L": "48",
            "n": 22,
            "bo": "2012",
            "ao": "2020",
            "bq": "4.9",
            "aq": "0.03",
            "b": "0.1202",
            "a": "0.1318",
            "d": "0.98911",
            "t": "-0.16961",
            "g": "0.00004",
            "v": "2.66584",
            "vo": "0.10001",
            "mp": "2003.5102",
            "hl": "2023.511",
            "ll": "1983.511",
            "eep": "0",
        }
        stream_endpoint = ws_streams_placeholder(
            "/<symbol>@ticker".replace("/", "", 1),
            params,
        )

        def mock_on(event, callback, stream):
            if event == "message" and stream == stream_endpoint:
                callback(expected_response)

        self.websocket_client.on = MagicMock(side_effect=mock_on)

        mock_callback = MagicMock()

        stream = await self.ws_streams.ticker24_hour(**params)
        assert isinstance(stream, RequestStreamHandle)
        assert callable(stream.on)
        assert callable(stream.unsubscribe)
        stream.on("message", mock_callback)
        mock_callback.assert_called_once_with(expected_response)

    @pytest.mark.asyncio
    async def test_ticker24_hour_success(self):
        """Test ticker24_hour() successfully with required parameters only."""

        params = {
            "symbol": "btcusdt",
        }

        expected_response = {
            "e": "24hrTicker",
            "E": 1657706425200,
            "T": 1657706425220,
            "s": "BTC-220930-18000-C",
            "o": "2000",
            "h": "2020",
            "l": "2000",
            "c": "2020",
            "V": "1.42",
            "A": "2841",
            "P": "0.01",
            "p": "20",
            "Q": "0.01",
            "F": "27",
            "L": "48",
            "n": 22,
            "bo": "2012",
            "ao": "2020",
            "bq": "4.9",
            "aq": "0.03",
            "b": "0.1202",
            "a": "0.1318",
            "d": "0.98911",
            "t": "-0.16961",
            "g": "0.00004",
            "v": "2.66584",
            "vo": "0.10001",
            "mp": "2003.5102",
            "hl": "2023.511",
            "ll": "1983.511",
            "eep": "0",
        }
        self.ws_streams.ticker24_hour = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.ticker24_hour(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_ticker24_hour_success_with_optional_params(self):
        """Test ticker24_hour() successfully with optional parameters."""

        params = {"symbol": "btcusdt", "id": 532601580}

        expected_response = {
            "e": "24hrTicker",
            "E": 1657706425200,
            "T": 1657706425220,
            "s": "BTC-220930-18000-C",
            "o": "2000",
            "h": "2020",
            "l": "2000",
            "c": "2020",
            "V": "1.42",
            "A": "2841",
            "P": "0.01",
            "p": "20",
            "Q": "0.01",
            "F": "27",
            "L": "48",
            "n": 22,
            "bo": "2012",
            "ao": "2020",
            "bq": "4.9",
            "aq": "0.03",
            "b": "0.1202",
            "a": "0.1318",
            "d": "0.98911",
            "t": "-0.16961",
            "g": "0.00004",
            "v": "2.66584",
            "vo": "0.10001",
            "mp": "2003.5102",
            "hl": "2023.511",
            "ll": "1983.511",
            "eep": "0",
        }

        self.ws_streams.ticker24_hour = AsyncMock(return_value=expected_response)

        response = await self.ws_streams.ticker24_hour(**params)
        assert response is not None
        assert response == expected_response

    @pytest.mark.asyncio
    async def test_ticker24_hour_missing_required_param_symbol(self):
        """Test that ticker24_hour() raises RequiredError when 'symbol' is missing."""
        params = {
            "symbol": "btcusdt",
        }
        params["symbol"] = None

        with pytest.raises(RequiredError, match="Missing required parameter 'symbol'"):
            await self.ws_streams.ticker24_hour(**params)

    @pytest.mark.asyncio
    async def test_ticker24_hour_server_error(self):
        """Test that ticker24_hour() raises an error when the server returns an error."""

        params = {
            "symbol": "btcusdt",
        }

        mock_error = Exception("ResponseError")
        self.ws_streams.ticker24_hour = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            await self.ws_streams.ticker24_hour(**params)

    @pytest.mark.asyncio
    async def test_ticker24_hour_by_underlying_asset_and_expiration_data_subscription(
        self,
    ):
        """Test that ticker24_hour_by_underlying_asset_and_expiration_data() subscribes to the correct WebSocket stream."""

        params = {
            "underlying_asset": "ETH",
            "expiration_date": "220930",
        }

        expected_response = [
            {
                "e": "24hrTicker",
                "E": 1657706425200,
                "T": 1657706425220,
                "s": "ETH-220930-1600-C",
                "o": "2000",
                "h": "2020",
                "l": "2000",
                "c": "2020",
                "V": "1.42",
                "A": "2841",
                "P": "0.01",
                "p": "20",
                "Q": "0.01",
                "F": "27",
                "L": "48",
                "n": 22,
                "bo": "2012",
                "ao": "2020",
                "bq": "4.9",
                "aq": "0.03",
                "b": "0.1202",
