"""
Binance Derivatives Trading COIN Futures REST API

OpenAPI Specification for the Binance Derivatives Trading COIN Futures REST API
The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
"""

import json
import pytest
import requests

from unittest.mock import MagicMock
from urllib.parse import parse_qs

from binance_common.configuration import ConfigurationRestAPI
from binance_common.errors import RequiredError
from binance_common.utils import normalize_query_values, is_one_of_model, snake_to_camel

from binance_sdk_derivatives_trading_coin_futures.rest_api.api import MarketDataApi
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import BasisResponse
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    CheckServerTimeResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    CompressedAggregateTradesListResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    ContinuousContractKlineCandlestickDataResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    ExchangeInformationResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    GetFundingRateHistoryOfPerpetualFuturesResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    GetFundingRateInfoResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    IndexPriceAndMarkPriceResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    IndexPriceKlineCandlestickDataResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    KlineCandlestickDataResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    LongShortRatioResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    MarkPriceKlineCandlestickDataResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    OldTradesLookupResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    OpenInterestResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    OpenInterestStatisticsResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    OrderBookResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    PremiumIndexKlineDataResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    QueryIndexPriceConstituentsResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    RecentTradesListResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    SymbolOrderBookTickerResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    SymbolPriceTickerResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TakerBuySellVolumeResponse,
)

from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    Ticker24hrPriceChangeStatisticsResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TopTraderLongShortRatioAccountsResponse,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TopTraderLongShortRatioPositionsResponse,
)


from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    BasisContractTypeEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import BasisPeriodEnum
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    ContinuousContractKlineCandlestickDataContractTypeEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    ContinuousContractKlineCandlestickDataIntervalEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    IndexPriceKlineCandlestickDataIntervalEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    KlineCandlestickDataIntervalEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    LongShortRatioPeriodEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    MarkPriceKlineCandlestickDataIntervalEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    OpenInterestStatisticsContractTypeEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    OpenInterestStatisticsPeriodEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    PremiumIndexKlineDataIntervalEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TakerBuySellVolumeContractTypeEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TakerBuySellVolumePeriodEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TopTraderLongShortRatioAccountsPeriodEnum,
)
from binance_sdk_derivatives_trading_coin_futures.rest_api.models import (
    TopTraderLongShortRatioPositionsPeriodEnum,
)


class TestMarketDataApi:
    @pytest.fixture(autouse=True)
    def setup_client(self):
        """Setup a client instance with mocked session before each test method."""
        self.mock_session = MagicMock(spec=requests.Session)
        config = ConfigurationRestAPI(
            api_key="test-api-key",
            api_secret="test-api-secret",
        )
        self.client = MarketDataApi(configuration=config, session=self.mock_session)

    def set_mock_response(self, data: dict = {}, status_code=200, headers=None):
        """Helper method to setup mock response for the client's session request."""
        if headers is None:
            headers = {}

        mock_response = MagicMock()
        mock_response.status_code = status_code
        mock_response.json.return_value = data
        mock_response.text = json.dumps(data)
        mock_response.headers = headers

        self.mock_session.request.return_value = mock_response

    def test_basis_success(self):
        """Test basis() successfully with required parameters only."""

        params = {
            "pair": "pair_example",
            "contract_type": BasisContractTypeEnum["PERPETUAL"].value,
            "period": BasisPeriodEnum["PERIOD_5m"].value,
        }

        expected_response = [
            {
                "indexPrice": "29269.93972727",
                "contractType": "CURRENT_QUARTER",
                "basisRate": "0.0024",
                "futuresPrice": "29341.3",
                "annualizedBasisRate": "0.0283",
                "basis": "71.36027273",
                "pair": "BTCUSD",
                "timestamp": 1653381600000,
            }
        ]

        self.set_mock_response(expected_response)

        response = self.client.basis(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs
        parsed_params = parse_qs(request_kwargs["params"])
        camel_case_params = {snake_to_camel(k): v for k, v in params.items()}
        normalized = normalize_query_values(parsed_params, camel_case_params)

        self.mock_session.request.assert_called_once()

        assert "url" in request_kwargs
        assert "/futures/data/basis" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"
        assert normalized["pair"] == "pair_example"
        assert normalized["contractType"] == BasisContractTypeEnum["PERPETUAL"].value
        assert normalized["period"] == BasisPeriodEnum["PERIOD_5m"].value

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(BasisResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif is_oneof or is_list or hasattr(BasisResponse, "from_dict"):
            expected = BasisResponse.from_dict(expected_response)
        else:
            expected = BasisResponse.model_validate_json(json.dumps(expected_response))

        assert response.data() == expected

    def test_basis_success_with_optional_params(self):
        """Test basis() successfully with optional parameters."""

        params = {
            "pair": "pair_example",
            "contract_type": BasisContractTypeEnum["PERPETUAL"].value,
            "period": BasisPeriodEnum["PERIOD_5m"].value,
            "limit": 100,
            "start_time": 1623319461670,
            "end_time": 1641782889000,
        }

        expected_response = [
            {
                "indexPrice": "29269.93972727",
                "contractType": "CURRENT_QUARTER",
                "basisRate": "0.0024",
                "futuresPrice": "29341.3",
                "annualizedBasisRate": "0.0283",
                "basis": "71.36027273",
                "pair": "BTCUSD",
                "timestamp": 1653381600000,
            }
        ]

        self.set_mock_response(expected_response)

        response = self.client.basis(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        assert "url" in request_kwargs
        assert "/futures/data/basis" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        self.mock_session.request.assert_called_once()
        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(BasisResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif is_oneof or is_list or hasattr(BasisResponse, "from_dict"):
            expected = BasisResponse.from_dict(expected_response)
        else:
            expected = BasisResponse.model_validate_json(json.dumps(expected_response))

        assert response.data() == expected

    def test_basis_missing_required_param_pair(self):
        """Test that basis() raises RequiredError when 'pair' is missing."""
        params = {
            "pair": "pair_example",
            "contract_type": BasisContractTypeEnum["PERPETUAL"].value,
            "period": BasisPeriodEnum["PERIOD_5m"].value,
        }
        params["pair"] = None

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

    def test_basis_missing_required_param_contract_type(self):
        """Test that basis() raises RequiredError when 'contract_type' is missing."""
        params = {
            "pair": "pair_example",
            "contract_type": BasisContractTypeEnum["PERPETUAL"].value,
            "period": BasisPeriodEnum["PERIOD_5m"].value,
        }
        params["contract_type"] = None

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

    def test_basis_missing_required_param_period(self):
        """Test that basis() raises RequiredError when 'period' is missing."""
        params = {
            "pair": "pair_example",
            "contract_type": BasisContractTypeEnum["PERPETUAL"].value,
            "period": BasisPeriodEnum["PERIOD_5m"].value,
        }
        params["period"] = None

        with pytest.raises(RequiredError, match="Missing required parameter 'period'"):
            self.client.basis(**params)

    def test_basis_server_error(self):
        """Test that basis() raises an error when the server returns an error."""

        params = {
            "pair": "pair_example",
            "contract_type": BasisContractTypeEnum["PERPETUAL"].value,
            "period": BasisPeriodEnum["PERIOD_5m"].value,
        }

        mock_error = Exception("ResponseError")
        self.client.basis = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            self.client.basis(**params)

    def test_check_server_time_success(self):
        """Test check_server_time() successfully with required parameters only."""

        expected_response = {"serverTime": 1499827319559}

        self.set_mock_response(expected_response)

        response = self.client.check_server_time()

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        self.mock_session.request.assert_called_once()

        assert "url" in request_kwargs
        assert "/dapi/v1/time" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(CheckServerTimeResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif is_oneof or is_list or hasattr(CheckServerTimeResponse, "from_dict"):
            expected = CheckServerTimeResponse.from_dict(expected_response)
        else:
            expected = CheckServerTimeResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_check_server_time_server_error(self):
        """Test that check_server_time() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.client.check_server_time = MagicMock(side_effect=mock_error)

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

    def test_compressed_aggregate_trades_list_success(self):
        """Test compressed_aggregate_trades_list() successfully with required parameters only."""

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

        expected_response = [
            {
                "a": 416690,
                "p": "9642.4",
                "q": "3",
                "f": 595259,
                "l": 595259,
                "T": 1591250548649,
                "m": False,
            }
        ]

        self.set_mock_response(expected_response)

        response = self.client.compressed_aggregate_trades_list(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs
        parsed_params = parse_qs(request_kwargs["params"])
        camel_case_params = {snake_to_camel(k): v for k, v in params.items()}
        normalized = normalize_query_values(parsed_params, camel_case_params)

        self.mock_session.request.assert_called_once()

        assert "url" in request_kwargs
        assert "/dapi/v1/aggTrades" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"
        assert normalized["symbol"] == "symbol_example"

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(CompressedAggregateTradesListResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof
            or is_list
            or hasattr(CompressedAggregateTradesListResponse, "from_dict")
        ):
            expected = CompressedAggregateTradesListResponse.from_dict(
                expected_response
            )
        else:
            expected = CompressedAggregateTradesListResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_compressed_aggregate_trades_list_success_with_optional_params(self):
        """Test compressed_aggregate_trades_list() successfully with optional parameters."""

        params = {
            "symbol": "symbol_example",
            "from_id": 1,
            "start_time": 1623319461670,
            "end_time": 1641782889000,
            "limit": 100,
        }

        expected_response = [
            {
                "a": 416690,
                "p": "9642.4",
                "q": "3",
                "f": 595259,
                "l": 595259,
                "T": 1591250548649,
                "m": False,
            }
        ]

        self.set_mock_response(expected_response)

        response = self.client.compressed_aggregate_trades_list(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        assert "url" in request_kwargs
        assert "/dapi/v1/aggTrades" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        self.mock_session.request.assert_called_once()
        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(CompressedAggregateTradesListResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof
            or is_list
            or hasattr(CompressedAggregateTradesListResponse, "from_dict")
        ):
            expected = CompressedAggregateTradesListResponse.from_dict(
                expected_response
            )
        else:
            expected = CompressedAggregateTradesListResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_compressed_aggregate_trades_list_missing_required_param_symbol(self):
        """Test that compressed_aggregate_trades_list() raises RequiredError when 'symbol' is missing."""
        params = {
            "symbol": "symbol_example",
        }
        params["symbol"] = None

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

    def test_compressed_aggregate_trades_list_server_error(self):
        """Test that compressed_aggregate_trades_list() raises an error when the server returns an error."""

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

        mock_error = Exception("ResponseError")
        self.client.compressed_aggregate_trades_list = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            self.client.compressed_aggregate_trades_list(**params)

    def test_continuous_contract_kline_candlestick_data_success(self):
        """Test continuous_contract_kline_candlestick_data() successfully with required parameters only."""

        params = {
            "pair": "pair_example",
            "contract_type": ContinuousContractKlineCandlestickDataContractTypeEnum[
                "PERPETUAL"
            ].value,
            "interval": ContinuousContractKlineCandlestickDataIntervalEnum[
                "INTERVAL_1m"
            ].value,
        }

        expected_response = [
            [
                1591258320000,
                "9640.7",
                "9642.4",
                "9640.6",
                "9642.0",
                "206",
                1591258379999,
                "2.13660389",
                48,
                "119",
                "1.23424865",
                "0",
            ]
        ]

        self.set_mock_response(expected_response)

        response = self.client.continuous_contract_kline_candlestick_data(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs
        parsed_params = parse_qs(request_kwargs["params"])
        camel_case_params = {snake_to_camel(k): v for k, v in params.items()}
        normalized = normalize_query_values(parsed_params, camel_case_params)

        self.mock_session.request.assert_called_once()

        assert "url" in request_kwargs
        assert "/dapi/v1/continuousKlines" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"
        assert normalized["pair"] == "pair_example"
        assert (
            normalized["contractType"]
            == ContinuousContractKlineCandlestickDataContractTypeEnum["PERPETUAL"].value
        )
        assert (
            normalized["interval"]
            == ContinuousContractKlineCandlestickDataIntervalEnum["INTERVAL_1m"].value
        )

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(ContinuousContractKlineCandlestickDataResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof
            or is_list
            or hasattr(ContinuousContractKlineCandlestickDataResponse, "from_dict")
        ):
            expected = ContinuousContractKlineCandlestickDataResponse.from_dict(
                expected_response
            )
        else:
            expected = (
                ContinuousContractKlineCandlestickDataResponse.model_validate_json(
                    json.dumps(expected_response)
                )
            )

        assert response.data() == expected

    def test_continuous_contract_kline_candlestick_data_success_with_optional_params(
        self,
    ):
        """Test continuous_contract_kline_candlestick_data() successfully with optional parameters."""

        params = {
            "pair": "pair_example",
            "contract_type": ContinuousContractKlineCandlestickDataContractTypeEnum[
                "PERPETUAL"
            ].value,
            "interval": ContinuousContractKlineCandlestickDataIntervalEnum[
                "INTERVAL_1m"
            ].value,
            "start_time": 1623319461670,
            "end_time": 1641782889000,
            "limit": 100,
        }

        expected_response = [
            [
                1591258320000,
                "9640.7",
                "9642.4",
                "9640.6",
                "9642.0",
                "206",
                1591258379999,
                "2.13660389",
                48,
                "119",
                "1.23424865",
                "0",
            ]
        ]

        self.set_mock_response(expected_response)

        response = self.client.continuous_contract_kline_candlestick_data(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        assert "url" in request_kwargs
        assert "/dapi/v1/continuousKlines" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        self.mock_session.request.assert_called_once()
        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(ContinuousContractKlineCandlestickDataResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof
            or is_list
            or hasattr(ContinuousContractKlineCandlestickDataResponse, "from_dict")
        ):
            expected = ContinuousContractKlineCandlestickDataResponse.from_dict(
                expected_response
            )
        else:
            expected = (
                ContinuousContractKlineCandlestickDataResponse.model_validate_json(
                    json.dumps(expected_response)
                )
            )

        assert response.data() == expected

    def test_continuous_contract_kline_candlestick_data_missing_required_param_pair(
        self,
    ):
        """Test that continuous_contract_kline_candlestick_data() raises RequiredError when 'pair' is missing."""
        params = {
            "pair": "pair_example",
            "contract_type": ContinuousContractKlineCandlestickDataContractTypeEnum[
                "PERPETUAL"
            ].value,
            "interval": ContinuousContractKlineCandlestickDataIntervalEnum[
                "INTERVAL_1m"
            ].value,
        }
        params["pair"] = None

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

    def test_continuous_contract_kline_candlestick_data_missing_required_param_contract_type(
        self,
    ):
        """Test that continuous_contract_kline_candlestick_data() raises RequiredError when 'contract_type' is missing."""
        params = {
            "pair": "pair_example",
            "contract_type": ContinuousContractKlineCandlestickDataContractTypeEnum[
                "PERPETUAL"
            ].value,
            "interval": ContinuousContractKlineCandlestickDataIntervalEnum[
                "INTERVAL_1m"
            ].value,
        }
        params["contract_type"] = None

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

    def test_continuous_contract_kline_candlestick_data_missing_required_param_interval(
        self,
    ):
        """Test that continuous_contract_kline_candlestick_data() raises RequiredError when 'interval' is missing."""
        params = {
            "pair": "pair_example",
            "contract_type": ContinuousContractKlineCandlestickDataContractTypeEnum[
                "PERPETUAL"
            ].value,
            "interval": ContinuousContractKlineCandlestickDataIntervalEnum[
                "INTERVAL_1m"
            ].value,
        }
        params["interval"] = None

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

    def test_continuous_contract_kline_candlestick_data_server_error(self):
        """Test that continuous_contract_kline_candlestick_data() raises an error when the server returns an error."""

        params = {
            "pair": "pair_example",
            "contract_type": ContinuousContractKlineCandlestickDataContractTypeEnum[
                "PERPETUAL"
            ].value,
            "interval": ContinuousContractKlineCandlestickDataIntervalEnum[
                "INTERVAL_1m"
            ].value,
        }

        mock_error = Exception("ResponseError")
        self.client.continuous_contract_kline_candlestick_data = MagicMock(
            side_effect=mock_error
        )

        with pytest.raises(Exception, match="ResponseError"):
            self.client.continuous_contract_kline_candlestick_data(**params)

    def test_exchange_information_success(self):
        """Test exchange_information() successfully with required parameters only."""

        expected_response = {
            "exchangeFilters": [],
            "rateLimits": [
                {
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 6000,
                    "rateLimitType": "REQUEST_WEIGHT",
                },
                {
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 6000,
                    "rateLimitType": "ORDERS",
                },
            ],
            "serverTime": 1565613908500,
            "symbols": [
                {
                    "filters": [
                        {
                            "filterType": "PRICE_FILTER",
                            "maxPrice": "100000",
                            "minPrice": "0.1",
                            "tickSize": "0.1",
                        },
                        {
                            "filterType": "LOT_SIZE",
                            "maxQty": "100000",
                            "minQty": "1",
                            "stepSize": "1",
                        },
                        {
                            "filterType": "MARKET_LOT_SIZE",
                            "maxQty": "100000",
                            "minQty": "1",
                            "stepSize": "1",
                        },
                        {"filterType": "MAX_NUM_ORDERS", "limit": 200},
                        {
                            "filterType": "PERCENT_PRICE",
                            "multiplierUp": "1.0500",
                            "multiplierDown": "0.9500",
                            "multiplierDecimal": "4",
                        },
                    ],
                    "OrderType": [
                        "LIMIT",
                        "MARKET",
                        "STOP",
                        "TAKE_PROFIT",
                        "TRAILING_STOP_MARKET",
                    ],
                    "timeInForce": ["GTC", "IOC", "FOK", "GTX"],
                    "liquidationFee": "0.010000",
                    "marketTakeBound": "0.30",
                    "symbol": "BTCUSD_200925",
                    "pair": "BTCUSD",
                    "contractType": "CURRENT_QUARTER",
                    "deliveryDate": 1601020800000,
                    "onboardDate": 1590739200000,
                    "contractStatus": "TRADING",
                    "contractSize": 100,
                    "quoteAsset": "USD",
                    "baseAsset": "BTC",
                    "marginAsset": "BTC",
                    "pricePrecision": 1,
                    "quantityPrecision": 0,
                    "baseAssetPrecision": 8,
                    "quotePrecision": 8,
                    "equalQtyPrecision": 4,
                    "triggerProtect": "0.0500",
                    "maintMarginPercent": "2.5000",
                    "requiredMarginPercent": "5.0000",
                    "underlyingType": "COIN",
                    "underlyingSubType": [],
                }
            ],
            "timezone": "UTC",
        }

        self.set_mock_response(expected_response)

        response = self.client.exchange_information()

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        self.mock_session.request.assert_called_once()

        assert "url" in request_kwargs
        assert "/dapi/v1/exchangeInfo" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(ExchangeInformationResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif is_oneof or is_list or hasattr(ExchangeInformationResponse, "from_dict"):
            expected = ExchangeInformationResponse.from_dict(expected_response)
        else:
            expected = ExchangeInformationResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_exchange_information_server_error(self):
        """Test that exchange_information() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.client.exchange_information = MagicMock(side_effect=mock_error)

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

    def test_get_funding_rate_history_of_perpetual_futures_success(self):
        """Test get_funding_rate_history_of_perpetual_futures() successfully with required parameters only."""

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

        expected_response = [
            {
                "symbol": "BTCUSD_PERP",
                "fundingTime": 1596038400000,
                "fundingRate": "-0.00300000",
            },
            {
                "symbol": "BTCUSD_PERP",
                "fundingTime": 1596067200000,
