/*
 * Binance Mining REST API
 *
 * OpenAPI Specification for the Binance Mining REST API
 *
 * The version of the OpenAPI document: 1.0.0
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#![allow(unused_imports)]
use async_trait::async_trait;
use derive_builder::Builder;
use reqwest;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::collections::BTreeMap;

use crate::common::{
    config::ConfigurationRestApi,
    models::{ParamBuildError, RestApiResponse},
    utils::send_request,
};
use crate::mining::rest_api::models;

const HAS_TIME_UNIT: bool = false;

#[async_trait]
pub trait MiningApi: Send + Sync {
    async fn account_list(
        &self,
        params: AccountListParams,
    ) -> anyhow::Result<RestApiResponse<models::AccountListResponse>>;
    async fn acquiring_algorithm(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::AcquiringAlgorithmResponse>>;
    async fn acquiring_coinname(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::AcquiringCoinnameResponse>>;
    async fn cancel_hashrate_resale_configuration(
        &self,
        params: CancelHashrateResaleConfigurationParams,
    ) -> anyhow::Result<RestApiResponse<models::CancelHashrateResaleConfigurationResponse>>;
    async fn earnings_list(
        &self,
        params: EarningsListParams,
    ) -> anyhow::Result<RestApiResponse<models::EarningsListResponse>>;
    async fn extra_bonus_list(
        &self,
        params: ExtraBonusListParams,
    ) -> anyhow::Result<RestApiResponse<models::ExtraBonusListResponse>>;
    async fn hashrate_resale_detail(
        &self,
        params: HashrateResaleDetailParams,
    ) -> anyhow::Result<RestApiResponse<models::HashrateResaleDetailResponse>>;
    async fn hashrate_resale_list(
        &self,
        params: HashrateResaleListParams,
    ) -> anyhow::Result<RestApiResponse<models::HashrateResaleListResponse>>;
    async fn hashrate_resale_request(
        &self,
        params: HashrateResaleRequestParams,
    ) -> anyhow::Result<RestApiResponse<models::HashrateResaleRequestResponse>>;
    async fn mining_account_earning(
        &self,
        params: MiningAccountEarningParams,
    ) -> anyhow::Result<RestApiResponse<models::MiningAccountEarningResponse>>;
    async fn request_for_detail_miner_list(
        &self,
        params: RequestForDetailMinerListParams,
    ) -> anyhow::Result<RestApiResponse<models::RequestForDetailMinerListResponse>>;
    async fn request_for_miner_list(
        &self,
        params: RequestForMinerListParams,
    ) -> anyhow::Result<RestApiResponse<models::RequestForMinerListResponse>>;
    async fn statistic_list(
        &self,
        params: StatisticListParams,
    ) -> anyhow::Result<RestApiResponse<models::StatisticListResponse>>;
}

#[derive(Debug, Clone)]
pub struct MiningApiClient {
    configuration: ConfigurationRestApi,
}

impl MiningApiClient {
    pub fn new(configuration: ConfigurationRestApi) -> Self {
        Self { configuration }
    }
}

/// Request parameters for the [`account_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`account_list`](#method.account_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AccountListParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AccountListParams {
    /// Create a builder for [`account_list`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    /// * `user_name` — Mining account test
    ///
    #[must_use]
    pub fn builder(algo: String, user_name: String) -> AccountListParamsBuilder {
        AccountListParamsBuilder::default()
            .algo(algo)
            .user_name(user_name)
    }
}
/// Request parameters for the [`cancel_hashrate_resale_configuration`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`cancel_hashrate_resale_configuration`](#method.cancel_hashrate_resale_configuration).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct CancelHashrateResaleConfigurationParams {
    /// Mining ID 168
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub config_id: i64,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl CancelHashrateResaleConfigurationParams {
    /// Create a builder for [`cancel_hashrate_resale_configuration`].
    ///
    /// Required parameters:
    ///
    /// * `config_id` — Mining ID 168
    /// * `user_name` — Mining account test
    ///
    #[must_use]
    pub fn builder(
        config_id: i64,
        user_name: String,
    ) -> CancelHashrateResaleConfigurationParamsBuilder {
        CancelHashrateResaleConfigurationParamsBuilder::default()
            .config_id(config_id)
            .user_name(user_name)
    }
}
/// Request parameters for the [`earnings_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`earnings_list`](#method.earnings_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct EarningsListParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    /// Coin Name
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub coin: Option<String>,
    /// Millisecond timestamp
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_date: Option<i64>,
    /// Millisecond timestamp
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_date: Option<i64>,
    /// Page number, empty default first page, starting from 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_index: Option<i64>,
    /// Min 10,Max 200
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_size: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl EarningsListParams {
    /// Create a builder for [`earnings_list`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    /// * `user_name` — Mining account test
    ///
    #[must_use]
    pub fn builder(algo: String, user_name: String) -> EarningsListParamsBuilder {
        EarningsListParamsBuilder::default()
            .algo(algo)
            .user_name(user_name)
    }
}
/// Request parameters for the [`extra_bonus_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`extra_bonus_list`](#method.extra_bonus_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct ExtraBonusListParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    /// Coin Name
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub coin: Option<String>,
    /// Millisecond timestamp
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_date: Option<i64>,
    /// Millisecond timestamp
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_date: Option<i64>,
    /// Page number, empty default first page, starting from 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_index: Option<i64>,
    /// Min 10,Max 200
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_size: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl ExtraBonusListParams {
    /// Create a builder for [`extra_bonus_list`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    /// * `user_name` — Mining account test
    ///
    #[must_use]
    pub fn builder(algo: String, user_name: String) -> ExtraBonusListParamsBuilder {
        ExtraBonusListParamsBuilder::default()
            .algo(algo)
            .user_name(user_name)
    }
}
/// Request parameters for the [`hashrate_resale_detail`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`hashrate_resale_detail`](#method.hashrate_resale_detail).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct HashrateResaleDetailParams {
    /// Mining ID 168
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub config_id: i64,
    /// Page number, empty default first page, starting from 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_index: Option<i64>,
    /// Min 10,Max 200
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_size: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl HashrateResaleDetailParams {
    /// Create a builder for [`hashrate_resale_detail`].
    ///
    /// Required parameters:
    ///
    /// * `config_id` — Mining ID 168
    ///
    #[must_use]
    pub fn builder(config_id: i64) -> HashrateResaleDetailParamsBuilder {
        HashrateResaleDetailParamsBuilder::default().config_id(config_id)
    }
}
/// Request parameters for the [`hashrate_resale_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`hashrate_resale_list`](#method.hashrate_resale_list).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct HashrateResaleListParams {
    /// Page number, empty default first page, starting from 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_index: Option<i64>,
    /// Min 10,Max 200
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_size: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl HashrateResaleListParams {
    /// Create a builder for [`hashrate_resale_list`].
    ///
    #[must_use]
    pub fn builder() -> HashrateResaleListParamsBuilder {
        HashrateResaleListParamsBuilder::default()
    }
}
/// Request parameters for the [`hashrate_resale_request`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`hashrate_resale_request`](#method.hashrate_resale_request).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct HashrateResaleRequestParams {
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Resale End Time (Millisecond timestamp) 1617659086000
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub end_date: i64,
    /// Resale Start Time(Millisecond timestamp) 1607659086000
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub start_date: i64,
    /// Mining Account S19pro
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub to_pool_user: String,
    /// Resale hashrate h/s must be transferred (BTC is greater than 500000000000 ETH is greater than 500000) 100000000
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub hash_rate: i64,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl HashrateResaleRequestParams {
    /// Create a builder for [`hashrate_resale_request`].
    ///
    /// Required parameters:
    ///
    /// * `user_name` — Mining account test
    /// * `algo` — Algorithm(sha256) sha256
    /// * `end_date` — Resale End Time (Millisecond timestamp) 1617659086000
    /// * `start_date` — Resale Start Time(Millisecond timestamp) 1607659086000
    /// * `to_pool_user` — Mining Account S19pro
    /// * `hash_rate` — Resale hashrate h/s must be transferred (BTC is greater than 500000000000 ETH is greater than 500000) 100000000
    ///
    #[must_use]
    pub fn builder(
        user_name: String,
        algo: String,
        end_date: i64,
        start_date: i64,
        to_pool_user: String,
        hash_rate: i64,
    ) -> HashrateResaleRequestParamsBuilder {
        HashrateResaleRequestParamsBuilder::default()
            .user_name(user_name)
            .algo(algo)
            .end_date(end_date)
            .start_date(start_date)
            .to_pool_user(to_pool_user)
            .hash_rate(hash_rate)
    }
}
/// Request parameters for the [`mining_account_earning`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`mining_account_earning`](#method.mining_account_earning).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct MiningAccountEarningParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Millisecond timestamp
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_date: Option<i64>,
    /// Millisecond timestamp
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_date: Option<i64>,
    /// Page number, empty default first page, starting from 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_index: Option<i64>,
    /// Min 10,Max 200
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_size: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl MiningAccountEarningParams {
    /// Create a builder for [`mining_account_earning`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    ///
    #[must_use]
    pub fn builder(algo: String) -> MiningAccountEarningParamsBuilder {
        MiningAccountEarningParamsBuilder::default().algo(algo)
    }
}
/// Request parameters for the [`request_for_detail_miner_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`request_for_detail_miner_list`](#method.request_for_detail_miner_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct RequestForDetailMinerListParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    /// Miner’s name(required) bhdc1.16A10404B
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub worker_name: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl RequestForDetailMinerListParams {
    /// Create a builder for [`request_for_detail_miner_list`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    /// * `user_name` — Mining account test
    /// * `worker_name` — Miner’s name(required) bhdc1.16A10404B
    ///
    #[must_use]
    pub fn builder(
        algo: String,
        user_name: String,
        worker_name: String,
    ) -> RequestForDetailMinerListParamsBuilder {
        RequestForDetailMinerListParamsBuilder::default()
            .algo(algo)
            .user_name(user_name)
            .worker_name(worker_name)
    }
}
/// Request parameters for the [`request_for_miner_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`request_for_miner_list`](#method.request_for_miner_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct RequestForMinerListParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    /// Page number, empty default first page, starting from 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub page_index: Option<i64>,
    /// sort sequence(default=0)0 positive sequence，1 negative sequence
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub sort: Option<i64>,
    /// Sort by( default 1): <br></br>1: miner name, <br></br>2: real-time computing power, <br></br>3: daily average computing power, <br></br>4: real-time rejection rate, <br></br>5: last submission time
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub sort_column: Option<i64>,
    /// miners status(default=0),0 all，1 valid，2 invalid，3 failure
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub worker_status: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl RequestForMinerListParams {
    /// Create a builder for [`request_for_miner_list`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    /// * `user_name` — Mining account test
    ///
    #[must_use]
    pub fn builder(algo: String, user_name: String) -> RequestForMinerListParamsBuilder {
        RequestForMinerListParamsBuilder::default()
            .algo(algo)
            .user_name(user_name)
    }
}
/// Request parameters for the [`statistic_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`statistic_list`](#method.statistic_list).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct StatisticListParams {
    /// Algorithm(sha256) sha256
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub algo: String,
    /// Mining account test
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub user_name: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl StatisticListParams {
    /// Create a builder for [`statistic_list`].
    ///
    /// Required parameters:
    ///
    /// * `algo` — Algorithm(sha256) sha256
    /// * `user_name` — Mining account test
    ///
    #[must_use]
    pub fn builder(algo: String, user_name: String) -> StatisticListParamsBuilder {
        StatisticListParamsBuilder::default()
            .algo(algo)
            .user_name(user_name)
    }
}

#[async_trait]
impl MiningApi for MiningApiClient {
    async fn account_list(
        &self,
        params: AccountListParams,
    ) -> anyhow::Result<RestApiResponse<models::AccountListResponse>> {
        let AccountListParams {
            algo,
            user_name,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("userName".to_string(), json!(user_name));

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::AccountListResponse>(
            &self.configuration,
            "/sapi/v1/mining/statistics/user/list",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn acquiring_algorithm(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::AcquiringAlgorithmResponse>> {
        let query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        send_request::<models::AcquiringAlgorithmResponse>(
            &self.configuration,
            "/sapi/v1/mining/pub/algoList",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn acquiring_coinname(
        &self,
    ) -> anyhow::Result<RestApiResponse<models::AcquiringCoinnameResponse>> {
        let query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        send_request::<models::AcquiringCoinnameResponse>(
            &self.configuration,
            "/sapi/v1/mining/pub/coinList",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn cancel_hashrate_resale_configuration(
        &self,
        params: CancelHashrateResaleConfigurationParams,
    ) -> anyhow::Result<RestApiResponse<models::CancelHashrateResaleConfigurationResponse>> {
        let CancelHashrateResaleConfigurationParams {
            config_id,
            user_name,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("configId".to_string(), json!(config_id));

        query_params.insert("userName".to_string(), json!(user_name));

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::CancelHashrateResaleConfigurationResponse>(
            &self.configuration,
            "/sapi/v1/mining/hash-transfer/config/cancel",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn earnings_list(
        &self,
        params: EarningsListParams,
    ) -> anyhow::Result<RestApiResponse<models::EarningsListResponse>> {
        let EarningsListParams {
            algo,
            user_name,
            coin,
            start_date,
            end_date,
            page_index,
            page_size,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("userName".to_string(), json!(user_name));

        if let Some(rw) = coin {
            query_params.insert("coin".to_string(), json!(rw));
        }

        if let Some(rw) = start_date {
            query_params.insert("startDate".to_string(), json!(rw));
        }

        if let Some(rw) = end_date {
            query_params.insert("endDate".to_string(), json!(rw));
        }

        if let Some(rw) = page_index {
            query_params.insert("pageIndex".to_string(), json!(rw));
        }

        if let Some(rw) = page_size {
            query_params.insert("pageSize".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::EarningsListResponse>(
            &self.configuration,
            "/sapi/v1/mining/payment/list",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn extra_bonus_list(
        &self,
        params: ExtraBonusListParams,
    ) -> anyhow::Result<RestApiResponse<models::ExtraBonusListResponse>> {
        let ExtraBonusListParams {
            algo,
            user_name,
            coin,
            start_date,
            end_date,
            page_index,
            page_size,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("userName".to_string(), json!(user_name));

        if let Some(rw) = coin {
            query_params.insert("coin".to_string(), json!(rw));
        }

        if let Some(rw) = start_date {
            query_params.insert("startDate".to_string(), json!(rw));
        }

        if let Some(rw) = end_date {
            query_params.insert("endDate".to_string(), json!(rw));
        }

        if let Some(rw) = page_index {
            query_params.insert("pageIndex".to_string(), json!(rw));
        }

        if let Some(rw) = page_size {
            query_params.insert("pageSize".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::ExtraBonusListResponse>(
            &self.configuration,
            "/sapi/v1/mining/payment/other",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn hashrate_resale_detail(
        &self,
        params: HashrateResaleDetailParams,
    ) -> anyhow::Result<RestApiResponse<models::HashrateResaleDetailResponse>> {
        let HashrateResaleDetailParams {
            config_id,
            page_index,
            page_size,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("configId".to_string(), json!(config_id));

        if let Some(rw) = page_index {
            query_params.insert("pageIndex".to_string(), json!(rw));
        }

        if let Some(rw) = page_size {
            query_params.insert("pageSize".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::HashrateResaleDetailResponse>(
            &self.configuration,
            "/sapi/v1/mining/hash-transfer/profit/details",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn hashrate_resale_list(
        &self,
        params: HashrateResaleListParams,
    ) -> anyhow::Result<RestApiResponse<models::HashrateResaleListResponse>> {
        let HashrateResaleListParams {
            page_index,
            page_size,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = page_index {
            query_params.insert("pageIndex".to_string(), json!(rw));
        }

        if let Some(rw) = page_size {
            query_params.insert("pageSize".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::HashrateResaleListResponse>(
            &self.configuration,
            "/sapi/v1/mining/hash-transfer/config/details/list",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn hashrate_resale_request(
        &self,
        params: HashrateResaleRequestParams,
    ) -> anyhow::Result<RestApiResponse<models::HashrateResaleRequestResponse>> {
        let HashrateResaleRequestParams {
            user_name,
            algo,
            end_date,
            start_date,
            to_pool_user,
            hash_rate,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("userName".to_string(), json!(user_name));

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("endDate".to_string(), json!(end_date));

        query_params.insert("startDate".to_string(), json!(start_date));

        query_params.insert("toPoolUser".to_string(), json!(to_pool_user));

        query_params.insert("hashRate".to_string(), json!(hash_rate));

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::HashrateResaleRequestResponse>(
            &self.configuration,
            "/sapi/v1/mining/hash-transfer/config",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn mining_account_earning(
        &self,
        params: MiningAccountEarningParams,
    ) -> anyhow::Result<RestApiResponse<models::MiningAccountEarningResponse>> {
        let MiningAccountEarningParams {
            algo,
            start_date,
            end_date,
            page_index,
            page_size,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        if let Some(rw) = start_date {
            query_params.insert("startDate".to_string(), json!(rw));
        }

        if let Some(rw) = end_date {
            query_params.insert("endDate".to_string(), json!(rw));
        }

        if let Some(rw) = page_index {
            query_params.insert("pageIndex".to_string(), json!(rw));
        }

        if let Some(rw) = page_size {
            query_params.insert("pageSize".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::MiningAccountEarningResponse>(
            &self.configuration,
            "/sapi/v1/mining/payment/uid",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn request_for_detail_miner_list(
        &self,
        params: RequestForDetailMinerListParams,
    ) -> anyhow::Result<RestApiResponse<models::RequestForDetailMinerListResponse>> {
        let RequestForDetailMinerListParams {
            algo,
            user_name,
            worker_name,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("userName".to_string(), json!(user_name));

        query_params.insert("workerName".to_string(), json!(worker_name));

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::RequestForDetailMinerListResponse>(
            &self.configuration,
            "/sapi/v1/mining/worker/detail",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn request_for_miner_list(
        &self,
        params: RequestForMinerListParams,
    ) -> anyhow::Result<RestApiResponse<models::RequestForMinerListResponse>> {
        let RequestForMinerListParams {
            algo,
            user_name,
            page_index,
            sort,
            sort_column,
            worker_status,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("userName".to_string(), json!(user_name));

        if let Some(rw) = page_index {
            query_params.insert("pageIndex".to_string(), json!(rw));
        }

        if let Some(rw) = sort {
            query_params.insert("sort".to_string(), json!(rw));
        }

        if let Some(rw) = sort_column {
            query_params.insert("sortColumn".to_string(), json!(rw));
        }

        if let Some(rw) = worker_status {
            query_params.insert("workerStatus".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::RequestForMinerListResponse>(
            &self.configuration,
            "/sapi/v1/mining/worker/list",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn statistic_list(
        &self,
        params: StatisticListParams,
    ) -> anyhow::Result<RestApiResponse<models::StatisticListResponse>> {
        let StatisticListParams {
            algo,
            user_name,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("algo".to_string(), json!(algo));

        query_params.insert("userName".to_string(), json!(user_name));

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::StatisticListResponse>(
            &self.configuration,
            "/sapi/v1/mining/statistics/user/status",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }
}

#[cfg(all(test, feature = "mining"))]
mod tests {
    use super::*;
    use crate::TOKIO_SHARED_RT;
    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
    use async_trait::async_trait;
    use std::collections::HashMap;

    struct DummyRestApiResponse<T> {
        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
        status: u16,
        headers: HashMap<String, String>,
        rate_limits: Option<Vec<RestApiRateLimit>>,
    }

    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
        fn from(dummy: DummyRestApiResponse<T>) -> Self {
            Self {
                data_fn: dummy.inner,
                status: dummy.status,
                headers: dummy.headers,
                rate_limits: dummy.rate_limits,
            }
        }
    }

    struct MockMiningApiClient {
        force_error: bool,
    }

    #[async_trait]
    impl MiningApi for MockMiningApiClient {
        async fn account_list(
            &self,
            _params: AccountListParams,
        ) -> anyhow::Result<RestApiResponse<models::AccountListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"type":"H_hashrate","userName":"test","list":[{"time":1585267200000,"hashrate":"0.00000000","reject":"0.00000000"},{"time":1585353600000,"hashrate":"0.00000000","reject":"0.00000000"}]},{"type":"D_hashrate","userName":"test","list":[{"time":1587906000000,"hashrate":"0.00000000","reject":"0.00000000"},{"time":1587909600000,"hashrate":"0.00000000","reject":"0.00000000"}]}]}"#).unwrap();
            let dummy_response: models::AccountListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::AccountListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn acquiring_algorithm(
            &self,
        ) -> anyhow::Result<RestApiResponse<models::AcquiringAlgorithmResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"algoName":"sha256","algoId":1,"poolIndex":0,"unit":"h/s"}]}"#).unwrap();
            let dummy_response: models::AcquiringAlgorithmResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::AcquiringAlgorithmResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn acquiring_coinname(
            &self,
        ) -> anyhow::Result<RestApiResponse<models::AcquiringCoinnameResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"coinName":"BTC","coinId":1,"poolIndex":0,"algoId":1,"algoName":"sha256"}]}"#).unwrap();
            let dummy_response: models::AcquiringCoinnameResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::AcquiringCoinnameResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn cancel_hashrate_resale_configuration(
            &self,
            _params: CancelHashrateResaleConfigurationParams,
        ) -> anyhow::Result<RestApiResponse<models::CancelHashrateResaleConfigurationResponse>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value =
                serde_json::from_str(r#"{"code":0,"msg":"","data":true}"#).unwrap();
            let dummy_response: models::CancelHashrateResaleConfigurationResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::CancelHashrateResaleConfigurationResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn earnings_list(
            &self,
            _params: EarningsListParams,
        ) -> anyhow::Result<RestApiResponse<models::EarningsListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"accountProfits":[{"time":1586188800000,"type":31,"hashTransfer":null,"transferAmount":null,"dayHashRate":129129903378244,"profitAmount":8.6083060304,"coinName":"BTC","status":2},{"time":1607529600000,"coinName":"BTC","type":0,"dayHashRate":9942053925926,"profitAmount":0.85426469,"hashTransfer":200000000000,"transferAmount":0.02180958,"status":2},{"time":1607443200000,"coinName":"BTC","type":31,"dayHashRate":200000000000,"profitAmount":0.02905916,"hashTransfer":null,"transferAmount":null,"status":2}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let dummy_response: models::EarningsListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::EarningsListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn extra_bonus_list(
            &self,
            _params: ExtraBonusListParams,
        ) -> anyhow::Result<RestApiResponse<models::ExtraBonusListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"otherProfits":[{"time":1607443200000,"coinName":"BTC","type":4,"profitAmount":0.0011859,"status":2}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let dummy_response: models::ExtraBonusListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::ExtraBonusListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn hashrate_resale_detail(
            &self,
            _params: HashrateResaleDetailParams,
        ) -> anyhow::Result<RestApiResponse<models::HashrateResaleDetailResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"profitTransferDetails":[{"poolUsername":"test4001","toPoolUsername":"pop","algoName":"sha256","hashRate":200000000000,"day":20201213,"amount":0.2256872,"coinName":"BTC"},{"poolUsername":"test4001","toPoolUsername":"pop","algoName":"sha256","hashRate":200000000000,"day":20201213,"amount":0.2256872,"coinName":"BTC"}],"totalNum":8,"pageSize":200}}"#).unwrap();
            let dummy_response: models::HashrateResaleDetailResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::HashrateResaleDetailResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn hashrate_resale_list(
            &self,
            _params: HashrateResaleListParams,
        ) -> anyhow::Result<RestApiResponse<models::HashrateResaleListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"configDetails":[{"configId":168,"poolUsername":"123","toPoolUsername":"user1","algoName":"Ethash","hashRate":5000000,"startDay":20201210,"endDay":20210405,"status":1,"type":0},{"configId":166,"poolUsername":"pop","toPoolUsername":"111111","algoName":"Ethash","hashRate":3320000,"startDay":20201226,"endDay":20201227,"status":0,"type":0}],"totalNum":21,"pageSize":200}}"#).unwrap();
            let dummy_response: models::HashrateResaleListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::HashrateResaleListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn hashrate_resale_request(
            &self,
            _params: HashrateResaleRequestParams,
        ) -> anyhow::Result<RestApiResponse<models::HashrateResaleRequestResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value =
                serde_json::from_str(r#"{"code":0,"msg":"","data":171}"#).unwrap();
            let dummy_response: models::HashrateResaleRequestResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::HashrateResaleRequestResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn mining_account_earning(
            &self,
            _params: MiningAccountEarningParams,
        ) -> anyhow::Result<RestApiResponse<models::MiningAccountEarningResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"accountProfits":[{"time":1607443200000,"coinName":"BTC","type":2,"puid":59985472,"subName":"vdvaghani","amount":0.09186957}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let dummy_response: models::MiningAccountEarningResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::MiningAccountEarningResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn request_for_detail_miner_list(
            &self,
            _params: RequestForDetailMinerListParams,
        ) -> anyhow::Result<RestApiResponse<models::RequestForDetailMinerListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"workerName":"bhdc1.16A10404B","type":"H_hashrate","hashrateDatas":[{"time":1587902400000,"hashrate":"0","reject":0},{"time":1587906000000,"hashrate":"0","reject":0}]},{"workerName":"bhdc1.16A10404B","type":"D_hashrate","hashrateDatas":[{"time":1587902400000,"hashrate":"0","reject":0},{"time":1587906000000,"hashrate":"0","reject":0}]}]}"#).unwrap();
            let dummy_response: models::RequestForDetailMinerListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::RequestForDetailMinerListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn request_for_miner_list(
            &self,
            _params: RequestForMinerListParams,
        ) -> anyhow::Result<RestApiResponse<models::RequestForMinerListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"workerDatas":[{"workerId":"1420554439452400131","workerName":"2X73","status":3,"hashRate":0,"dayHashRate":0,"rejectRate":0,"lastShareTime":1587712919000},{"workerId":"7893926126382807951","workerName":"AZDC1.1A10101","status":2,"hashRate":29711247541680,"dayHashRate":1.269778129801366E13,"rejectRate":0,"lastShareTime":1587969727000}],"totalNum":18530,"pageSize":20}}"#).unwrap();
            let dummy_response: models::RequestForMinerListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::RequestForMinerListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn statistic_list(
            &self,
            _params: StatisticListParams,
        ) -> anyhow::Result<RestApiResponse<models::StatisticListResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"fifteenMinHashRate":"457835490067496409.00000000","dayHashRate":"214289268068874127.65000000","validNum":0,"invalidNum":17562,"profitToday":{"BTC":"0.00314332","BSV":"56.17055953","BCH":"106.61586001"},"profitYesterday":{"BTC":"0.00314332","BSV":"56.17055953","BCH":"106.61586001"},"userName":"test","unit":"h/s","algo":"sha256"}}"#).unwrap();
            let dummy_response: models::StatisticListResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::StatisticListResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }
    }

    #[test]
    fn account_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = AccountListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"type":"H_hashrate","userName":"test","list":[{"time":1585267200000,"hashrate":"0.00000000","reject":"0.00000000"},{"time":1585353600000,"hashrate":"0.00000000","reject":"0.00000000"}]},{"type":"D_hashrate","userName":"test","list":[{"time":1587906000000,"hashrate":"0.00000000","reject":"0.00000000"},{"time":1587909600000,"hashrate":"0.00000000","reject":"0.00000000"}]}]}"#).unwrap();
            let expected_response : models::AccountListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountListResponse");

            let resp = client.account_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn account_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = AccountListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"type":"H_hashrate","userName":"test","list":[{"time":1585267200000,"hashrate":"0.00000000","reject":"0.00000000"},{"time":1585353600000,"hashrate":"0.00000000","reject":"0.00000000"}]},{"type":"D_hashrate","userName":"test","list":[{"time":1587906000000,"hashrate":"0.00000000","reject":"0.00000000"},{"time":1587909600000,"hashrate":"0.00000000","reject":"0.00000000"}]}]}"#).unwrap();
            let expected_response : models::AccountListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountListResponse");

            let resp = client.account_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn account_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = AccountListParams::builder(
                "algo_example".to_string(),
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.account_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn acquiring_algorithm_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"algoName":"sha256","algoId":1,"poolIndex":0,"unit":"h/s"}]}"#).unwrap();
            let expected_response : models::AcquiringAlgorithmResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AcquiringAlgorithmResponse");

            let resp = client.acquiring_algorithm().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn acquiring_algorithm_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"algoName":"sha256","algoId":1,"poolIndex":0,"unit":"h/s"}]}"#).unwrap();
            let expected_response : models::AcquiringAlgorithmResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AcquiringAlgorithmResponse");

            let resp = client.acquiring_algorithm().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn acquiring_algorithm_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            match client.acquiring_algorithm().await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn acquiring_coinname_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"coinName":"BTC","coinId":1,"poolIndex":0,"algoId":1,"algoName":"sha256"}]}"#).unwrap();
            let expected_response : models::AcquiringCoinnameResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AcquiringCoinnameResponse");

            let resp = client.acquiring_coinname().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn acquiring_coinname_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"coinName":"BTC","coinId":1,"poolIndex":0,"algoId":1,"algoName":"sha256"}]}"#).unwrap();
            let expected_response : models::AcquiringCoinnameResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AcquiringCoinnameResponse");

            let resp = client.acquiring_coinname().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn acquiring_coinname_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            match client.acquiring_coinname().await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn cancel_hashrate_resale_configuration_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = CancelHashrateResaleConfigurationParams::builder(
                1,
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"code":0,"msg":"","data":true}"#).unwrap();
            let expected_response: models::CancelHashrateResaleConfigurationResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::CancelHashrateResaleConfigurationResponse");

            let resp = client
                .cancel_hashrate_resale_configuration(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn cancel_hashrate_resale_configuration_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = CancelHashrateResaleConfigurationParams::builder(
                1,
                "user_name_example".to_string(),
            )
            .recv_window(5000)
            .build()
            .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"code":0,"msg":"","data":true}"#).unwrap();
            let expected_response: models::CancelHashrateResaleConfigurationResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::CancelHashrateResaleConfigurationResponse");

            let resp = client
                .cancel_hashrate_resale_configuration(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn cancel_hashrate_resale_configuration_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = CancelHashrateResaleConfigurationParams::builder(
                1,
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.cancel_hashrate_resale_configuration(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn earnings_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = EarningsListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"accountProfits":[{"time":1586188800000,"type":31,"hashTransfer":null,"transferAmount":null,"dayHashRate":129129903378244,"profitAmount":8.6083060304,"coinName":"BTC","status":2},{"time":1607529600000,"coinName":"BTC","type":0,"dayHashRate":9942053925926,"profitAmount":0.85426469,"hashTransfer":200000000000,"transferAmount":0.02180958,"status":2},{"time":1607443200000,"coinName":"BTC","type":31,"dayHashRate":200000000000,"profitAmount":0.02905916,"hashTransfer":null,"transferAmount":null,"status":2}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let expected_response : models::EarningsListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EarningsListResponse");

            let resp = client.earnings_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn earnings_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = EarningsListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).coin("coin_example".to_string()).start_date(789).end_date(789).page_index(1).page_size(789).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"accountProfits":[{"time":1586188800000,"type":31,"hashTransfer":null,"transferAmount":null,"dayHashRate":129129903378244,"profitAmount":8.6083060304,"coinName":"BTC","status":2},{"time":1607529600000,"coinName":"BTC","type":0,"dayHashRate":9942053925926,"profitAmount":0.85426469,"hashTransfer":200000000000,"transferAmount":0.02180958,"status":2},{"time":1607443200000,"coinName":"BTC","type":31,"dayHashRate":200000000000,"profitAmount":0.02905916,"hashTransfer":null,"transferAmount":null,"status":2}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let expected_response : models::EarningsListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::EarningsListResponse");

            let resp = client.earnings_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn earnings_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = EarningsListParams::builder(
                "algo_example".to_string(),
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.earnings_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn extra_bonus_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = ExtraBonusListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"otherProfits":[{"time":1607443200000,"coinName":"BTC","type":4,"profitAmount":0.0011859,"status":2}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let expected_response : models::ExtraBonusListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ExtraBonusListResponse");

            let resp = client.extra_bonus_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn extra_bonus_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = ExtraBonusListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).coin("coin_example".to_string()).start_date(789).end_date(789).page_index(1).page_size(789).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"otherProfits":[{"time":1607443200000,"coinName":"BTC","type":4,"profitAmount":0.0011859,"status":2}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let expected_response : models::ExtraBonusListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ExtraBonusListResponse");

            let resp = client.extra_bonus_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn extra_bonus_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = ExtraBonusListParams::builder(
                "algo_example".to_string(),
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.extra_bonus_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn hashrate_resale_detail_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = HashrateResaleDetailParams::builder(1,).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"profitTransferDetails":[{"poolUsername":"test4001","toPoolUsername":"pop","algoName":"sha256","hashRate":200000000000,"day":20201213,"amount":0.2256872,"coinName":"BTC"},{"poolUsername":"test4001","toPoolUsername":"pop","algoName":"sha256","hashRate":200000000000,"day":20201213,"amount":0.2256872,"coinName":"BTC"}],"totalNum":8,"pageSize":200}}"#).unwrap();
            let expected_response : models::HashrateResaleDetailResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::HashrateResaleDetailResponse");

            let resp = client.hashrate_resale_detail(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn hashrate_resale_detail_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = HashrateResaleDetailParams::builder(1,).page_index(1).page_size(789).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"profitTransferDetails":[{"poolUsername":"test4001","toPoolUsername":"pop","algoName":"sha256","hashRate":200000000000,"day":20201213,"amount":0.2256872,"coinName":"BTC"},{"poolUsername":"test4001","toPoolUsername":"pop","algoName":"sha256","hashRate":200000000000,"day":20201213,"amount":0.2256872,"coinName":"BTC"}],"totalNum":8,"pageSize":200}}"#).unwrap();
            let expected_response : models::HashrateResaleDetailResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::HashrateResaleDetailResponse");

            let resp = client.hashrate_resale_detail(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn hashrate_resale_detail_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = HashrateResaleDetailParams::builder(1).build().unwrap();

            match client.hashrate_resale_detail(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn hashrate_resale_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = HashrateResaleListParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"configDetails":[{"configId":168,"poolUsername":"123","toPoolUsername":"user1","algoName":"Ethash","hashRate":5000000,"startDay":20201210,"endDay":20210405,"status":1,"type":0},{"configId":166,"poolUsername":"pop","toPoolUsername":"111111","algoName":"Ethash","hashRate":3320000,"startDay":20201226,"endDay":20201227,"status":0,"type":0}],"totalNum":21,"pageSize":200}}"#).unwrap();
            let expected_response : models::HashrateResaleListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::HashrateResaleListResponse");

            let resp = client.hashrate_resale_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn hashrate_resale_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = HashrateResaleListParams::builder().page_index(1).page_size(789).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"configDetails":[{"configId":168,"poolUsername":"123","toPoolUsername":"user1","algoName":"Ethash","hashRate":5000000,"startDay":20201210,"endDay":20210405,"status":1,"type":0},{"configId":166,"poolUsername":"pop","toPoolUsername":"111111","algoName":"Ethash","hashRate":3320000,"startDay":20201226,"endDay":20201227,"status":0,"type":0}],"totalNum":21,"pageSize":200}}"#).unwrap();
            let expected_response : models::HashrateResaleListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::HashrateResaleListResponse");

            let resp = client.hashrate_resale_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn hashrate_resale_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = HashrateResaleListParams::builder().build().unwrap();

            match client.hashrate_resale_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn hashrate_resale_request_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = HashrateResaleRequestParams::builder(
                "user_name_example".to_string(),
                "algo_example".to_string(),
                789,
                789,
                "to_pool_user_example".to_string(),
                789,
            )
            .build()
            .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"code":0,"msg":"","data":171}"#).unwrap();
            let expected_response: models::HashrateResaleRequestResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::HashrateResaleRequestResponse");

            let resp = client
                .hashrate_resale_request(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn hashrate_resale_request_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = HashrateResaleRequestParams::builder(
                "user_name_example".to_string(),
                "algo_example".to_string(),
                789,
                789,
                "to_pool_user_example".to_string(),
                789,
            )
            .recv_window(5000)
            .build()
            .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"code":0,"msg":"","data":171}"#).unwrap();
            let expected_response: models::HashrateResaleRequestResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::HashrateResaleRequestResponse");

            let resp = client
                .hashrate_resale_request(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn hashrate_resale_request_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = HashrateResaleRequestParams::builder(
                "user_name_example".to_string(),
                "algo_example".to_string(),
                789,
                789,
                "to_pool_user_example".to_string(),
                789,
            )
            .build()
            .unwrap();

            match client.hashrate_resale_request(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn mining_account_earning_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = MiningAccountEarningParams::builder("algo_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"accountProfits":[{"time":1607443200000,"coinName":"BTC","type":2,"puid":59985472,"subName":"vdvaghani","amount":0.09186957}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let expected_response : models::MiningAccountEarningResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::MiningAccountEarningResponse");

            let resp = client.mining_account_earning(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn mining_account_earning_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = MiningAccountEarningParams::builder("algo_example".to_string(),).start_date(789).end_date(789).page_index(1).page_size(789).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"accountProfits":[{"time":1607443200000,"coinName":"BTC","type":2,"puid":59985472,"subName":"vdvaghani","amount":0.09186957}],"totalNum":3,"pageSize":20}}"#).unwrap();
            let expected_response : models::MiningAccountEarningResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::MiningAccountEarningResponse");

            let resp = client.mining_account_earning(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn mining_account_earning_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = MiningAccountEarningParams::builder("algo_example".to_string())
                .build()
                .unwrap();

            match client.mining_account_earning(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn request_for_detail_miner_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = RequestForDetailMinerListParams::builder("algo_example".to_string(),"user_name_example".to_string(),"worker_name_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"workerName":"bhdc1.16A10404B","type":"H_hashrate","hashrateDatas":[{"time":1587902400000,"hashrate":"0","reject":0},{"time":1587906000000,"hashrate":"0","reject":0}]},{"workerName":"bhdc1.16A10404B","type":"D_hashrate","hashrateDatas":[{"time":1587902400000,"hashrate":"0","reject":0},{"time":1587906000000,"hashrate":"0","reject":0}]}]}"#).unwrap();
            let expected_response : models::RequestForDetailMinerListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::RequestForDetailMinerListResponse");

            let resp = client.request_for_detail_miner_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn request_for_detail_miner_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = RequestForDetailMinerListParams::builder("algo_example".to_string(),"user_name_example".to_string(),"worker_name_example".to_string(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":[{"workerName":"bhdc1.16A10404B","type":"H_hashrate","hashrateDatas":[{"time":1587902400000,"hashrate":"0","reject":0},{"time":1587906000000,"hashrate":"0","reject":0}]},{"workerName":"bhdc1.16A10404B","type":"D_hashrate","hashrateDatas":[{"time":1587902400000,"hashrate":"0","reject":0},{"time":1587906000000,"hashrate":"0","reject":0}]}]}"#).unwrap();
            let expected_response : models::RequestForDetailMinerListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::RequestForDetailMinerListResponse");

            let resp = client.request_for_detail_miner_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn request_for_detail_miner_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = RequestForDetailMinerListParams::builder(
                "algo_example".to_string(),
                "user_name_example".to_string(),
                "worker_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.request_for_detail_miner_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn request_for_miner_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = RequestForMinerListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"workerDatas":[{"workerId":"1420554439452400131","workerName":"2X73","status":3,"hashRate":0,"dayHashRate":0,"rejectRate":0,"lastShareTime":1587712919000},{"workerId":"7893926126382807951","workerName":"AZDC1.1A10101","status":2,"hashRate":29711247541680,"dayHashRate":1.269778129801366E13,"rejectRate":0,"lastShareTime":1587969727000}],"totalNum":18530,"pageSize":20}}"#).unwrap();
            let expected_response : models::RequestForMinerListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::RequestForMinerListResponse");

            let resp = client.request_for_miner_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn request_for_miner_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = RequestForMinerListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).page_index(1).sort(0).sort_column(1).worker_status(0).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"workerDatas":[{"workerId":"1420554439452400131","workerName":"2X73","status":3,"hashRate":0,"dayHashRate":0,"rejectRate":0,"lastShareTime":1587712919000},{"workerId":"7893926126382807951","workerName":"AZDC1.1A10101","status":2,"hashRate":29711247541680,"dayHashRate":1.269778129801366E13,"rejectRate":0,"lastShareTime":1587969727000}],"totalNum":18530,"pageSize":20}}"#).unwrap();
            let expected_response : models::RequestForMinerListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::RequestForMinerListResponse");

            let resp = client.request_for_miner_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn request_for_miner_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = RequestForMinerListParams::builder(
                "algo_example".to_string(),
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.request_for_miner_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn statistic_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = StatisticListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"fifteenMinHashRate":"457835490067496409.00000000","dayHashRate":"214289268068874127.65000000","validNum":0,"invalidNum":17562,"profitToday":{"BTC":"0.00314332","BSV":"56.17055953","BCH":"106.61586001"},"profitYesterday":{"BTC":"0.00314332","BSV":"56.17055953","BCH":"106.61586001"},"userName":"test","unit":"h/s","algo":"sha256"}}"#).unwrap();
            let expected_response : models::StatisticListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::StatisticListResponse");

            let resp = client.statistic_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn statistic_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: false };

            let params = StatisticListParams::builder("algo_example".to_string(),"user_name_example".to_string(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"code":0,"msg":"","data":{"fifteenMinHashRate":"457835490067496409.00000000","dayHashRate":"214289268068874127.65000000","validNum":0,"invalidNum":17562,"profitToday":{"BTC":"0.00314332","BSV":"56.17055953","BCH":"106.61586001"},"profitYesterday":{"BTC":"0.00314332","BSV":"56.17055953","BCH":"106.61586001"},"userName":"test","unit":"h/s","algo":"sha256"}}"#).unwrap();
            let expected_response : models::StatisticListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::StatisticListResponse");

            let resp = client.statistic_list(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn statistic_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMiningApiClient { force_error: true };

            let params = StatisticListParams::builder(
                "algo_example".to_string(),
                "user_name_example".to_string(),
            )
            .build()
            .unwrap();

            match client.statistic_list(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }
}
