"""
Binance Derivatives Trading Portfolio Margin REST API

OpenAPI Specification for the Binance Derivatives Trading Portfolio Margin 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, patch
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_portfolio_margin.rest_api.api import AccountApi
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    AccountBalanceResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    AccountInformationResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    BnbTransferResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    ChangeAutoRepayFuturesStatusResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    ChangeCmInitialLeverageResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    ChangeCmPositionModeResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    ChangeUmInitialLeverageResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    ChangeUmPositionModeResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    CmNotionalAndLeverageBracketsResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    FundAutoCollectionResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    FundCollectionByAssetResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetAutoRepayFuturesStatusResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetCmAccountDetailResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetCmCurrentPositionModeResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetCmIncomeHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetDownloadIdForUmFuturesOrderHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetDownloadIdForUmFuturesTradeHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetDownloadIdForUmFuturesTransactionHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetMarginBorrowLoanInterestHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmAccountDetailResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmAccountDetailV2Response,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmCurrentPositionModeResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmFuturesOrderDownloadLinkByIdResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmFuturesTradeDownloadLinkByIdResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmFuturesTransactionDownloadLinkByIdResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUmIncomeHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUserCommissionRateForCmResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    GetUserCommissionRateForUmResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    MarginMaxBorrowResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    PortfolioMarginUmTradingQuantitativeRulesIndicatorsResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryCmPositionInformationResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryMarginLoanRecordResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryMarginMaxWithdrawResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryMarginRepayRecordResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryPortfolioMarginNegativeBalanceInterestHistoryResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryUmPositionInformationResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryUserNegativeBalanceAutoExchangeRecordResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    QueryUserRateLimitResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    RepayFuturesNegativeBalanceResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    UmFuturesAccountConfigurationResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    UmFuturesSymbolConfigurationResponse,
)
from binance_sdk_derivatives_trading_portfolio_margin.rest_api.models import (
    UmNotionalAndLeverageBracketsResponse,
)


class TestAccountApi:
    @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 = AccountApi(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

    @patch("binance_common.utils.get_signature")
    def test_account_balance_success(self, mock_get_signature):
        """Test account_balance() successfully with required parameters only."""

        expected_response = [
            {
                "asset": "USDT",
                "totalWalletBalance": "122607.35137903",
                "crossMarginAsset": "92.27530794",
                "crossMarginBorrowed": "10.00000000",
                "crossMarginFree": "100.00000000",
                "crossMarginInterest": "0.72469206",
                "crossMarginLocked": "3.00000000",
                "umWalletBalance": "0.00000000",
                "umUnrealizedPNL": "23.72469206",
                "cmWalletBalance": "23.72469206",
                "cmUnrealizedPNL": "",
                "updateTime": 1617939110373,
                "negativeBalance": "0",
            }
        ]
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.account_balance()

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

        self.mock_session.request.assert_called_once()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/balance" 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(AccountBalanceResponse)

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

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_account_balance_success_with_optional_params(self, mock_get_signature):
        """Test account_balance() successfully with optional parameters."""

        params = {"asset": "asset_example", "recv_window": 5000}

        expected_response = [
            {
                "asset": "USDT",
                "totalWalletBalance": "122607.35137903",
                "crossMarginAsset": "92.27530794",
                "crossMarginBorrowed": "10.00000000",
                "crossMarginFree": "100.00000000",
                "crossMarginInterest": "0.72469206",
                "crossMarginLocked": "3.00000000",
                "umWalletBalance": "0.00000000",
                "umUnrealizedPNL": "23.72469206",
                "cmWalletBalance": "23.72469206",
                "cmUnrealizedPNL": "",
                "updateTime": 1617939110373,
                "negativeBalance": "0",
            }
        ]
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

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

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

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/balance" 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(AccountBalanceResponse)

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

        assert response.data() == expected

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

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

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

    @patch("binance_common.utils.get_signature")
    def test_account_information_success(self, mock_get_signature):
        """Test account_information() successfully with required parameters only."""

        expected_response = {
            "uniMMR": "5167.92171923",
            "accountEquity": "122607.35137903",
            "actualEquity": "73.47428058",
            "accountInitialMargin": "23.72469206",
            "accountMaintMargin": "23.72469206",
            "accountStatus": "NORMAL",
            "virtualMaxWithdrawAmount": "1627523.32459208",
            "totalAvailableBalance": "",
            "totalMarginOpenLoss": "",
            "updateTime": 1657707212154,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.account_information()

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

        self.mock_session.request.assert_called_once()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/account" 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(AccountInformationResponse)

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

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_account_information_success_with_optional_params(self, mock_get_signature):
        """Test account_information() successfully with optional parameters."""

        params = {"recv_window": 5000}

        expected_response = {
            "uniMMR": "5167.92171923",
            "accountEquity": "122607.35137903",
            "actualEquity": "73.47428058",
            "accountInitialMargin": "23.72469206",
            "accountMaintMargin": "23.72469206",
            "accountStatus": "NORMAL",
            "virtualMaxWithdrawAmount": "1627523.32459208",
            "totalAvailableBalance": "",
            "totalMarginOpenLoss": "",
            "updateTime": 1657707212154,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

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

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

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/account" 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(AccountInformationResponse)

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

        assert response.data() == expected

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

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

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

    @patch("binance_common.utils.get_signature")
    def test_bnb_transfer_success(self, mock_get_signature):
        """Test bnb_transfer() successfully with required parameters only."""

        params = {
            "amount": 1.0,
            "transfer_side": "transfer_side_example",
        }

        expected_response = {"tranId": 100000001}
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.bnb_transfer(**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()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/bnb-transfer" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"
        assert normalized["amount"] == 1.0
        assert normalized["transferSide"] == "transfer_side_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(BnbTransferResponse)

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

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_bnb_transfer_success_with_optional_params(self, mock_get_signature):
        """Test bnb_transfer() successfully with optional parameters."""

        params = {
            "amount": 1.0,
            "transfer_side": "transfer_side_example",
            "recv_window": 5000,
        }

        expected_response = {"tranId": 100000001}
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

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

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

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/bnb-transfer" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"

        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(BnbTransferResponse)

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

        assert response.data() == expected

    def test_bnb_transfer_missing_required_param_amount(self):
        """Test that bnb_transfer() raises RequiredError when 'amount' is missing."""
        params = {
            "amount": 1.0,
            "transfer_side": "transfer_side_example",
        }
        params["amount"] = None

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

    def test_bnb_transfer_missing_required_param_transfer_side(self):
        """Test that bnb_transfer() raises RequiredError when 'transfer_side' is missing."""
        params = {
            "amount": 1.0,
            "transfer_side": "transfer_side_example",
        }
        params["transfer_side"] = None

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

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

        params = {
            "amount": 1.0,
            "transfer_side": "transfer_side_example",
        }

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

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

    @patch("binance_common.utils.get_signature")
    def test_change_auto_repay_futures_status_success(self, mock_get_signature):
        """Test change_auto_repay_futures_status() successfully with required parameters only."""

        params = {
            "auto_repay": "True",
        }

        expected_response = {"msg": "success"}
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.change_auto_repay_futures_status(**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()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/repay-futures-switch" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"
        assert normalized["autoRepay"] == "True"

        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(ChangeAutoRepayFuturesStatusResponse)

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

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_change_auto_repay_futures_status_success_with_optional_params(
        self, mock_get_signature
    ):
        """Test change_auto_repay_futures_status() successfully with optional parameters."""

        params = {"auto_repay": "True", "recv_window": 5000}

        expected_response = {"msg": "success"}
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

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

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

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/repay-futures-switch" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"

        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(ChangeAutoRepayFuturesStatusResponse)

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

        assert response.data() == expected

    def test_change_auto_repay_futures_status_missing_required_param_auto_repay(self):
        """Test that change_auto_repay_futures_status() raises RequiredError when 'auto_repay' is missing."""
        params = {
            "auto_repay": "True",
        }
        params["auto_repay"] = None

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

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

        params = {
            "auto_repay": "True",
        }

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

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

    @patch("binance_common.utils.get_signature")
    def test_change_cm_initial_leverage_success(self, mock_get_signature):
        """Test change_cm_initial_leverage() successfully with required parameters only."""

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

        expected_response = {
            "leverage": 21,
            "maxQty": "1000",
            "symbol": "BTCUSD_200925",
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.change_cm_initial_leverage(**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()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/cm/leverage" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"
        assert normalized["symbol"] == "symbol_example"
        assert normalized["leverage"] == 56

        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(ChangeCmInitialLeverageResponse)

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

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_change_cm_initial_leverage_success_with_optional_params(
        self, mock_get_signature
    ):
        """Test change_cm_initial_leverage() successfully with optional parameters."""

        params = {"symbol": "symbol_example", "leverage": 56, "recv_window": 5000}

        expected_response = {
            "leverage": 21,
            "maxQty": "1000",
            "symbol": "BTCUSD_200925",
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

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

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

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/cm/leverage" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"

        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(ChangeCmInitialLeverageResponse)

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

        assert response.data() == expected

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

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

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

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

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

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

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

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

    @patch("binance_common.utils.get_signature")
    def test_change_cm_position_mode_success(self, mock_get_signature):
        """Test change_cm_position_mode() successfully with required parameters only."""

        params = {
            "dual_side_position": "dual_side_position_example",
        }

        expected_response = {"code": 200, "msg": "success"}
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.change_cm_position_mode(**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()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/papi/v1/cm/positionSide/dual" in request_kwargs["url"]
        assert request_kwargs["method"] == "POST"
        assert normalized["dualSidePosition"] == "dual_side_position_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(ChangeCmPositionModeResponse)

        if is_list and not is_flat_list:
            expected = expected_response
