"""
Binance Derivatives Trading COIN Futures WebSocket Market Streams

OpenAPI Specification for the Binance Derivatives Trading COIN Futures 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_coin_futures.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_aggregate_trade_streams_subscription(self):
        """Test that aggregate_trade_streams() subscribes to the correct WebSocket stream."""

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

        expected_response = {
            "e": "aggTrade",
            "E": 1591261134288,
            "a": 424951,
            "s": "BTCUSD_200626",
            "p": "9643.5",
            "q": "2",
            "f": 606073,
            "l": 606073,
            "T": 1591261134199,
            "m": False,
        }
        stream_endpoint = ws_streams_placeholder(
            "/<symbol>@aggTrade".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.aggregate_trade_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_aggregate_trade_streams_success(self):
        """Test aggregate_trade_streams() successfully with required parameters only."""

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

        expected_response = {
            "e": "aggTrade",
            "E": 1591261134288,
            "a": 424951,
            "s": "BTCUSD_200626",
            "p": "9643.5",
            "q": "2",
            "f": 606073,
            "l": 606073,
            "T": 1591261134199,
            "m": False,
        }
        self.ws_streams.aggregate_trade_streams = AsyncMock(
            return_value=expected_response
        )

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

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

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

        expected_response = {
            "e": "aggTrade",
            "E": 1591261134288,
            "a": 424951,
            "s": "BTCUSD_200626",
            "p": "9643.5",
            "q": "2",
            "f": 606073,
            "l": 606073,
            "T": 1591261134199,
            "m": False,
        }

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

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

    @pytest.mark.asyncio
    async def test_aggregate_trade_streams_missing_required_param_symbol(self):
        """Test that aggregate_trade_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.aggregate_trade_streams(**params)

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

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

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

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

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

        expected_response = {
            "e": "bookTicker",
            "u": 17242169,
            "s": "BTCUSD_200626",
            "ps": "BTCUSD",
            "b": "9548.1",
            "B": "52",
            "a": "9548.5",
            "A": "11",
            "T": 1591268628155,
            "E": 1591268628166,
        }
        stream_endpoint = ws_streams_placeholder(
            "/!bookTicker".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.all_book_tickers_stream()
        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_all_book_tickers_stream_success(self):
        """Test all_book_tickers_stream() successfully with required parameters only."""

        expected_response = {
            "e": "bookTicker",
            "u": 17242169,
            "s": "BTCUSD_200626",
            "ps": "BTCUSD",
            "b": "9548.1",
            "B": "52",
            "a": "9548.5",
            "A": "11",
            "T": 1591268628155,
            "E": 1591268628166,
        }
        self.ws_streams.all_book_tickers_stream = AsyncMock(
            return_value=expected_response
        )

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

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

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "e": "bookTicker",
            "u": 17242169,
            "s": "BTCUSD_200626",
            "ps": "BTCUSD",
            "b": "9548.1",
            "B": "52",
            "a": "9548.5",
            "A": "11",
            "T": 1591268628155,
            "E": 1591268628166,
        }

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

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

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

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

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

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

        expected_response = {
            "e": "forceOrder",
            "E": 1591154240950,
            "o": {
                "s": "BTCUSD_200925",
                "ps": "BTCUSD",
                "S": "SELL",
                "o": "LIMIT",
                "f": "IOC",
                "q": "1",
                "p": "9425.5",
                "ap": "9496.5",
                "X": "FILLED",
                "l": "1",
                "z": "1",
                "T": 1591154240949,
            },
        }
        stream_endpoint = ws_streams_placeholder(
            "/!forceOrder@arr".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.all_market_liquidation_order_streams()
        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_all_market_liquidation_order_streams_success(self):
        """Test all_market_liquidation_order_streams() successfully with required parameters only."""

        expected_response = {
            "e": "forceOrder",
            "E": 1591154240950,
            "o": {
                "s": "BTCUSD_200925",
                "ps": "BTCUSD",
                "S": "SELL",
                "o": "LIMIT",
                "f": "IOC",
                "q": "1",
                "p": "9425.5",
                "ap": "9496.5",
                "X": "FILLED",
                "l": "1",
                "z": "1",
                "T": 1591154240949,
            },
        }
        self.ws_streams.all_market_liquidation_order_streams = AsyncMock(
            return_value=expected_response
        )

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

    @pytest.mark.asyncio
    async def test_all_market_liquidation_order_streams_success_with_optional_params(
        self,
    ):
        """Test all_market_liquidation_order_streams() successfully with optional parameters."""

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "e": "forceOrder",
            "E": 1591154240950,
            "o": {
                "s": "BTCUSD_200925",
                "ps": "BTCUSD",
                "S": "SELL",
                "o": "LIMIT",
                "f": "IOC",
                "q": "1",
                "p": "9425.5",
                "ap": "9496.5",
                "X": "FILLED",
                "l": "1",
                "z": "1",
                "T": 1591154240949,
            },
        }

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

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

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

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

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

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

        expected_response = [
            {
                "e": "24hrMiniTicker",
                "E": 1591267704450,
                "s": "BTCUSD_200626",
                "ps": "BTCUSD",
                "c": "9561.7",
                "o": "9580.9",
                "h": "10000.0",
                "l": "7000.0",
                "v": "487476",
                "q": "33264343847.22378500",
            }
        ]
        stream_endpoint = ws_streams_placeholder(
            "/!miniTicker@arr".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.all_market_mini_tickers_stream()
        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_all_market_mini_tickers_stream_success(self):
        """Test all_market_mini_tickers_stream() successfully with required parameters only."""

        expected_response = [
            {
                "e": "24hrMiniTicker",
                "E": 1591267704450,
                "s": "BTCUSD_200626",
                "ps": "BTCUSD",
                "c": "9561.7",
                "o": "9580.9",
                "h": "10000.0",
                "l": "7000.0",
                "v": "487476",
                "q": "33264343847.22378500",
            }
        ]
        self.ws_streams.all_market_mini_tickers_stream = AsyncMock(
            return_value=expected_response
        )

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

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

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = [
            {
                "e": "24hrMiniTicker",
                "E": 1591267704450,
                "s": "BTCUSD_200626",
                "ps": "BTCUSD",
                "c": "9561.7",
                "o": "9580.9",
                "h": "10000.0",
                "l": "7000.0",
                "v": "487476",
                "q": "33264343847.22378500",
            }
        ]

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

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

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

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

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

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

        expected_response = [
            {
                "e": "24hrTicker",
                "E": 1591268262453,
                "s": "BTCUSD_200626",
                "ps": "BTCUSD",
                "p": "-43.4",
                "P": "-0.452",
                "w": "0.00147974",
                "c": "9548.5",
                "Q": "2",
                "o": "9591.9",
                "h": "10000.0",
                "l": "7000.0",
                "v": "487850",
                "q": "32968676323.46222700",
                "O": 1591181820000,
                "C": 1591268262442,
                "F": 512014,
                "L": 615289,
                "n": 103272,
            }
        ]
        stream_endpoint = ws_streams_placeholder(
            "/!ticker@arr".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.all_market_tickers_streams()
        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_all_market_tickers_streams_success(self):
        """Test all_market_tickers_streams() successfully with required parameters only."""

        expected_response = [
            {
                "e": "24hrTicker",
                "E": 1591268262453,
                "s": "BTCUSD_200626",
                "ps": "BTCUSD",
                "p": "-43.4",
                "P": "-0.452",
                "w": "0.00147974",
                "c": "9548.5",
                "Q": "2",
                "o": "9591.9",
                "h": "10000.0",
                "l": "7000.0",
                "v": "487850",
                "q": "32968676323.46222700",
                "O": 1591181820000,
                "C": 1591268262442,
                "F": 512014,
                "L": 615289,
                "n": 103272,
            }
        ]
        self.ws_streams.all_market_tickers_streams = AsyncMock(
            return_value=expected_response
        )

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

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

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = [
            {
                "e": "24hrTicker",
                "E": 1591268262453,
                "s": "BTCUSD_200626",
                "ps": "BTCUSD",
                "p": "-43.4",
                "P": "-0.452",
                "w": "0.00147974",
                "c": "9548.5",
                "Q": "2",
                "o": "9591.9",
                "h": "10000.0",
                "l": "7000.0",
                "v": "487850",
                "q": "32968676323.46222700",
                "O": 1591181820000,
                "C": 1591268262442,
                "F": 512014,
                "L": 615289,
                "n": 103272,
            }
        ]

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

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

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

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

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

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

        params = {
            "pair": "btcusdt",
            "contract_type": "next_quarter",
            "interval": "1m",
        }

        expected_response = {
            "e": "continuous_kline",
            "E": 1591261542539,
            "ps": "BTCUSD",
            "ct": "NEXT_QUARTER",
            "k": {
                "t": 1591261500000,
                "T": 1591261559999,
                "i": "1m",
                "f": 606400,
                "L": 606430,
                "o": "9638.9",
                "c": "9639.8",
                "h": "9639.8",
                "l": "9638.6",
                "v": "156",
                "n": 31,
                "x": False,
                "q": "1.61836886",
                "V": "73",
                "Q": "0.75731156",
                "B": "0",
            },
        }
        stream_endpoint = ws_streams_placeholder(
            "/<pair>_<contractType>@continuousKline_<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.continuous_contract_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_continuous_contract_kline_candlestick_streams_success(self):
        """Test continuous_contract_kline_candlestick_streams() successfully with required parameters only."""

        params = {
            "pair": "btcusdt",
            "contract_type": "next_quarter",
            "interval": "1m",
        }

        expected_response = {
            "e": "continuous_kline",
            "E": 1591261542539,
            "ps": "BTCUSD",
            "ct": "NEXT_QUARTER",
            "k": {
                "t": 1591261500000,
                "T": 1591261559999,
                "i": "1m",
                "f": 606400,
                "L": 606430,
                "o": "9638.9",
                "c": "9639.8",
                "h": "9639.8",
                "l": "9638.6",
                "v": "156",
                "n": 31,
                "x": False,
                "q": "1.61836886",
                "V": "73",
                "Q": "0.75731156",
                "B": "0",
            },
        }
        self.ws_streams.continuous_contract_kline_candlestick_streams = AsyncMock(
            return_value=expected_response
        )

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

    @pytest.mark.asyncio
    async def test_continuous_contract_kline_candlestick_streams_success_with_optional_params(
        self,
    ):
        """Test continuous_contract_kline_candlestick_streams() successfully with optional parameters."""

        params = {
            "pair": "btcusdt",
            "contract_type": "next_quarter",
            "interval": "1m",
            "id": "e9d6b4349871b40611412680b3445fac",
        }

        expected_response = {
            "e": "continuous_kline",
            "E": 1591261542539,
            "ps": "BTCUSD",
            "ct": "NEXT_QUARTER",
            "k": {
                "t": 1591261500000,
                "T": 1591261559999,
                "i": "1m",
                "f": 606400,
                "L": 606430,
                "o": "9638.9",
                "c": "9639.8",
                "h": "9639.8",
                "l": "9638.6",
                "v": "156",
                "n": 31,
                "x": False,
                "q": "1.61836886",
                "V": "73",
                "Q": "0.75731156",
                "B": "0",
            },
        }

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

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

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

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

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

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

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

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

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

        params = {
            "pair": "btcusdt",
            "contract_type": "next_quarter",
            "interval": "1m",
        }

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

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

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

        expected_response = {
            "e": "contractInfo",
            "E": 1669647330375,
            "s": "APTUSD_PERP",
            "ps": "APTUSD",
            "ct": "PERPETUAL",
            "dt": 4133404800000,
            "ot": 1666594800000,
            "cs": "TRADING",
            "bks": [
                {
                    "bs": 1,
                    "bnf": 0,
                    "bnc": 5000,
                    "mmr": 0.01,
                    "cf": 0,
                    "mi": 21,
                    "ma": 50,
                },
                {
                    "bs": 2,
                    "bnf": 5000,
                    "bnc": 25000,
                    "mmr": 0.025,
                    "cf": 75,
                    "mi": 11,
                    "ma": 20,
                },
            ],
        }
        stream_endpoint = ws_streams_placeholder(
            "/!contractInfo".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.contract_info_stream()
        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_contract_info_stream_success(self):
        """Test contract_info_stream() successfully with required parameters only."""

        expected_response = {
            "e": "contractInfo",
            "E": 1669647330375,
            "s": "APTUSD_PERP",
            "ps": "APTUSD",
            "ct": "PERPETUAL",
            "dt": 4133404800000,
            "ot": 1666594800000,
            "cs": "TRADING",
            "bks": [
                {
                    "bs": 1,
                    "bnf": 0,
                    "bnc": 5000,
                    "mmr": 0.01,
                    "cf": 0,
                    "mi": 21,
                    "ma": 50,
                },
                {
                    "bs": 2,
                    "bnf": 5000,
                    "bnc": 25000,
                    "mmr": 0.025,
                    "cf": 75,
                    "mi": 11,
                    "ma": 20,
                },
            ],
        }
        self.ws_streams.contract_info_stream = AsyncMock(return_value=expected_response)

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

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

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "e": "contractInfo",
            "E": 1669647330375,
            "s": "APTUSD_PERP",
            "ps": "APTUSD",
            "ct": "PERPETUAL",
            "dt": 4133404800000,
            "ot": 1666594800000,
            "cs": "TRADING",
            "bks": [
                {
                    "bs": 1,
                    "bnf": 0,
                    "bnc": 5000,
                    "mmr": 0.01,
                    "cf": 0,
                    "mi": 21,
                    "ma": 50,
                },
                {
                    "bs": 2,
                    "bnf": 5000,
                    "bnc": 25000,
                    "mmr": 0.025,
                    "cf": 75,
                    "mi": 11,
                    "ma": 20,
                },
            ],
        }

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

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

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

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

        with pytest.raises(Exception, match="ResponseError"):
