Skip to main content

binance_sdk/spot/websocket_api/apis/
account_api.rs

1/*
2 * Binance Spot WebSocket API
3 *
4 * OpenAPI Specifications for the Binance Spot WebSocket API
5 *
6 * API documents:
7 * - [Github web-socket-api documentation file](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-api.md)
8 * - [General API information for web-socket-api on website](https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api/general-api-information)
9 *
10 *
11 * The version of the OpenAPI document: 1.0.0
12 *
13 *
14 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
15 * https://openapi-generator.tech
16 * Do not edit the class manually.
17 */
18
19#![allow(unused_imports)]
20use anyhow::Context;
21use async_trait::async_trait;
22use derive_builder::Builder;
23use rust_decimal::prelude::*;
24use serde::{Deserialize, Serialize};
25use serde_json::Value;
26use std::{collections::BTreeMap, sync::Arc};
27
28use crate::common::{
29    errors::WebsocketError,
30    models::{ParamBuildError, WebsocketApiResponse},
31    utils::remove_empty_value,
32    websocket::{WebsocketApi, WebsocketMessageSendOptions},
33};
34use crate::spot::websocket_api::models;
35
36#[async_trait]
37pub trait AccountApi: Send + Sync {
38    async fn account_commission(
39        &self,
40        params: AccountCommissionParams,
41    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountCommissionResponseResult>>>;
42    async fn account_rate_limits_orders(
43        &self,
44        params: AccountRateLimitsOrdersParams,
45    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AccountRateLimitsOrdersResponseResultInner>>>;
46    async fn account_status(
47        &self,
48        params: AccountStatusParams,
49    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountStatusResponseResult>>>;
50    async fn all_order_lists(
51        &self,
52        params: AllOrderListsParams,
53    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrderListsResponseResultInner>>>;
54    async fn all_orders(
55        &self,
56        params: AllOrdersParams,
57    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrdersResponseResultInner>>>;
58    async fn my_allocations(
59        &self,
60        params: MyAllocationsParams,
61    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyAllocationsResponseResultInner>>>;
62    async fn my_filters(
63        &self,
64        params: MyFiltersParams,
65    ) -> anyhow::Result<WebsocketApiResponse<Box<models::MyFiltersResponseResult>>>;
66    async fn my_prevented_matches(
67        &self,
68        params: MyPreventedMatchesParams,
69    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyPreventedMatchesResponseResultInner>>>;
70    async fn my_trades(
71        &self,
72        params: MyTradesParams,
73    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyTradesResponseResultInner>>>;
74    async fn open_order_lists_status(
75        &self,
76        params: OpenOrderListsStatusParams,
77    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrderListsStatusResponseResultInner>>>;
78    async fn open_orders_status(
79        &self,
80        params: OpenOrdersStatusParams,
81    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrdersStatusResponseResultInner>>>;
82    async fn order_amendments(
83        &self,
84        params: OrderAmendmentsParams,
85    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OrderAmendmentsResponseResultInner>>>;
86    async fn order_list_status(
87        &self,
88        params: OrderListStatusParams,
89    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AllOrderListsResponseResultInner>>>;
90    async fn order_status(
91        &self,
92        params: OrderStatusParams,
93    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderStatusResponseResult>>>;
94}
95
96#[derive(Clone)]
97pub struct AccountApiClient {
98    websocket_api_base: Arc<WebsocketApi>,
99}
100
101impl AccountApiClient {
102    pub fn new(websocket_api_base: Arc<WebsocketApi>) -> Self {
103        Self { websocket_api_base }
104    }
105}
106
107/// Request parameters for the [`account_commission`] operation.
108///
109/// This struct holds all of the inputs you can pass when calling
110/// [`account_commission`](#method.account_commission).
111#[derive(Clone, Debug, Builder)]
112#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
113pub struct AccountCommissionParams {
114    ///
115    /// The `symbol` parameter.
116    ///
117    /// This field is **required.
118    #[builder(setter(into))]
119    pub symbol: String,
120    /// Unique WebSocket request ID.
121    ///
122    /// This field is **optional.
123    #[builder(setter(into), default)]
124    pub id: Option<String>,
125}
126
127impl AccountCommissionParams {
128    /// Create a builder for [`account_commission`].
129    ///
130    /// Required parameters:
131    ///
132    /// * `symbol` — String
133    ///
134    #[must_use]
135    pub fn builder(symbol: String) -> AccountCommissionParamsBuilder {
136        AccountCommissionParamsBuilder::default().symbol(symbol)
137    }
138}
139/// Request parameters for the [`account_rate_limits_orders`] operation.
140///
141/// This struct holds all of the inputs you can pass when calling
142/// [`account_rate_limits_orders`](#method.account_rate_limits_orders).
143#[derive(Clone, Debug, Builder, Default)]
144#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
145pub struct AccountRateLimitsOrdersParams {
146    /// Unique WebSocket request ID.
147    ///
148    /// This field is **optional.
149    #[builder(setter(into), default)]
150    pub id: Option<String>,
151    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
152    ///
153    /// This field is **optional.
154    #[builder(setter(into), default)]
155    pub recv_window: Option<rust_decimal::Decimal>,
156}
157
158impl AccountRateLimitsOrdersParams {
159    /// Create a builder for [`account_rate_limits_orders`].
160    ///
161    #[must_use]
162    pub fn builder() -> AccountRateLimitsOrdersParamsBuilder {
163        AccountRateLimitsOrdersParamsBuilder::default()
164    }
165}
166/// Request parameters for the [`account_status`] operation.
167///
168/// This struct holds all of the inputs you can pass when calling
169/// [`account_status`](#method.account_status).
170#[derive(Clone, Debug, Builder, Default)]
171#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
172pub struct AccountStatusParams {
173    /// Unique WebSocket request ID.
174    ///
175    /// This field is **optional.
176    #[builder(setter(into), default)]
177    pub id: Option<String>,
178    /// When set to `true`, emits only the non-zero balances of an account. <br>Default value: false
179    ///
180    /// This field is **optional.
181    #[builder(setter(into), default)]
182    pub omit_zero_balances: Option<bool>,
183    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
184    ///
185    /// This field is **optional.
186    #[builder(setter(into), default)]
187    pub recv_window: Option<rust_decimal::Decimal>,
188}
189
190impl AccountStatusParams {
191    /// Create a builder for [`account_status`].
192    ///
193    #[must_use]
194    pub fn builder() -> AccountStatusParamsBuilder {
195        AccountStatusParamsBuilder::default()
196    }
197}
198/// Request parameters for the [`all_order_lists`] operation.
199///
200/// This struct holds all of the inputs you can pass when calling
201/// [`all_order_lists`](#method.all_order_lists).
202#[derive(Clone, Debug, Builder, Default)]
203#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
204pub struct AllOrderListsParams {
205    /// Unique WebSocket request ID.
206    ///
207    /// This field is **optional.
208    #[builder(setter(into), default)]
209    pub id: Option<String>,
210    /// Trade ID to begin at
211    ///
212    /// This field is **optional.
213    #[builder(setter(into), default)]
214    pub from_id: Option<i32>,
215    ///
216    /// The `start_time` parameter.
217    ///
218    /// This field is **optional.
219    #[builder(setter(into), default)]
220    pub start_time: Option<i64>,
221    ///
222    /// The `end_time` parameter.
223    ///
224    /// This field is **optional.
225    #[builder(setter(into), default)]
226    pub end_time: Option<i64>,
227    /// Default: 100; Maximum: 5000
228    ///
229    /// This field is **optional.
230    #[builder(setter(into), default)]
231    pub limit: Option<i32>,
232    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
233    ///
234    /// This field is **optional.
235    #[builder(setter(into), default)]
236    pub recv_window: Option<rust_decimal::Decimal>,
237}
238
239impl AllOrderListsParams {
240    /// Create a builder for [`all_order_lists`].
241    ///
242    #[must_use]
243    pub fn builder() -> AllOrderListsParamsBuilder {
244        AllOrderListsParamsBuilder::default()
245    }
246}
247/// Request parameters for the [`all_orders`] operation.
248///
249/// This struct holds all of the inputs you can pass when calling
250/// [`all_orders`](#method.all_orders).
251#[derive(Clone, Debug, Builder)]
252#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
253pub struct AllOrdersParams {
254    ///
255    /// The `symbol` parameter.
256    ///
257    /// This field is **required.
258    #[builder(setter(into))]
259    pub symbol: String,
260    /// Unique WebSocket request ID.
261    ///
262    /// This field is **optional.
263    #[builder(setter(into), default)]
264    pub id: Option<String>,
265    /// `orderId`or`origClientOrderId`mustbesent
266    ///
267    /// This field is **optional.
268    #[builder(setter(into), default)]
269    pub order_id: Option<i64>,
270    ///
271    /// The `start_time` parameter.
272    ///
273    /// This field is **optional.
274    #[builder(setter(into), default)]
275    pub start_time: Option<i64>,
276    ///
277    /// The `end_time` parameter.
278    ///
279    /// This field is **optional.
280    #[builder(setter(into), default)]
281    pub end_time: Option<i64>,
282    /// Default: 100; Maximum: 5000
283    ///
284    /// This field is **optional.
285    #[builder(setter(into), default)]
286    pub limit: Option<i32>,
287    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
288    ///
289    /// This field is **optional.
290    #[builder(setter(into), default)]
291    pub recv_window: Option<rust_decimal::Decimal>,
292}
293
294impl AllOrdersParams {
295    /// Create a builder for [`all_orders`].
296    ///
297    /// Required parameters:
298    ///
299    /// * `symbol` — String
300    ///
301    #[must_use]
302    pub fn builder(symbol: String) -> AllOrdersParamsBuilder {
303        AllOrdersParamsBuilder::default().symbol(symbol)
304    }
305}
306/// Request parameters for the [`my_allocations`] operation.
307///
308/// This struct holds all of the inputs you can pass when calling
309/// [`my_allocations`](#method.my_allocations).
310#[derive(Clone, Debug, Builder)]
311#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
312pub struct MyAllocationsParams {
313    ///
314    /// The `symbol` parameter.
315    ///
316    /// This field is **required.
317    #[builder(setter(into))]
318    pub symbol: String,
319    /// Unique WebSocket request ID.
320    ///
321    /// This field is **optional.
322    #[builder(setter(into), default)]
323    pub id: Option<String>,
324    ///
325    /// The `start_time` parameter.
326    ///
327    /// This field is **optional.
328    #[builder(setter(into), default)]
329    pub start_time: Option<i64>,
330    ///
331    /// The `end_time` parameter.
332    ///
333    /// This field is **optional.
334    #[builder(setter(into), default)]
335    pub end_time: Option<i64>,
336    ///
337    /// The `from_allocation_id` parameter.
338    ///
339    /// This field is **optional.
340    #[builder(setter(into), default)]
341    pub from_allocation_id: Option<i32>,
342    /// Default: 100; Maximum: 5000
343    ///
344    /// This field is **optional.
345    #[builder(setter(into), default)]
346    pub limit: Option<i32>,
347    /// `orderId`or`origClientOrderId`mustbesent
348    ///
349    /// This field is **optional.
350    #[builder(setter(into), default)]
351    pub order_id: Option<i64>,
352    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
353    ///
354    /// This field is **optional.
355    #[builder(setter(into), default)]
356    pub recv_window: Option<rust_decimal::Decimal>,
357}
358
359impl MyAllocationsParams {
360    /// Create a builder for [`my_allocations`].
361    ///
362    /// Required parameters:
363    ///
364    /// * `symbol` — String
365    ///
366    #[must_use]
367    pub fn builder(symbol: String) -> MyAllocationsParamsBuilder {
368        MyAllocationsParamsBuilder::default().symbol(symbol)
369    }
370}
371/// Request parameters for the [`my_filters`] operation.
372///
373/// This struct holds all of the inputs you can pass when calling
374/// [`my_filters`](#method.my_filters).
375#[derive(Clone, Debug, Builder)]
376#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
377pub struct MyFiltersParams {
378    ///
379    /// The `symbol` parameter.
380    ///
381    /// This field is **required.
382    #[builder(setter(into))]
383    pub symbol: String,
384    /// Unique WebSocket request ID.
385    ///
386    /// This field is **optional.
387    #[builder(setter(into), default)]
388    pub id: Option<String>,
389    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
390    ///
391    /// This field is **optional.
392    #[builder(setter(into), default)]
393    pub recv_window: Option<rust_decimal::Decimal>,
394}
395
396impl MyFiltersParams {
397    /// Create a builder for [`my_filters`].
398    ///
399    /// Required parameters:
400    ///
401    /// * `symbol` — String
402    ///
403    #[must_use]
404    pub fn builder(symbol: String) -> MyFiltersParamsBuilder {
405        MyFiltersParamsBuilder::default().symbol(symbol)
406    }
407}
408/// Request parameters for the [`my_prevented_matches`] operation.
409///
410/// This struct holds all of the inputs you can pass when calling
411/// [`my_prevented_matches`](#method.my_prevented_matches).
412#[derive(Clone, Debug, Builder)]
413#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
414pub struct MyPreventedMatchesParams {
415    ///
416    /// The `symbol` parameter.
417    ///
418    /// This field is **required.
419    #[builder(setter(into))]
420    pub symbol: String,
421    /// Unique WebSocket request ID.
422    ///
423    /// This field is **optional.
424    #[builder(setter(into), default)]
425    pub id: Option<String>,
426    ///
427    /// The `prevented_match_id` parameter.
428    ///
429    /// This field is **optional.
430    #[builder(setter(into), default)]
431    pub prevented_match_id: Option<i64>,
432    /// `orderId`or`origClientOrderId`mustbesent
433    ///
434    /// This field is **optional.
435    #[builder(setter(into), default)]
436    pub order_id: Option<i64>,
437    ///
438    /// The `from_prevented_match_id` parameter.
439    ///
440    /// This field is **optional.
441    #[builder(setter(into), default)]
442    pub from_prevented_match_id: Option<i64>,
443    /// Default: 100; Maximum: 5000
444    ///
445    /// This field is **optional.
446    #[builder(setter(into), default)]
447    pub limit: Option<i32>,
448    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
449    ///
450    /// This field is **optional.
451    #[builder(setter(into), default)]
452    pub recv_window: Option<rust_decimal::Decimal>,
453}
454
455impl MyPreventedMatchesParams {
456    /// Create a builder for [`my_prevented_matches`].
457    ///
458    /// Required parameters:
459    ///
460    /// * `symbol` — String
461    ///
462    #[must_use]
463    pub fn builder(symbol: String) -> MyPreventedMatchesParamsBuilder {
464        MyPreventedMatchesParamsBuilder::default().symbol(symbol)
465    }
466}
467/// Request parameters for the [`my_trades`] operation.
468///
469/// This struct holds all of the inputs you can pass when calling
470/// [`my_trades`](#method.my_trades).
471#[derive(Clone, Debug, Builder)]
472#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
473pub struct MyTradesParams {
474    ///
475    /// The `symbol` parameter.
476    ///
477    /// This field is **required.
478    #[builder(setter(into))]
479    pub symbol: String,
480    /// Unique WebSocket request ID.
481    ///
482    /// This field is **optional.
483    #[builder(setter(into), default)]
484    pub id: Option<String>,
485    /// `orderId`or`origClientOrderId`mustbesent
486    ///
487    /// This field is **optional.
488    #[builder(setter(into), default)]
489    pub order_id: Option<i64>,
490    ///
491    /// The `start_time` parameter.
492    ///
493    /// This field is **optional.
494    #[builder(setter(into), default)]
495    pub start_time: Option<i64>,
496    ///
497    /// The `end_time` parameter.
498    ///
499    /// This field is **optional.
500    #[builder(setter(into), default)]
501    pub end_time: Option<i64>,
502    /// Trade ID to begin at
503    ///
504    /// This field is **optional.
505    #[builder(setter(into), default)]
506    pub from_id: Option<i32>,
507    /// Default: 100; Maximum: 5000
508    ///
509    /// This field is **optional.
510    #[builder(setter(into), default)]
511    pub limit: Option<i32>,
512    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
513    ///
514    /// This field is **optional.
515    #[builder(setter(into), default)]
516    pub recv_window: Option<rust_decimal::Decimal>,
517}
518
519impl MyTradesParams {
520    /// Create a builder for [`my_trades`].
521    ///
522    /// Required parameters:
523    ///
524    /// * `symbol` — String
525    ///
526    #[must_use]
527    pub fn builder(symbol: String) -> MyTradesParamsBuilder {
528        MyTradesParamsBuilder::default().symbol(symbol)
529    }
530}
531/// Request parameters for the [`open_order_lists_status`] operation.
532///
533/// This struct holds all of the inputs you can pass when calling
534/// [`open_order_lists_status`](#method.open_order_lists_status).
535#[derive(Clone, Debug, Builder, Default)]
536#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
537pub struct OpenOrderListsStatusParams {
538    /// Unique WebSocket request ID.
539    ///
540    /// This field is **optional.
541    #[builder(setter(into), default)]
542    pub id: Option<String>,
543    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
544    ///
545    /// This field is **optional.
546    #[builder(setter(into), default)]
547    pub recv_window: Option<rust_decimal::Decimal>,
548}
549
550impl OpenOrderListsStatusParams {
551    /// Create a builder for [`open_order_lists_status`].
552    ///
553    #[must_use]
554    pub fn builder() -> OpenOrderListsStatusParamsBuilder {
555        OpenOrderListsStatusParamsBuilder::default()
556    }
557}
558/// Request parameters for the [`open_orders_status`] operation.
559///
560/// This struct holds all of the inputs you can pass when calling
561/// [`open_orders_status`](#method.open_orders_status).
562#[derive(Clone, Debug, Builder, Default)]
563#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
564pub struct OpenOrdersStatusParams {
565    /// Unique WebSocket request ID.
566    ///
567    /// This field is **optional.
568    #[builder(setter(into), default)]
569    pub id: Option<String>,
570    /// Describe a single symbol
571    ///
572    /// This field is **optional.
573    #[builder(setter(into), default)]
574    pub symbol: Option<String>,
575    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
576    ///
577    /// This field is **optional.
578    #[builder(setter(into), default)]
579    pub recv_window: Option<rust_decimal::Decimal>,
580}
581
582impl OpenOrdersStatusParams {
583    /// Create a builder for [`open_orders_status`].
584    ///
585    #[must_use]
586    pub fn builder() -> OpenOrdersStatusParamsBuilder {
587        OpenOrdersStatusParamsBuilder::default()
588    }
589}
590/// Request parameters for the [`order_amendments`] operation.
591///
592/// This struct holds all of the inputs you can pass when calling
593/// [`order_amendments`](#method.order_amendments).
594#[derive(Clone, Debug, Builder)]
595#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
596pub struct OrderAmendmentsParams {
597    ///
598    /// The `symbol` parameter.
599    ///
600    /// This field is **required.
601    #[builder(setter(into))]
602    pub symbol: String,
603    ///
604    /// The `order_id` parameter.
605    ///
606    /// This field is **required.
607    #[builder(setter(into))]
608    pub order_id: i64,
609    /// Unique WebSocket request ID.
610    ///
611    /// This field is **optional.
612    #[builder(setter(into), default)]
613    pub id: Option<String>,
614    ///
615    /// The `from_execution_id` parameter.
616    ///
617    /// This field is **optional.
618    #[builder(setter(into), default)]
619    pub from_execution_id: Option<i64>,
620    /// Default: 500; Maximum: 1000
621    ///
622    /// This field is **optional.
623    #[builder(setter(into), default)]
624    pub limit: Option<i64>,
625    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
626    ///
627    /// This field is **optional.
628    #[builder(setter(into), default)]
629    pub recv_window: Option<rust_decimal::Decimal>,
630}
631
632impl OrderAmendmentsParams {
633    /// Create a builder for [`order_amendments`].
634    ///
635    /// Required parameters:
636    ///
637    /// * `symbol` — String
638    /// * `order_id` — i64
639    ///
640    #[must_use]
641    pub fn builder(symbol: String, order_id: i64) -> OrderAmendmentsParamsBuilder {
642        OrderAmendmentsParamsBuilder::default()
643            .symbol(symbol)
644            .order_id(order_id)
645    }
646}
647/// Request parameters for the [`order_list_status`] operation.
648///
649/// This struct holds all of the inputs you can pass when calling
650/// [`order_list_status`](#method.order_list_status).
651#[derive(Clone, Debug, Builder, Default)]
652#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
653pub struct OrderListStatusParams {
654    /// Unique WebSocket request ID.
655    ///
656    /// This field is **optional.
657    #[builder(setter(into), default)]
658    pub id: Option<String>,
659    /// `orderId`or`origClientOrderId`mustbesent
660    ///
661    /// This field is **optional.
662    #[builder(setter(into), default)]
663    pub orig_client_order_id: Option<String>,
664    /// Cancel order list by orderListId
665    ///
666    /// This field is **optional.
667    #[builder(setter(into), default)]
668    pub order_list_id: Option<i32>,
669    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
670    ///
671    /// This field is **optional.
672    #[builder(setter(into), default)]
673    pub recv_window: Option<rust_decimal::Decimal>,
674}
675
676impl OrderListStatusParams {
677    /// Create a builder for [`order_list_status`].
678    ///
679    #[must_use]
680    pub fn builder() -> OrderListStatusParamsBuilder {
681        OrderListStatusParamsBuilder::default()
682    }
683}
684/// Request parameters for the [`order_status`] operation.
685///
686/// This struct holds all of the inputs you can pass when calling
687/// [`order_status`](#method.order_status).
688#[derive(Clone, Debug, Builder)]
689#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
690pub struct OrderStatusParams {
691    ///
692    /// The `symbol` parameter.
693    ///
694    /// This field is **required.
695    #[builder(setter(into))]
696    pub symbol: String,
697    /// Unique WebSocket request ID.
698    ///
699    /// This field is **optional.
700    #[builder(setter(into), default)]
701    pub id: Option<String>,
702    /// `orderId`or`origClientOrderId`mustbesent
703    ///
704    /// This field is **optional.
705    #[builder(setter(into), default)]
706    pub order_id: Option<i64>,
707    /// `orderId`or`origClientOrderId`mustbesent
708    ///
709    /// This field is **optional.
710    #[builder(setter(into), default)]
711    pub orig_client_order_id: Option<String>,
712    /// The value cannot be greater than `60000`. <br> Supports up to three decimal places of precision (e.g., 6000.346) so that microseconds may be specified.
713    ///
714    /// This field is **optional.
715    #[builder(setter(into), default)]
716    pub recv_window: Option<rust_decimal::Decimal>,
717}
718
719impl OrderStatusParams {
720    /// Create a builder for [`order_status`].
721    ///
722    /// Required parameters:
723    ///
724    /// * `symbol` — String
725    ///
726    #[must_use]
727    pub fn builder(symbol: String) -> OrderStatusParamsBuilder {
728        OrderStatusParamsBuilder::default().symbol(symbol)
729    }
730}
731
732#[async_trait]
733impl AccountApi for AccountApiClient {
734    async fn account_commission(
735        &self,
736        params: AccountCommissionParams,
737    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountCommissionResponseResult>>> {
738        let AccountCommissionParams { symbol, id } = params;
739
740        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
741        payload.insert("symbol".to_string(), serde_json::json!(symbol));
742        if let Some(value) = id {
743            payload.insert("id".to_string(), serde_json::json!(value));
744        }
745        let payload = remove_empty_value(payload);
746
747        self.websocket_api_base
748            .send_message::<Box<models::AccountCommissionResponseResult>>(
749                "/account.commission".trim_start_matches('/'),
750                payload,
751                WebsocketMessageSendOptions::new().signed(),
752            )
753            .await
754            .map_err(anyhow::Error::from)?
755            .into_iter()
756            .next()
757            .ok_or(WebsocketError::NoResponse)
758            .map_err(anyhow::Error::from)
759    }
760
761    async fn account_rate_limits_orders(
762        &self,
763        params: AccountRateLimitsOrdersParams,
764    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AccountRateLimitsOrdersResponseResultInner>>>
765    {
766        let AccountRateLimitsOrdersParams { id, recv_window } = params;
767
768        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
769        if let Some(value) = id {
770            payload.insert("id".to_string(), serde_json::json!(value));
771        }
772        if let Some(value) = recv_window {
773            payload.insert("recvWindow".to_string(), serde_json::json!(value));
774        }
775        let payload = remove_empty_value(payload);
776
777        self.websocket_api_base
778            .send_message::<Vec<models::AccountRateLimitsOrdersResponseResultInner>>(
779                "/account.rateLimits.orders".trim_start_matches('/'),
780                payload,
781                WebsocketMessageSendOptions::new().signed(),
782            )
783            .await
784            .map_err(anyhow::Error::from)?
785            .into_iter()
786            .next()
787            .ok_or(WebsocketError::NoResponse)
788            .map_err(anyhow::Error::from)
789    }
790
791    async fn account_status(
792        &self,
793        params: AccountStatusParams,
794    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountStatusResponseResult>>> {
795        let AccountStatusParams {
796            id,
797            omit_zero_balances,
798            recv_window,
799        } = params;
800
801        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
802        if let Some(value) = id {
803            payload.insert("id".to_string(), serde_json::json!(value));
804        }
805        if let Some(value) = omit_zero_balances {
806            payload.insert("omitZeroBalances".to_string(), serde_json::json!(value));
807        }
808        if let Some(value) = recv_window {
809            payload.insert("recvWindow".to_string(), serde_json::json!(value));
810        }
811        let payload = remove_empty_value(payload);
812
813        self.websocket_api_base
814            .send_message::<Box<models::AccountStatusResponseResult>>(
815                "/account.status".trim_start_matches('/'),
816                payload,
817                WebsocketMessageSendOptions::new().signed(),
818            )
819            .await
820            .map_err(anyhow::Error::from)?
821            .into_iter()
822            .next()
823            .ok_or(WebsocketError::NoResponse)
824            .map_err(anyhow::Error::from)
825    }
826
827    async fn all_order_lists(
828        &self,
829        params: AllOrderListsParams,
830    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrderListsResponseResultInner>>> {
831        let AllOrderListsParams {
832            id,
833            from_id,
834            start_time,
835            end_time,
836            limit,
837            recv_window,
838        } = params;
839
840        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
841        if let Some(value) = id {
842            payload.insert("id".to_string(), serde_json::json!(value));
843        }
844        if let Some(value) = from_id {
845            payload.insert("fromId".to_string(), serde_json::json!(value));
846        }
847        if let Some(value) = start_time {
848            payload.insert("startTime".to_string(), serde_json::json!(value));
849        }
850        if let Some(value) = end_time {
851            payload.insert("endTime".to_string(), serde_json::json!(value));
852        }
853        if let Some(value) = limit {
854            payload.insert("limit".to_string(), serde_json::json!(value));
855        }
856        if let Some(value) = recv_window {
857            payload.insert("recvWindow".to_string(), serde_json::json!(value));
858        }
859        let payload = remove_empty_value(payload);
860
861        self.websocket_api_base
862            .send_message::<Vec<models::AllOrderListsResponseResultInner>>(
863                "/allOrderLists".trim_start_matches('/'),
864                payload,
865                WebsocketMessageSendOptions::new().signed(),
866            )
867            .await
868            .map_err(anyhow::Error::from)?
869            .into_iter()
870            .next()
871            .ok_or(WebsocketError::NoResponse)
872            .map_err(anyhow::Error::from)
873    }
874
875    async fn all_orders(
876        &self,
877        params: AllOrdersParams,
878    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrdersResponseResultInner>>> {
879        let AllOrdersParams {
880            symbol,
881            id,
882            order_id,
883            start_time,
884            end_time,
885            limit,
886            recv_window,
887        } = params;
888
889        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
890        payload.insert("symbol".to_string(), serde_json::json!(symbol));
891        if let Some(value) = id {
892            payload.insert("id".to_string(), serde_json::json!(value));
893        }
894        if let Some(value) = order_id {
895            payload.insert("orderId".to_string(), serde_json::json!(value));
896        }
897        if let Some(value) = start_time {
898            payload.insert("startTime".to_string(), serde_json::json!(value));
899        }
900        if let Some(value) = end_time {
901            payload.insert("endTime".to_string(), serde_json::json!(value));
902        }
903        if let Some(value) = limit {
904            payload.insert("limit".to_string(), serde_json::json!(value));
905        }
906        if let Some(value) = recv_window {
907            payload.insert("recvWindow".to_string(), serde_json::json!(value));
908        }
909        let payload = remove_empty_value(payload);
910
911        self.websocket_api_base
912            .send_message::<Vec<models::AllOrdersResponseResultInner>>(
913                "/allOrders".trim_start_matches('/'),
914                payload,
915                WebsocketMessageSendOptions::new().signed(),
916            )
917            .await
918            .map_err(anyhow::Error::from)?
919            .into_iter()
920            .next()
921            .ok_or(WebsocketError::NoResponse)
922            .map_err(anyhow::Error::from)
923    }
924
925    async fn my_allocations(
926        &self,
927        params: MyAllocationsParams,
928    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyAllocationsResponseResultInner>>> {
929        let MyAllocationsParams {
930            symbol,
931            id,
932            start_time,
933            end_time,
934            from_allocation_id,
935            limit,
936            order_id,
937            recv_window,
938        } = params;
939
940        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
941        payload.insert("symbol".to_string(), serde_json::json!(symbol));
942        if let Some(value) = id {
943            payload.insert("id".to_string(), serde_json::json!(value));
944        }
945        if let Some(value) = start_time {
946            payload.insert("startTime".to_string(), serde_json::json!(value));
947        }
948        if let Some(value) = end_time {
949            payload.insert("endTime".to_string(), serde_json::json!(value));
950        }
951        if let Some(value) = from_allocation_id {
952            payload.insert("fromAllocationId".to_string(), serde_json::json!(value));
953        }
954        if let Some(value) = limit {
955            payload.insert("limit".to_string(), serde_json::json!(value));
956        }
957        if let Some(value) = order_id {
958            payload.insert("orderId".to_string(), serde_json::json!(value));
959        }
960        if let Some(value) = recv_window {
961            payload.insert("recvWindow".to_string(), serde_json::json!(value));
962        }
963        let payload = remove_empty_value(payload);
964
965        self.websocket_api_base
966            .send_message::<Vec<models::MyAllocationsResponseResultInner>>(
967                "/myAllocations".trim_start_matches('/'),
968                payload,
969                WebsocketMessageSendOptions::new().signed(),
970            )
971            .await
972            .map_err(anyhow::Error::from)?
973            .into_iter()
974            .next()
975            .ok_or(WebsocketError::NoResponse)
976            .map_err(anyhow::Error::from)
977    }
978
979    async fn my_filters(
980        &self,
981        params: MyFiltersParams,
982    ) -> anyhow::Result<WebsocketApiResponse<Box<models::MyFiltersResponseResult>>> {
983        let MyFiltersParams {
984            symbol,
985            id,
986            recv_window,
987        } = params;
988
989        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
990        payload.insert("symbol".to_string(), serde_json::json!(symbol));
991        if let Some(value) = id {
992            payload.insert("id".to_string(), serde_json::json!(value));
993        }
994        if let Some(value) = recv_window {
995            payload.insert("recvWindow".to_string(), serde_json::json!(value));
996        }
997        let payload = remove_empty_value(payload);
998
999        self.websocket_api_base
1000            .send_message::<Box<models::MyFiltersResponseResult>>(
1001                "/myFilters".trim_start_matches('/'),
1002                payload,
1003                WebsocketMessageSendOptions::new().signed(),
1004            )
1005            .await
1006            .map_err(anyhow::Error::from)?
1007            .into_iter()
1008            .next()
1009            .ok_or(WebsocketError::NoResponse)
1010            .map_err(anyhow::Error::from)
1011    }
1012
1013    async fn my_prevented_matches(
1014        &self,
1015        params: MyPreventedMatchesParams,
1016    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyPreventedMatchesResponseResultInner>>>
1017    {
1018        let MyPreventedMatchesParams {
1019            symbol,
1020            id,
1021            prevented_match_id,
1022            order_id,
1023            from_prevented_match_id,
1024            limit,
1025            recv_window,
1026        } = params;
1027
1028        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1029        payload.insert("symbol".to_string(), serde_json::json!(symbol));
1030        if let Some(value) = id {
1031            payload.insert("id".to_string(), serde_json::json!(value));
1032        }
1033        if let Some(value) = prevented_match_id {
1034            payload.insert("preventedMatchId".to_string(), serde_json::json!(value));
1035        }
1036        if let Some(value) = order_id {
1037            payload.insert("orderId".to_string(), serde_json::json!(value));
1038        }
1039        if let Some(value) = from_prevented_match_id {
1040            payload.insert("fromPreventedMatchId".to_string(), serde_json::json!(value));
1041        }
1042        if let Some(value) = limit {
1043            payload.insert("limit".to_string(), serde_json::json!(value));
1044        }
1045        if let Some(value) = recv_window {
1046            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1047        }
1048        let payload = remove_empty_value(payload);
1049
1050        self.websocket_api_base
1051            .send_message::<Vec<models::MyPreventedMatchesResponseResultInner>>(
1052                "/myPreventedMatches".trim_start_matches('/'),
1053                payload,
1054                WebsocketMessageSendOptions::new().signed(),
1055            )
1056            .await
1057            .map_err(anyhow::Error::from)?
1058            .into_iter()
1059            .next()
1060            .ok_or(WebsocketError::NoResponse)
1061            .map_err(anyhow::Error::from)
1062    }
1063
1064    async fn my_trades(
1065        &self,
1066        params: MyTradesParams,
1067    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyTradesResponseResultInner>>> {
1068        let MyTradesParams {
1069            symbol,
1070            id,
1071            order_id,
1072            start_time,
1073            end_time,
1074            from_id,
1075            limit,
1076            recv_window,
1077        } = params;
1078
1079        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1080        payload.insert("symbol".to_string(), serde_json::json!(symbol));
1081        if let Some(value) = id {
1082            payload.insert("id".to_string(), serde_json::json!(value));
1083        }
1084        if let Some(value) = order_id {
1085            payload.insert("orderId".to_string(), serde_json::json!(value));
1086        }
1087        if let Some(value) = start_time {
1088            payload.insert("startTime".to_string(), serde_json::json!(value));
1089        }
1090        if let Some(value) = end_time {
1091            payload.insert("endTime".to_string(), serde_json::json!(value));
1092        }
1093        if let Some(value) = from_id {
1094            payload.insert("fromId".to_string(), serde_json::json!(value));
1095        }
1096        if let Some(value) = limit {
1097            payload.insert("limit".to_string(), serde_json::json!(value));
1098        }
1099        if let Some(value) = recv_window {
1100            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1101        }
1102        let payload = remove_empty_value(payload);
1103
1104        self.websocket_api_base
1105            .send_message::<Vec<models::MyTradesResponseResultInner>>(
1106                "/myTrades".trim_start_matches('/'),
1107                payload,
1108                WebsocketMessageSendOptions::new().signed(),
1109            )
1110            .await
1111            .map_err(anyhow::Error::from)?
1112            .into_iter()
1113            .next()
1114            .ok_or(WebsocketError::NoResponse)
1115            .map_err(anyhow::Error::from)
1116    }
1117
1118    async fn open_order_lists_status(
1119        &self,
1120        params: OpenOrderListsStatusParams,
1121    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrderListsStatusResponseResultInner>>>
1122    {
1123        let OpenOrderListsStatusParams { id, recv_window } = params;
1124
1125        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1126        if let Some(value) = id {
1127            payload.insert("id".to_string(), serde_json::json!(value));
1128        }
1129        if let Some(value) = recv_window {
1130            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1131        }
1132        let payload = remove_empty_value(payload);
1133
1134        self.websocket_api_base
1135            .send_message::<Vec<models::OpenOrderListsStatusResponseResultInner>>(
1136                "/openOrderLists.status".trim_start_matches('/'),
1137                payload,
1138                WebsocketMessageSendOptions::new().signed(),
1139            )
1140            .await
1141            .map_err(anyhow::Error::from)?
1142            .into_iter()
1143            .next()
1144            .ok_or(WebsocketError::NoResponse)
1145            .map_err(anyhow::Error::from)
1146    }
1147
1148    async fn open_orders_status(
1149        &self,
1150        params: OpenOrdersStatusParams,
1151    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrdersStatusResponseResultInner>>>
1152    {
1153        let OpenOrdersStatusParams {
1154            id,
1155            symbol,
1156            recv_window,
1157        } = params;
1158
1159        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1160        if let Some(value) = id {
1161            payload.insert("id".to_string(), serde_json::json!(value));
1162        }
1163        if let Some(value) = symbol {
1164            payload.insert("symbol".to_string(), serde_json::json!(value));
1165        }
1166        if let Some(value) = recv_window {
1167            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1168        }
1169        let payload = remove_empty_value(payload);
1170
1171        self.websocket_api_base
1172            .send_message::<Vec<models::OpenOrdersStatusResponseResultInner>>(
1173                "/openOrders.status".trim_start_matches('/'),
1174                payload,
1175                WebsocketMessageSendOptions::new().signed(),
1176            )
1177            .await
1178            .map_err(anyhow::Error::from)?
1179            .into_iter()
1180            .next()
1181            .ok_or(WebsocketError::NoResponse)
1182            .map_err(anyhow::Error::from)
1183    }
1184
1185    async fn order_amendments(
1186        &self,
1187        params: OrderAmendmentsParams,
1188    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OrderAmendmentsResponseResultInner>>> {
1189        let OrderAmendmentsParams {
1190            symbol,
1191            order_id,
1192            id,
1193            from_execution_id,
1194            limit,
1195            recv_window,
1196        } = params;
1197
1198        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1199        payload.insert("symbol".to_string(), serde_json::json!(symbol));
1200        payload.insert("orderId".to_string(), serde_json::json!(order_id));
1201        if let Some(value) = id {
1202            payload.insert("id".to_string(), serde_json::json!(value));
1203        }
1204        if let Some(value) = from_execution_id {
1205            payload.insert("fromExecutionId".to_string(), serde_json::json!(value));
1206        }
1207        if let Some(value) = limit {
1208            payload.insert("limit".to_string(), serde_json::json!(value));
1209        }
1210        if let Some(value) = recv_window {
1211            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1212        }
1213        let payload = remove_empty_value(payload);
1214
1215        self.websocket_api_base
1216            .send_message::<Vec<models::OrderAmendmentsResponseResultInner>>(
1217                "/order.amendments".trim_start_matches('/'),
1218                payload,
1219                WebsocketMessageSendOptions::new().signed(),
1220            )
1221            .await
1222            .map_err(anyhow::Error::from)?
1223            .into_iter()
1224            .next()
1225            .ok_or(WebsocketError::NoResponse)
1226            .map_err(anyhow::Error::from)
1227    }
1228
1229    async fn order_list_status(
1230        &self,
1231        params: OrderListStatusParams,
1232    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AllOrderListsResponseResultInner>>> {
1233        let OrderListStatusParams {
1234            id,
1235            orig_client_order_id,
1236            order_list_id,
1237            recv_window,
1238        } = params;
1239
1240        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1241        if let Some(value) = id {
1242            payload.insert("id".to_string(), serde_json::json!(value));
1243        }
1244        if let Some(value) = orig_client_order_id {
1245            payload.insert("origClientOrderId".to_string(), serde_json::json!(value));
1246        }
1247        if let Some(value) = order_list_id {
1248            payload.insert("orderListId".to_string(), serde_json::json!(value));
1249        }
1250        if let Some(value) = recv_window {
1251            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1252        }
1253        let payload = remove_empty_value(payload);
1254
1255        self.websocket_api_base
1256            .send_message::<Box<models::AllOrderListsResponseResultInner>>(
1257                "/orderList.status".trim_start_matches('/'),
1258                payload,
1259                WebsocketMessageSendOptions::new().signed(),
1260            )
1261            .await
1262            .map_err(anyhow::Error::from)?
1263            .into_iter()
1264            .next()
1265            .ok_or(WebsocketError::NoResponse)
1266            .map_err(anyhow::Error::from)
1267    }
1268
1269    async fn order_status(
1270        &self,
1271        params: OrderStatusParams,
1272    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderStatusResponseResult>>> {
1273        let OrderStatusParams {
1274            symbol,
1275            id,
1276            order_id,
1277            orig_client_order_id,
1278            recv_window,
1279        } = params;
1280
1281        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
1282        payload.insert("symbol".to_string(), serde_json::json!(symbol));
1283        if let Some(value) = id {
1284            payload.insert("id".to_string(), serde_json::json!(value));
1285        }
1286        if let Some(value) = order_id {
1287            payload.insert("orderId".to_string(), serde_json::json!(value));
1288        }
1289        if let Some(value) = orig_client_order_id {
1290            payload.insert("origClientOrderId".to_string(), serde_json::json!(value));
1291        }
1292        if let Some(value) = recv_window {
1293            payload.insert("recvWindow".to_string(), serde_json::json!(value));
1294        }
1295        let payload = remove_empty_value(payload);
1296
1297        self.websocket_api_base
1298            .send_message::<Box<models::OrderStatusResponseResult>>(
1299                "/order.status".trim_start_matches('/'),
1300                payload,
1301                WebsocketMessageSendOptions::new().signed(),
1302            )
1303            .await
1304            .map_err(anyhow::Error::from)?
1305            .into_iter()
1306            .next()
1307            .ok_or(WebsocketError::NoResponse)
1308            .map_err(anyhow::Error::from)
1309    }
1310}
1311
1312#[cfg(all(test, feature = "spot"))]
1313mod tests {
1314    use super::*;
1315    use crate::TOKIO_SHARED_RT;
1316    use crate::common::websocket::{WebsocketApi, WebsocketConnection, WebsocketHandler};
1317    use crate::config::ConfigurationWebsocketApi;
1318    use crate::errors::WebsocketError;
1319    use crate::models::WebsocketApiRateLimit;
1320    use serde_json::{Value, json};
1321    use tokio::spawn;
1322    use tokio::sync::mpsc::{UnboundedReceiver, unbounded_channel};
1323    use tokio::time::{Duration, timeout};
1324    use tokio_tungstenite::tungstenite::Message;
1325
1326    async fn setup() -> (
1327        Arc<WebsocketApi>,
1328        Arc<WebsocketConnection>,
1329        UnboundedReceiver<Message>,
1330    ) {
1331        let conn = WebsocketConnection::new("test-conn");
1332        let (tx, rx) = unbounded_channel::<Message>();
1333        {
1334            let mut conn_state = conn.state.lock().await;
1335            conn_state.ws_write_tx = Some(tx);
1336        }
1337
1338        let config = ConfigurationWebsocketApi::builder()
1339            .api_key("key")
1340            .api_secret("secret")
1341            .build()
1342            .expect("Failed to build configuration");
1343        let ws_api = WebsocketApi::new(config, vec![conn.clone()]);
1344        conn.set_handler(ws_api.clone() as Arc<dyn WebsocketHandler>)
1345            .await;
1346        ws_api.clone().connect().await.unwrap();
1347
1348        (ws_api, conn, rx)
1349    }
1350
1351    #[test]
1352    fn account_commission_success() {
1353        TOKIO_SHARED_RT.block_on(async {
1354            let (ws_api, conn, mut rx) = setup().await;
1355            let client = AccountApiClient::new(ws_api.clone());
1356
1357            let handle = spawn(async move {
1358                let params = AccountCommissionParams::builder("BNBUSDT".to_string(),).build().unwrap();
1359                client.account_commission(params).await
1360            });
1361
1362            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
1363            let Message::Text(text) = sent else { panic!() };
1364            let v: Value = serde_json::from_str(&text).unwrap();
1365            let id = v["id"].as_str().unwrap();
1366            assert_eq!(v["method"], "/account.commission".trim_start_matches('/'));
1367
1368            let mut resp_json: Value = serde_json::from_str(r#"{"id":"d3df8a61-98ea-4fe0-8f4e-0fcea5d418b0","status":200,"result":{"symbol":"BTCUSDT","standardCommission":{"maker":"0.00000010","taker":"0.00000020","buyer":"0.00000030","seller":"0.00000040"},"specialCommission":{"maker":"0.01000000","taker":"0.02000000","buyer":"0.03000000","seller":"0.04000000"},"taxCommission":{"maker":"0.00000112","taker":"0.00000114","buyer":"0.00000118","seller":"0.00000116"},"discount":{"enabledForAccount":true,"enabledForSymbol":true,"discountAsset":"BNB","discount":"0.75000000"}},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
1369            resp_json["id"] = id.into();
1370
1371            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
1372            let expected_data: Box<models::AccountCommissionResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
1373            let empty_array = Value::Array(vec![]);
1374            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
1375            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
1376                match raw_rate_limits.as_array() {
1377                    Some(arr) if arr.is_empty() => None,
1378                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
1379                    None => None,
1380                };
1381
1382            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1383
1384            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
1385
1386
1387            let response_rate_limits = response.rate_limits.clone();
1388            let response_data = response.data().expect("deserialize data");
1389
1390            assert_eq!(response_rate_limits, expected_rate_limits);
1391            assert_eq!(response_data, expected_data);
1392        });
1393    }
1394
1395    #[test]
1396    fn account_commission_error_response() {
1397        TOKIO_SHARED_RT.block_on(async {
1398            let (ws_api, conn, mut rx) = setup().await;
1399            let client = AccountApiClient::new(ws_api.clone());
1400
1401            let handle = tokio::spawn(async move {
1402                let params = AccountCommissionParams::builder("BNBUSDT".to_string(),).build().unwrap();
1403                client.account_commission(params).await
1404            });
1405
1406            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
1407            let Message::Text(text) = sent else { panic!() };
1408            let v: Value = serde_json::from_str(&text).unwrap();
1409            let id = v["id"].as_str().unwrap().to_string();
1410
1411            let resp_json = json!({
1412                "id": id,
1413                "status": 400,
1414                    "error": {
1415                        "code": -2010,
1416                        "msg": "Account has insufficient balance for requested action.",
1417                    },
1418                    "rateLimits": [
1419                        {
1420                            "rateLimitType": "ORDERS",
1421                            "interval": "SECOND",
1422                            "intervalNum": 10,
1423                            "limit": 50,
1424                            "count": 13
1425                        },
1426                    ],
1427            });
1428            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1429
1430            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
1431            match join {
1432                Ok(Err(e)) => {
1433                    let msg = e.to_string();
1434                    assert!(
1435                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
1436                        "Expected error msg to contain server error, got: {msg}"
1437                    );
1438                }
1439                Ok(Ok(_)) => panic!("Expected error"),
1440                Err(_) => panic!("Task panicked"),
1441            }
1442        });
1443    }
1444
1445    #[test]
1446    fn account_commission_request_timeout() {
1447        TOKIO_SHARED_RT.block_on(async {
1448            let (ws_api, _conn, mut rx) = setup().await;
1449            let client = AccountApiClient::new(ws_api.clone());
1450
1451            let handle = spawn(async move {
1452                let params = AccountCommissionParams::builder("BNBUSDT".to_string())
1453                    .build()
1454                    .unwrap();
1455                client.account_commission(params).await
1456            });
1457
1458            let sent = timeout(Duration::from_secs(1), rx.recv())
1459                .await
1460                .expect("send should occur")
1461                .expect("channel closed");
1462            let Message::Text(text) = sent else {
1463                panic!("expected Message Text")
1464            };
1465
1466            let _: Value = serde_json::from_str(&text).unwrap();
1467
1468            let result = handle.await.expect("task completed");
1469            match result {
1470                Err(e) => {
1471                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
1472                        assert!(matches!(inner, WebsocketError::Timeout));
1473                    } else {
1474                        panic!("Unexpected error type: {:?}", e);
1475                    }
1476                }
1477                Ok(_) => panic!("Expected timeout error"),
1478            }
1479        });
1480    }
1481
1482    #[test]
1483    fn account_rate_limits_orders_success() {
1484        TOKIO_SHARED_RT.block_on(async {
1485            let (ws_api, conn, mut rx) = setup().await;
1486            let client = AccountApiClient::new(ws_api.clone());
1487
1488            let handle = spawn(async move {
1489                let params = AccountRateLimitsOrdersParams::builder().build().unwrap();
1490                client.account_rate_limits_orders(params).await
1491            });
1492
1493            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
1494            let Message::Text(text) = sent else { panic!() };
1495            let v: Value = serde_json::from_str(&text).unwrap();
1496            let id = v["id"].as_str().unwrap();
1497            assert_eq!(v["method"], "/account.rateLimits.orders".trim_start_matches('/'));
1498
1499            let mut resp_json: Value = serde_json::from_str(r#"{"id":"d3783d8d-f8d1-4d2c-b8a0-b7596af5a664","status":200,"result":[{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":0},{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":0}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":40}]}"#).unwrap();
1500            resp_json["id"] = id.into();
1501
1502            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
1503            let expected_data: Vec<models::AccountRateLimitsOrdersResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
1504            let empty_array = Value::Array(vec![]);
1505            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
1506            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
1507                match raw_rate_limits.as_array() {
1508                    Some(arr) if arr.is_empty() => None,
1509                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
1510                    None => None,
1511                };
1512
1513            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1514
1515            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
1516
1517
1518            let response_rate_limits = response.rate_limits.clone();
1519            let response_data = response.data().expect("deserialize data");
1520
1521            assert_eq!(response_rate_limits, expected_rate_limits);
1522            assert_eq!(response_data, expected_data);
1523        });
1524    }
1525
1526    #[test]
1527    fn account_rate_limits_orders_error_response() {
1528        TOKIO_SHARED_RT.block_on(async {
1529            let (ws_api, conn, mut rx) = setup().await;
1530            let client = AccountApiClient::new(ws_api.clone());
1531
1532            let handle = tokio::spawn(async move {
1533                let params = AccountRateLimitsOrdersParams::builder().build().unwrap();
1534                client.account_rate_limits_orders(params).await
1535            });
1536
1537            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
1538            let Message::Text(text) = sent else { panic!() };
1539            let v: Value = serde_json::from_str(&text).unwrap();
1540            let id = v["id"].as_str().unwrap().to_string();
1541
1542            let resp_json = json!({
1543                "id": id,
1544                "status": 400,
1545                    "error": {
1546                        "code": -2010,
1547                        "msg": "Account has insufficient balance for requested action.",
1548                    },
1549                    "rateLimits": [
1550                        {
1551                            "rateLimitType": "ORDERS",
1552                            "interval": "SECOND",
1553                            "intervalNum": 10,
1554                            "limit": 50,
1555                            "count": 13
1556                        },
1557                    ],
1558            });
1559            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1560
1561            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
1562            match join {
1563                Ok(Err(e)) => {
1564                    let msg = e.to_string();
1565                    assert!(
1566                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
1567                        "Expected error msg to contain server error, got: {msg}"
1568                    );
1569                }
1570                Ok(Ok(_)) => panic!("Expected error"),
1571                Err(_) => panic!("Task panicked"),
1572            }
1573        });
1574    }
1575
1576    #[test]
1577    fn account_rate_limits_orders_request_timeout() {
1578        TOKIO_SHARED_RT.block_on(async {
1579            let (ws_api, _conn, mut rx) = setup().await;
1580            let client = AccountApiClient::new(ws_api.clone());
1581
1582            let handle = spawn(async move {
1583                let params = AccountRateLimitsOrdersParams::builder().build().unwrap();
1584                client.account_rate_limits_orders(params).await
1585            });
1586
1587            let sent = timeout(Duration::from_secs(1), rx.recv())
1588                .await
1589                .expect("send should occur")
1590                .expect("channel closed");
1591            let Message::Text(text) = sent else {
1592                panic!("expected Message Text")
1593            };
1594
1595            let _: Value = serde_json::from_str(&text).unwrap();
1596
1597            let result = handle.await.expect("task completed");
1598            match result {
1599                Err(e) => {
1600                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
1601                        assert!(matches!(inner, WebsocketError::Timeout));
1602                    } else {
1603                        panic!("Unexpected error type: {:?}", e);
1604                    }
1605                }
1606                Ok(_) => panic!("Expected timeout error"),
1607            }
1608        });
1609    }
1610
1611    #[test]
1612    fn account_status_success() {
1613        TOKIO_SHARED_RT.block_on(async {
1614            let (ws_api, conn, mut rx) = setup().await;
1615            let client = AccountApiClient::new(ws_api.clone());
1616
1617            let handle = spawn(async move {
1618                let params = AccountStatusParams::builder().build().unwrap();
1619                client.account_status(params).await
1620            });
1621
1622            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
1623            let Message::Text(text) = sent else { panic!() };
1624            let v: Value = serde_json::from_str(&text).unwrap();
1625            let id = v["id"].as_str().unwrap();
1626            assert_eq!(v["method"], "/account.status".trim_start_matches('/'));
1627
1628            let mut resp_json: Value = serde_json::from_str(r#"{"id":"605a6d20-6588-4cb9-afa0-b0ab087507ba","status":200,"result":{"makerCommission":15,"takerCommission":15,"buyerCommission":0,"sellerCommission":0,"canTrade":true,"canWithdraw":true,"canDeposit":true,"commissionRates":{"maker":"0.00150000","taker":"0.00150000","buyer":"0.00000000","seller":"0.00000000"},"brokered":false,"requireSelfTradePrevention":false,"preventSor":false,"updateTime":1660801833000,"accountType":"SPOT","balances":[{"asset":"USDT","free":"1021.21000000","locked":"0.00000000"},{"asset":"BTC","free":"1.3447112","locked":"0.08600000"},{"asset":"BNB","free":"0.00000000","locked":"0.00000000"}],"permissions":["SPOT"],"uid":354937868},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
1629            resp_json["id"] = id.into();
1630
1631            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
1632            let expected_data: Box<models::AccountStatusResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
1633            let empty_array = Value::Array(vec![]);
1634            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
1635            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
1636                match raw_rate_limits.as_array() {
1637                    Some(arr) if arr.is_empty() => None,
1638                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
1639                    None => None,
1640                };
1641
1642            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1643
1644            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
1645
1646
1647            let response_rate_limits = response.rate_limits.clone();
1648            let response_data = response.data().expect("deserialize data");
1649
1650            assert_eq!(response_rate_limits, expected_rate_limits);
1651            assert_eq!(response_data, expected_data);
1652        });
1653    }
1654
1655    #[test]
1656    fn account_status_error_response() {
1657        TOKIO_SHARED_RT.block_on(async {
1658            let (ws_api, conn, mut rx) = setup().await;
1659            let client = AccountApiClient::new(ws_api.clone());
1660
1661            let handle = tokio::spawn(async move {
1662                let params = AccountStatusParams::builder().build().unwrap();
1663                client.account_status(params).await
1664            });
1665
1666            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
1667            let Message::Text(text) = sent else { panic!() };
1668            let v: Value = serde_json::from_str(&text).unwrap();
1669            let id = v["id"].as_str().unwrap().to_string();
1670
1671            let resp_json = json!({
1672                "id": id,
1673                "status": 400,
1674                    "error": {
1675                        "code": -2010,
1676                        "msg": "Account has insufficient balance for requested action.",
1677                    },
1678                    "rateLimits": [
1679                        {
1680                            "rateLimitType": "ORDERS",
1681                            "interval": "SECOND",
1682                            "intervalNum": 10,
1683                            "limit": 50,
1684                            "count": 13
1685                        },
1686                    ],
1687            });
1688            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1689
1690            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
1691            match join {
1692                Ok(Err(e)) => {
1693                    let msg = e.to_string();
1694                    assert!(
1695                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
1696                        "Expected error msg to contain server error, got: {msg}"
1697                    );
1698                }
1699                Ok(Ok(_)) => panic!("Expected error"),
1700                Err(_) => panic!("Task panicked"),
1701            }
1702        });
1703    }
1704
1705    #[test]
1706    fn account_status_request_timeout() {
1707        TOKIO_SHARED_RT.block_on(async {
1708            let (ws_api, _conn, mut rx) = setup().await;
1709            let client = AccountApiClient::new(ws_api.clone());
1710
1711            let handle = spawn(async move {
1712                let params = AccountStatusParams::builder().build().unwrap();
1713                client.account_status(params).await
1714            });
1715
1716            let sent = timeout(Duration::from_secs(1), rx.recv())
1717                .await
1718                .expect("send should occur")
1719                .expect("channel closed");
1720            let Message::Text(text) = sent else {
1721                panic!("expected Message Text")
1722            };
1723
1724            let _: Value = serde_json::from_str(&text).unwrap();
1725
1726            let result = handle.await.expect("task completed");
1727            match result {
1728                Err(e) => {
1729                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
1730                        assert!(matches!(inner, WebsocketError::Timeout));
1731                    } else {
1732                        panic!("Unexpected error type: {:?}", e);
1733                    }
1734                }
1735                Ok(_) => panic!("Expected timeout error"),
1736            }
1737        });
1738    }
1739
1740    #[test]
1741    fn all_order_lists_success() {
1742        TOKIO_SHARED_RT.block_on(async {
1743            let (ws_api, conn, mut rx) = setup().await;
1744            let client = AccountApiClient::new(ws_api.clone());
1745
1746            let handle = spawn(async move {
1747                let params = AllOrderListsParams::builder().build().unwrap();
1748                client.all_order_lists(params).await
1749            });
1750
1751            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
1752            let Message::Text(text) = sent else { panic!() };
1753            let v: Value = serde_json::from_str(&text).unwrap();
1754            let id = v["id"].as_str().unwrap();
1755            assert_eq!(v["method"], "/allOrderLists".trim_start_matches('/'));
1756
1757            let mut resp_json: Value = serde_json::from_str(r#"{"id":"8617b7b3-1b3d-4dec-94cd-eefd929b8ceb","status":200,"result":[{"orderListId":1274512,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569138902,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us"},{"symbol":"BTCUSDT","orderId":12569138901,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU"}]}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
1758            resp_json["id"] = id.into();
1759
1760            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
1761            let expected_data: Vec<models::AllOrderListsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
1762            let empty_array = Value::Array(vec![]);
1763            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
1764            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
1765                match raw_rate_limits.as_array() {
1766                    Some(arr) if arr.is_empty() => None,
1767                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
1768                    None => None,
1769                };
1770
1771            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1772
1773            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
1774
1775
1776            let response_rate_limits = response.rate_limits.clone();
1777            let response_data = response.data().expect("deserialize data");
1778
1779            assert_eq!(response_rate_limits, expected_rate_limits);
1780            assert_eq!(response_data, expected_data);
1781        });
1782    }
1783
1784    #[test]
1785    fn all_order_lists_error_response() {
1786        TOKIO_SHARED_RT.block_on(async {
1787            let (ws_api, conn, mut rx) = setup().await;
1788            let client = AccountApiClient::new(ws_api.clone());
1789
1790            let handle = tokio::spawn(async move {
1791                let params = AllOrderListsParams::builder().build().unwrap();
1792                client.all_order_lists(params).await
1793            });
1794
1795            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
1796            let Message::Text(text) = sent else { panic!() };
1797            let v: Value = serde_json::from_str(&text).unwrap();
1798            let id = v["id"].as_str().unwrap().to_string();
1799
1800            let resp_json = json!({
1801                "id": id,
1802                "status": 400,
1803                    "error": {
1804                        "code": -2010,
1805                        "msg": "Account has insufficient balance for requested action.",
1806                    },
1807                    "rateLimits": [
1808                        {
1809                            "rateLimitType": "ORDERS",
1810                            "interval": "SECOND",
1811                            "intervalNum": 10,
1812                            "limit": 50,
1813                            "count": 13
1814                        },
1815                    ],
1816            });
1817            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1818
1819            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
1820            match join {
1821                Ok(Err(e)) => {
1822                    let msg = e.to_string();
1823                    assert!(
1824                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
1825                        "Expected error msg to contain server error, got: {msg}"
1826                    );
1827                }
1828                Ok(Ok(_)) => panic!("Expected error"),
1829                Err(_) => panic!("Task panicked"),
1830            }
1831        });
1832    }
1833
1834    #[test]
1835    fn all_order_lists_request_timeout() {
1836        TOKIO_SHARED_RT.block_on(async {
1837            let (ws_api, _conn, mut rx) = setup().await;
1838            let client = AccountApiClient::new(ws_api.clone());
1839
1840            let handle = spawn(async move {
1841                let params = AllOrderListsParams::builder().build().unwrap();
1842                client.all_order_lists(params).await
1843            });
1844
1845            let sent = timeout(Duration::from_secs(1), rx.recv())
1846                .await
1847                .expect("send should occur")
1848                .expect("channel closed");
1849            let Message::Text(text) = sent else {
1850                panic!("expected Message Text")
1851            };
1852
1853            let _: Value = serde_json::from_str(&text).unwrap();
1854
1855            let result = handle.await.expect("task completed");
1856            match result {
1857                Err(e) => {
1858                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
1859                        assert!(matches!(inner, WebsocketError::Timeout));
1860                    } else {
1861                        panic!("Unexpected error type: {:?}", e);
1862                    }
1863                }
1864                Ok(_) => panic!("Expected timeout error"),
1865            }
1866        });
1867    }
1868
1869    #[test]
1870    fn all_orders_success() {
1871        TOKIO_SHARED_RT.block_on(async {
1872            let (ws_api, conn, mut rx) = setup().await;
1873            let client = AccountApiClient::new(ws_api.clone());
1874
1875            let handle = spawn(async move {
1876                let params = AllOrdersParams::builder("BNBUSDT".to_string(),).build().unwrap();
1877                client.all_orders(params).await
1878            });
1879
1880            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
1881            let Message::Text(text) = sent else { panic!() };
1882            let v: Value = serde_json::from_str(&text).unwrap();
1883            let id = v["id"].as_str().unwrap();
1884            assert_eq!(v["method"], "/allOrders".trim_start_matches('/'));
1885
1886            let mut resp_json: Value = serde_json::from_str(r#"{"id":"734235c2-13d2-4574-be68-723e818c08f3","status":200,"result":[{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157","price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00847000","cummulativeQuoteQty":"198.33521500","status":"FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1660801715639,"updateTime":1660801717945,"isWorking":true,"workingTime":1660801715639,"origQuoteOrderQty":"0.00000000","selfTradePreventionMode":"NONE","preventedMatchId":0,"preventedQuantity":"1.200000"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
1887            resp_json["id"] = id.into();
1888
1889            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
1890            let expected_data: Vec<models::AllOrdersResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
1891            let empty_array = Value::Array(vec![]);
1892            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
1893            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
1894                match raw_rate_limits.as_array() {
1895                    Some(arr) if arr.is_empty() => None,
1896                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
1897                    None => None,
1898                };
1899
1900            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1901
1902            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
1903
1904
1905            let response_rate_limits = response.rate_limits.clone();
1906            let response_data = response.data().expect("deserialize data");
1907
1908            assert_eq!(response_rate_limits, expected_rate_limits);
1909            assert_eq!(response_data, expected_data);
1910        });
1911    }
1912
1913    #[test]
1914    fn all_orders_error_response() {
1915        TOKIO_SHARED_RT.block_on(async {
1916            let (ws_api, conn, mut rx) = setup().await;
1917            let client = AccountApiClient::new(ws_api.clone());
1918
1919            let handle = tokio::spawn(async move {
1920                let params = AllOrdersParams::builder("BNBUSDT".to_string(),).build().unwrap();
1921                client.all_orders(params).await
1922            });
1923
1924            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
1925            let Message::Text(text) = sent else { panic!() };
1926            let v: Value = serde_json::from_str(&text).unwrap();
1927            let id = v["id"].as_str().unwrap().to_string();
1928
1929            let resp_json = json!({
1930                "id": id,
1931                "status": 400,
1932                    "error": {
1933                        "code": -2010,
1934                        "msg": "Account has insufficient balance for requested action.",
1935                    },
1936                    "rateLimits": [
1937                        {
1938                            "rateLimitType": "ORDERS",
1939                            "interval": "SECOND",
1940                            "intervalNum": 10,
1941                            "limit": 50,
1942                            "count": 13
1943                        },
1944                    ],
1945            });
1946            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
1947
1948            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
1949            match join {
1950                Ok(Err(e)) => {
1951                    let msg = e.to_string();
1952                    assert!(
1953                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
1954                        "Expected error msg to contain server error, got: {msg}"
1955                    );
1956                }
1957                Ok(Ok(_)) => panic!("Expected error"),
1958                Err(_) => panic!("Task panicked"),
1959            }
1960        });
1961    }
1962
1963    #[test]
1964    fn all_orders_request_timeout() {
1965        TOKIO_SHARED_RT.block_on(async {
1966            let (ws_api, _conn, mut rx) = setup().await;
1967            let client = AccountApiClient::new(ws_api.clone());
1968
1969            let handle = spawn(async move {
1970                let params = AllOrdersParams::builder("BNBUSDT".to_string())
1971                    .build()
1972                    .unwrap();
1973                client.all_orders(params).await
1974            });
1975
1976            let sent = timeout(Duration::from_secs(1), rx.recv())
1977                .await
1978                .expect("send should occur")
1979                .expect("channel closed");
1980            let Message::Text(text) = sent else {
1981                panic!("expected Message Text")
1982            };
1983
1984            let _: Value = serde_json::from_str(&text).unwrap();
1985
1986            let result = handle.await.expect("task completed");
1987            match result {
1988                Err(e) => {
1989                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
1990                        assert!(matches!(inner, WebsocketError::Timeout));
1991                    } else {
1992                        panic!("Unexpected error type: {:?}", e);
1993                    }
1994                }
1995                Ok(_) => panic!("Expected timeout error"),
1996            }
1997        });
1998    }
1999
2000    #[test]
2001    fn my_allocations_success() {
2002        TOKIO_SHARED_RT.block_on(async {
2003            let (ws_api, conn, mut rx) = setup().await;
2004            let client = AccountApiClient::new(ws_api.clone());
2005
2006            let handle = spawn(async move {
2007                let params = MyAllocationsParams::builder("BNBUSDT".to_string(),).build().unwrap();
2008                client.my_allocations(params).await
2009            });
2010
2011            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2012            let Message::Text(text) = sent else { panic!() };
2013            let v: Value = serde_json::from_str(&text).unwrap();
2014            let id = v["id"].as_str().unwrap();
2015            assert_eq!(v["method"], "/myAllocations".trim_start_matches('/'));
2016
2017            let mut resp_json: Value = serde_json::from_str(r#"{"id":"g4ce6a53-a39d-4f71-823b-4ab5r391d6y8","status":200,"result":[{"symbol":"BTCUSDT","allocationId":0,"allocationType":"SOR","orderId":500,"orderListId":-1,"price":"1.00000000","qty":"0.10000000","quoteQty":"0.10000000","commission":"0.00000000","commissionAsset":"BTC","time":1687319487614,"isBuyer":false,"isMaker":false,"isAllocator":false}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
2018            resp_json["id"] = id.into();
2019
2020            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2021            let expected_data: Vec<models::MyAllocationsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2022            let empty_array = Value::Array(vec![]);
2023            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2024            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2025                match raw_rate_limits.as_array() {
2026                    Some(arr) if arr.is_empty() => None,
2027                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2028                    None => None,
2029                };
2030
2031            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2032
2033            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2034
2035
2036            let response_rate_limits = response.rate_limits.clone();
2037            let response_data = response.data().expect("deserialize data");
2038
2039            assert_eq!(response_rate_limits, expected_rate_limits);
2040            assert_eq!(response_data, expected_data);
2041        });
2042    }
2043
2044    #[test]
2045    fn my_allocations_error_response() {
2046        TOKIO_SHARED_RT.block_on(async {
2047            let (ws_api, conn, mut rx) = setup().await;
2048            let client = AccountApiClient::new(ws_api.clone());
2049
2050            let handle = tokio::spawn(async move {
2051                let params = MyAllocationsParams::builder("BNBUSDT".to_string(),).build().unwrap();
2052                client.my_allocations(params).await
2053            });
2054
2055            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2056            let Message::Text(text) = sent else { panic!() };
2057            let v: Value = serde_json::from_str(&text).unwrap();
2058            let id = v["id"].as_str().unwrap().to_string();
2059
2060            let resp_json = json!({
2061                "id": id,
2062                "status": 400,
2063                    "error": {
2064                        "code": -2010,
2065                        "msg": "Account has insufficient balance for requested action.",
2066                    },
2067                    "rateLimits": [
2068                        {
2069                            "rateLimitType": "ORDERS",
2070                            "interval": "SECOND",
2071                            "intervalNum": 10,
2072                            "limit": 50,
2073                            "count": 13
2074                        },
2075                    ],
2076            });
2077            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2078
2079            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2080            match join {
2081                Ok(Err(e)) => {
2082                    let msg = e.to_string();
2083                    assert!(
2084                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2085                        "Expected error msg to contain server error, got: {msg}"
2086                    );
2087                }
2088                Ok(Ok(_)) => panic!("Expected error"),
2089                Err(_) => panic!("Task panicked"),
2090            }
2091        });
2092    }
2093
2094    #[test]
2095    fn my_allocations_request_timeout() {
2096        TOKIO_SHARED_RT.block_on(async {
2097            let (ws_api, _conn, mut rx) = setup().await;
2098            let client = AccountApiClient::new(ws_api.clone());
2099
2100            let handle = spawn(async move {
2101                let params = MyAllocationsParams::builder("BNBUSDT".to_string())
2102                    .build()
2103                    .unwrap();
2104                client.my_allocations(params).await
2105            });
2106
2107            let sent = timeout(Duration::from_secs(1), rx.recv())
2108                .await
2109                .expect("send should occur")
2110                .expect("channel closed");
2111            let Message::Text(text) = sent else {
2112                panic!("expected Message Text")
2113            };
2114
2115            let _: Value = serde_json::from_str(&text).unwrap();
2116
2117            let result = handle.await.expect("task completed");
2118            match result {
2119                Err(e) => {
2120                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2121                        assert!(matches!(inner, WebsocketError::Timeout));
2122                    } else {
2123                        panic!("Unexpected error type: {:?}", e);
2124                    }
2125                }
2126                Ok(_) => panic!("Expected timeout error"),
2127            }
2128        });
2129    }
2130
2131    #[test]
2132    fn my_filters_success() {
2133        TOKIO_SHARED_RT.block_on(async {
2134            let (ws_api, conn, mut rx) = setup().await;
2135            let client = AccountApiClient::new(ws_api.clone());
2136
2137            let handle = spawn(async move {
2138                let params = MyFiltersParams::builder("BNBUSDT".to_string(),).build().unwrap();
2139                client.my_filters(params).await
2140            });
2141
2142            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2143            let Message::Text(text) = sent else { panic!() };
2144            let v: Value = serde_json::from_str(&text).unwrap();
2145            let id = v["id"].as_str().unwrap();
2146            assert_eq!(v["method"], "/myFilters".trim_start_matches('/'));
2147
2148            let mut resp_json: Value = serde_json::from_str(r#"{"id":"1758009606869","status":200,"result":{"exchangeFilters":[{"filterType":"EXCHANGE_MAX_NUM_ORDERS","maxNumOrders":1000}],"symbolFilters":[{"filterType":"MAX_NUM_ORDER_LISTS","maxNumOrderLists":20}],"assetFilters":[{"filterType":"MAX_ASSET","asset":"JPY","limit":"1000000.00000000"}]},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000},{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000},{"rateLimitType":"RAW_REQUESTS","interval":"MINUTE","intervalNum":5,"limit":61000}]}"#).unwrap();
2149            resp_json["id"] = id.into();
2150
2151            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2152            let expected_data: Box<models::MyFiltersResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2153            let empty_array = Value::Array(vec![]);
2154            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2155            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2156                match raw_rate_limits.as_array() {
2157                    Some(arr) if arr.is_empty() => None,
2158                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2159                    None => None,
2160                };
2161
2162            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2163
2164            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2165
2166
2167            let response_rate_limits = response.rate_limits.clone();
2168            let response_data = response.data().expect("deserialize data");
2169
2170            assert_eq!(response_rate_limits, expected_rate_limits);
2171            assert_eq!(response_data, expected_data);
2172        });
2173    }
2174
2175    #[test]
2176    fn my_filters_error_response() {
2177        TOKIO_SHARED_RT.block_on(async {
2178            let (ws_api, conn, mut rx) = setup().await;
2179            let client = AccountApiClient::new(ws_api.clone());
2180
2181            let handle = tokio::spawn(async move {
2182                let params = MyFiltersParams::builder("BNBUSDT".to_string(),).build().unwrap();
2183                client.my_filters(params).await
2184            });
2185
2186            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2187            let Message::Text(text) = sent else { panic!() };
2188            let v: Value = serde_json::from_str(&text).unwrap();
2189            let id = v["id"].as_str().unwrap().to_string();
2190
2191            let resp_json = json!({
2192                "id": id,
2193                "status": 400,
2194                    "error": {
2195                        "code": -2010,
2196                        "msg": "Account has insufficient balance for requested action.",
2197                    },
2198                    "rateLimits": [
2199                        {
2200                            "rateLimitType": "ORDERS",
2201                            "interval": "SECOND",
2202                            "intervalNum": 10,
2203                            "limit": 50,
2204                            "count": 13
2205                        },
2206                    ],
2207            });
2208            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2209
2210            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2211            match join {
2212                Ok(Err(e)) => {
2213                    let msg = e.to_string();
2214                    assert!(
2215                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2216                        "Expected error msg to contain server error, got: {msg}"
2217                    );
2218                }
2219                Ok(Ok(_)) => panic!("Expected error"),
2220                Err(_) => panic!("Task panicked"),
2221            }
2222        });
2223    }
2224
2225    #[test]
2226    fn my_filters_request_timeout() {
2227        TOKIO_SHARED_RT.block_on(async {
2228            let (ws_api, _conn, mut rx) = setup().await;
2229            let client = AccountApiClient::new(ws_api.clone());
2230
2231            let handle = spawn(async move {
2232                let params = MyFiltersParams::builder("BNBUSDT".to_string())
2233                    .build()
2234                    .unwrap();
2235                client.my_filters(params).await
2236            });
2237
2238            let sent = timeout(Duration::from_secs(1), rx.recv())
2239                .await
2240                .expect("send should occur")
2241                .expect("channel closed");
2242            let Message::Text(text) = sent else {
2243                panic!("expected Message Text")
2244            };
2245
2246            let _: Value = serde_json::from_str(&text).unwrap();
2247
2248            let result = handle.await.expect("task completed");
2249            match result {
2250                Err(e) => {
2251                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2252                        assert!(matches!(inner, WebsocketError::Timeout));
2253                    } else {
2254                        panic!("Unexpected error type: {:?}", e);
2255                    }
2256                }
2257                Ok(_) => panic!("Expected timeout error"),
2258            }
2259        });
2260    }
2261
2262    #[test]
2263    fn my_prevented_matches_success() {
2264        TOKIO_SHARED_RT.block_on(async {
2265            let (ws_api, conn, mut rx) = setup().await;
2266            let client = AccountApiClient::new(ws_api.clone());
2267
2268            let handle = spawn(async move {
2269                let params = MyPreventedMatchesParams::builder("BNBUSDT".to_string(),).build().unwrap();
2270                client.my_prevented_matches(params).await
2271            });
2272
2273            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2274            let Message::Text(text) = sent else { panic!() };
2275            let v: Value = serde_json::from_str(&text).unwrap();
2276            let id = v["id"].as_str().unwrap();
2277            assert_eq!(v["method"], "/myPreventedMatches".trim_start_matches('/'));
2278
2279            let mut resp_json: Value = serde_json::from_str(r#"{"id":"g4ce6a53-a39d-4f71-823b-4ab5r391d6y8","status":200,"result":[{"symbol":"BTCUSDT","preventedMatchId":1,"takerOrderId":5,"makerSymbol":"BTCUSDT","makerOrderId":3,"tradeGroupId":1,"selfTradePreventionMode":"EXPIRE_MAKER","price":"1.100000","makerPreventedQuantity":"1.300000","transactTime":1669101687094}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
2280            resp_json["id"] = id.into();
2281
2282            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2283            let expected_data: Vec<models::MyPreventedMatchesResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2284            let empty_array = Value::Array(vec![]);
2285            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2286            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2287                match raw_rate_limits.as_array() {
2288                    Some(arr) if arr.is_empty() => None,
2289                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2290                    None => None,
2291                };
2292
2293            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2294
2295            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2296
2297
2298            let response_rate_limits = response.rate_limits.clone();
2299            let response_data = response.data().expect("deserialize data");
2300
2301            assert_eq!(response_rate_limits, expected_rate_limits);
2302            assert_eq!(response_data, expected_data);
2303        });
2304    }
2305
2306    #[test]
2307    fn my_prevented_matches_error_response() {
2308        TOKIO_SHARED_RT.block_on(async {
2309            let (ws_api, conn, mut rx) = setup().await;
2310            let client = AccountApiClient::new(ws_api.clone());
2311
2312            let handle = tokio::spawn(async move {
2313                let params = MyPreventedMatchesParams::builder("BNBUSDT".to_string(),).build().unwrap();
2314                client.my_prevented_matches(params).await
2315            });
2316
2317            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2318            let Message::Text(text) = sent else { panic!() };
2319            let v: Value = serde_json::from_str(&text).unwrap();
2320            let id = v["id"].as_str().unwrap().to_string();
2321
2322            let resp_json = json!({
2323                "id": id,
2324                "status": 400,
2325                    "error": {
2326                        "code": -2010,
2327                        "msg": "Account has insufficient balance for requested action.",
2328                    },
2329                    "rateLimits": [
2330                        {
2331                            "rateLimitType": "ORDERS",
2332                            "interval": "SECOND",
2333                            "intervalNum": 10,
2334                            "limit": 50,
2335                            "count": 13
2336                        },
2337                    ],
2338            });
2339            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2340
2341            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2342            match join {
2343                Ok(Err(e)) => {
2344                    let msg = e.to_string();
2345                    assert!(
2346                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2347                        "Expected error msg to contain server error, got: {msg}"
2348                    );
2349                }
2350                Ok(Ok(_)) => panic!("Expected error"),
2351                Err(_) => panic!("Task panicked"),
2352            }
2353        });
2354    }
2355
2356    #[test]
2357    fn my_prevented_matches_request_timeout() {
2358        TOKIO_SHARED_RT.block_on(async {
2359            let (ws_api, _conn, mut rx) = setup().await;
2360            let client = AccountApiClient::new(ws_api.clone());
2361
2362            let handle = spawn(async move {
2363                let params = MyPreventedMatchesParams::builder("BNBUSDT".to_string())
2364                    .build()
2365                    .unwrap();
2366                client.my_prevented_matches(params).await
2367            });
2368
2369            let sent = timeout(Duration::from_secs(1), rx.recv())
2370                .await
2371                .expect("send should occur")
2372                .expect("channel closed");
2373            let Message::Text(text) = sent else {
2374                panic!("expected Message Text")
2375            };
2376
2377            let _: Value = serde_json::from_str(&text).unwrap();
2378
2379            let result = handle.await.expect("task completed");
2380            match result {
2381                Err(e) => {
2382                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2383                        assert!(matches!(inner, WebsocketError::Timeout));
2384                    } else {
2385                        panic!("Unexpected error type: {:?}", e);
2386                    }
2387                }
2388                Ok(_) => panic!("Expected timeout error"),
2389            }
2390        });
2391    }
2392
2393    #[test]
2394    fn my_trades_success() {
2395        TOKIO_SHARED_RT.block_on(async {
2396            let (ws_api, conn, mut rx) = setup().await;
2397            let client = AccountApiClient::new(ws_api.clone());
2398
2399            let handle = spawn(async move {
2400                let params = MyTradesParams::builder("BNBUSDT".to_string(),).build().unwrap();
2401                client.my_trades(params).await
2402            });
2403
2404            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2405            let Message::Text(text) = sent else { panic!() };
2406            let v: Value = serde_json::from_str(&text).unwrap();
2407            let id = v["id"].as_str().unwrap();
2408            assert_eq!(v["method"], "/myTrades".trim_start_matches('/'));
2409
2410            let mut resp_json: Value = serde_json::from_str(r#"{"id":"f4ce6a53-a29d-4f70-823b-4ab59391d6e8","status":200,"result":[{"symbol":"BTCUSDT","id":1650422482,"orderId":12569099453,"orderListId":-1,"price":"23416.50000000","qty":"0.00212000","quoteQty":"49.64298000","commission":"0.00000000","commissionAsset":"BNB","time":1660801715793,"isBuyer":false,"isMaker":true,"isBestMatch":true},{"symbol":"BTCUSDT","id":1650422481,"orderId":12569099453,"orderListId":-1,"price":"23416.10000000","qty":"0.00635000","quoteQty":"148.69223500","commission":"0.00000000","commissionAsset":"BNB","time":1660801715793,"isBuyer":false,"isMaker":true,"isBestMatch":true}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
2411            resp_json["id"] = id.into();
2412
2413            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2414            let expected_data: Vec<models::MyTradesResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2415            let empty_array = Value::Array(vec![]);
2416            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2417            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2418                match raw_rate_limits.as_array() {
2419                    Some(arr) if arr.is_empty() => None,
2420                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2421                    None => None,
2422                };
2423
2424            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2425
2426            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2427
2428
2429            let response_rate_limits = response.rate_limits.clone();
2430            let response_data = response.data().expect("deserialize data");
2431
2432            assert_eq!(response_rate_limits, expected_rate_limits);
2433            assert_eq!(response_data, expected_data);
2434        });
2435    }
2436
2437    #[test]
2438    fn my_trades_error_response() {
2439        TOKIO_SHARED_RT.block_on(async {
2440            let (ws_api, conn, mut rx) = setup().await;
2441            let client = AccountApiClient::new(ws_api.clone());
2442
2443            let handle = tokio::spawn(async move {
2444                let params = MyTradesParams::builder("BNBUSDT".to_string(),).build().unwrap();
2445                client.my_trades(params).await
2446            });
2447
2448            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2449            let Message::Text(text) = sent else { panic!() };
2450            let v: Value = serde_json::from_str(&text).unwrap();
2451            let id = v["id"].as_str().unwrap().to_string();
2452
2453            let resp_json = json!({
2454                "id": id,
2455                "status": 400,
2456                    "error": {
2457                        "code": -2010,
2458                        "msg": "Account has insufficient balance for requested action.",
2459                    },
2460                    "rateLimits": [
2461                        {
2462                            "rateLimitType": "ORDERS",
2463                            "interval": "SECOND",
2464                            "intervalNum": 10,
2465                            "limit": 50,
2466                            "count": 13
2467                        },
2468                    ],
2469            });
2470            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2471
2472            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2473            match join {
2474                Ok(Err(e)) => {
2475                    let msg = e.to_string();
2476                    assert!(
2477                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2478                        "Expected error msg to contain server error, got: {msg}"
2479                    );
2480                }
2481                Ok(Ok(_)) => panic!("Expected error"),
2482                Err(_) => panic!("Task panicked"),
2483            }
2484        });
2485    }
2486
2487    #[test]
2488    fn my_trades_request_timeout() {
2489        TOKIO_SHARED_RT.block_on(async {
2490            let (ws_api, _conn, mut rx) = setup().await;
2491            let client = AccountApiClient::new(ws_api.clone());
2492
2493            let handle = spawn(async move {
2494                let params = MyTradesParams::builder("BNBUSDT".to_string())
2495                    .build()
2496                    .unwrap();
2497                client.my_trades(params).await
2498            });
2499
2500            let sent = timeout(Duration::from_secs(1), rx.recv())
2501                .await
2502                .expect("send should occur")
2503                .expect("channel closed");
2504            let Message::Text(text) = sent else {
2505                panic!("expected Message Text")
2506            };
2507
2508            let _: Value = serde_json::from_str(&text).unwrap();
2509
2510            let result = handle.await.expect("task completed");
2511            match result {
2512                Err(e) => {
2513                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2514                        assert!(matches!(inner, WebsocketError::Timeout));
2515                    } else {
2516                        panic!("Unexpected error type: {:?}", e);
2517                    }
2518                }
2519                Ok(_) => panic!("Expected timeout error"),
2520            }
2521        });
2522    }
2523
2524    #[test]
2525    fn open_order_lists_status_success() {
2526        TOKIO_SHARED_RT.block_on(async {
2527            let (ws_api, conn, mut rx) = setup().await;
2528            let client = AccountApiClient::new(ws_api.clone());
2529
2530            let handle = spawn(async move {
2531                let params = OpenOrderListsStatusParams::builder().build().unwrap();
2532                client.open_order_lists_status(params).await
2533            });
2534
2535            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2536            let Message::Text(text) = sent else { panic!() };
2537            let v: Value = serde_json::from_str(&text).unwrap();
2538            let id = v["id"].as_str().unwrap();
2539            assert_eq!(v["method"], "/openOrderLists.status".trim_start_matches('/'));
2540
2541            let mut resp_json: Value = serde_json::from_str(r#"{"id":"3a4437e2-41a3-4c19-897c-9cadc5dce8b6","status":200,"result":[{"orderListId":0,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":5,"clientOrderId":"1ZqG7bBuYwaF4SU8CwnwHm"},{"symbol":"BTCUSDT","orderId":4,"clientOrderId":"CUhLgTXnX5n2c0gWiLpV4d"}]}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":6}]}"#).unwrap();
2542            resp_json["id"] = id.into();
2543
2544            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2545            let expected_data: Vec<models::OpenOrderListsStatusResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2546            let empty_array = Value::Array(vec![]);
2547            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2548            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2549                match raw_rate_limits.as_array() {
2550                    Some(arr) if arr.is_empty() => None,
2551                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2552                    None => None,
2553                };
2554
2555            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2556
2557            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2558
2559
2560            let response_rate_limits = response.rate_limits.clone();
2561            let response_data = response.data().expect("deserialize data");
2562
2563            assert_eq!(response_rate_limits, expected_rate_limits);
2564            assert_eq!(response_data, expected_data);
2565        });
2566    }
2567
2568    #[test]
2569    fn open_order_lists_status_error_response() {
2570        TOKIO_SHARED_RT.block_on(async {
2571            let (ws_api, conn, mut rx) = setup().await;
2572            let client = AccountApiClient::new(ws_api.clone());
2573
2574            let handle = tokio::spawn(async move {
2575                let params = OpenOrderListsStatusParams::builder().build().unwrap();
2576                client.open_order_lists_status(params).await
2577            });
2578
2579            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2580            let Message::Text(text) = sent else { panic!() };
2581            let v: Value = serde_json::from_str(&text).unwrap();
2582            let id = v["id"].as_str().unwrap().to_string();
2583
2584            let resp_json = json!({
2585                "id": id,
2586                "status": 400,
2587                    "error": {
2588                        "code": -2010,
2589                        "msg": "Account has insufficient balance for requested action.",
2590                    },
2591                    "rateLimits": [
2592                        {
2593                            "rateLimitType": "ORDERS",
2594                            "interval": "SECOND",
2595                            "intervalNum": 10,
2596                            "limit": 50,
2597                            "count": 13
2598                        },
2599                    ],
2600            });
2601            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2602
2603            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2604            match join {
2605                Ok(Err(e)) => {
2606                    let msg = e.to_string();
2607                    assert!(
2608                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2609                        "Expected error msg to contain server error, got: {msg}"
2610                    );
2611                }
2612                Ok(Ok(_)) => panic!("Expected error"),
2613                Err(_) => panic!("Task panicked"),
2614            }
2615        });
2616    }
2617
2618    #[test]
2619    fn open_order_lists_status_request_timeout() {
2620        TOKIO_SHARED_RT.block_on(async {
2621            let (ws_api, _conn, mut rx) = setup().await;
2622            let client = AccountApiClient::new(ws_api.clone());
2623
2624            let handle = spawn(async move {
2625                let params = OpenOrderListsStatusParams::builder().build().unwrap();
2626                client.open_order_lists_status(params).await
2627            });
2628
2629            let sent = timeout(Duration::from_secs(1), rx.recv())
2630                .await
2631                .expect("send should occur")
2632                .expect("channel closed");
2633            let Message::Text(text) = sent else {
2634                panic!("expected Message Text")
2635            };
2636
2637            let _: Value = serde_json::from_str(&text).unwrap();
2638
2639            let result = handle.await.expect("task completed");
2640            match result {
2641                Err(e) => {
2642                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2643                        assert!(matches!(inner, WebsocketError::Timeout));
2644                    } else {
2645                        panic!("Unexpected error type: {:?}", e);
2646                    }
2647                }
2648                Ok(_) => panic!("Expected timeout error"),
2649            }
2650        });
2651    }
2652
2653    #[test]
2654    fn open_orders_status_success() {
2655        TOKIO_SHARED_RT.block_on(async {
2656            let (ws_api, conn, mut rx) = setup().await;
2657            let client = AccountApiClient::new(ws_api.clone());
2658
2659            let handle = spawn(async move {
2660                let params = OpenOrdersStatusParams::builder().build().unwrap();
2661                client.open_orders_status(params).await
2662            });
2663
2664            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2665            let Message::Text(text) = sent else { panic!() };
2666            let v: Value = serde_json::from_str(&text).unwrap();
2667            let id = v["id"].as_str().unwrap();
2668            assert_eq!(v["method"], "/openOrders.status".trim_start_matches('/'));
2669
2670            let mut resp_json: Value = serde_json::from_str(r#"{"id":"55f07876-4f6f-4c47-87dc-43e5fff3f2e7","status":200,"result":[{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157","price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00720000","origQuoteOrderQty":"0.00000000","cummulativeQuoteQty":"172.43931000","status":"PARTIALLY_FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1660801715639,"updateTime":1660801717945,"isWorking":true,"workingTime":1660801715639,"selfTradePreventionMode":"NONE"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":6}]}"#).unwrap();
2671            resp_json["id"] = id.into();
2672
2673            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2674            let expected_data: Vec<models::OpenOrdersStatusResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2675            let empty_array = Value::Array(vec![]);
2676            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2677            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2678                match raw_rate_limits.as_array() {
2679                    Some(arr) if arr.is_empty() => None,
2680                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2681                    None => None,
2682                };
2683
2684            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2685
2686            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2687
2688
2689            let response_rate_limits = response.rate_limits.clone();
2690            let response_data = response.data().expect("deserialize data");
2691
2692            assert_eq!(response_rate_limits, expected_rate_limits);
2693            assert_eq!(response_data, expected_data);
2694        });
2695    }
2696
2697    #[test]
2698    fn open_orders_status_error_response() {
2699        TOKIO_SHARED_RT.block_on(async {
2700            let (ws_api, conn, mut rx) = setup().await;
2701            let client = AccountApiClient::new(ws_api.clone());
2702
2703            let handle = tokio::spawn(async move {
2704                let params = OpenOrdersStatusParams::builder().build().unwrap();
2705                client.open_orders_status(params).await
2706            });
2707
2708            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2709            let Message::Text(text) = sent else { panic!() };
2710            let v: Value = serde_json::from_str(&text).unwrap();
2711            let id = v["id"].as_str().unwrap().to_string();
2712
2713            let resp_json = json!({
2714                "id": id,
2715                "status": 400,
2716                    "error": {
2717                        "code": -2010,
2718                        "msg": "Account has insufficient balance for requested action.",
2719                    },
2720                    "rateLimits": [
2721                        {
2722                            "rateLimitType": "ORDERS",
2723                            "interval": "SECOND",
2724                            "intervalNum": 10,
2725                            "limit": 50,
2726                            "count": 13
2727                        },
2728                    ],
2729            });
2730            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2731
2732            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2733            match join {
2734                Ok(Err(e)) => {
2735                    let msg = e.to_string();
2736                    assert!(
2737                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2738                        "Expected error msg to contain server error, got: {msg}"
2739                    );
2740                }
2741                Ok(Ok(_)) => panic!("Expected error"),
2742                Err(_) => panic!("Task panicked"),
2743            }
2744        });
2745    }
2746
2747    #[test]
2748    fn open_orders_status_request_timeout() {
2749        TOKIO_SHARED_RT.block_on(async {
2750            let (ws_api, _conn, mut rx) = setup().await;
2751            let client = AccountApiClient::new(ws_api.clone());
2752
2753            let handle = spawn(async move {
2754                let params = OpenOrdersStatusParams::builder().build().unwrap();
2755                client.open_orders_status(params).await
2756            });
2757
2758            let sent = timeout(Duration::from_secs(1), rx.recv())
2759                .await
2760                .expect("send should occur")
2761                .expect("channel closed");
2762            let Message::Text(text) = sent else {
2763                panic!("expected Message Text")
2764            };
2765
2766            let _: Value = serde_json::from_str(&text).unwrap();
2767
2768            let result = handle.await.expect("task completed");
2769            match result {
2770                Err(e) => {
2771                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2772                        assert!(matches!(inner, WebsocketError::Timeout));
2773                    } else {
2774                        panic!("Unexpected error type: {:?}", e);
2775                    }
2776                }
2777                Ok(_) => panic!("Expected timeout error"),
2778            }
2779        });
2780    }
2781
2782    #[test]
2783    fn order_amendments_success() {
2784        TOKIO_SHARED_RT.block_on(async {
2785            let (ws_api, conn, mut rx) = setup().await;
2786            let client = AccountApiClient::new(ws_api.clone());
2787
2788            let handle = spawn(async move {
2789                let params = OrderAmendmentsParams::builder("BNBUSDT".to_string(),1,).build().unwrap();
2790                client.order_amendments(params).await
2791            });
2792
2793            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2794            let Message::Text(text) = sent else { panic!() };
2795            let v: Value = serde_json::from_str(&text).unwrap();
2796            let id = v["id"].as_str().unwrap();
2797            assert_eq!(v["method"], "/order.amendments".trim_start_matches('/'));
2798
2799            let mut resp_json: Value = serde_json::from_str(r#"{"id":"6f5ebe91-01d9-43ac-be99-57cf062e0e30","status":200,"result":[{"symbol":"BTCUSDT","orderId":23,"executionId":60,"origClientOrderId":"my_pending_order","newClientOrderId":"xbxXh5SSwaHS7oUEOCI88B","origQty":"7.00000000","newQty":"5.00000000","time":1741924229819}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":4}]}"#).unwrap();
2800            resp_json["id"] = id.into();
2801
2802            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2803            let expected_data: Vec<models::OrderAmendmentsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2804            let empty_array = Value::Array(vec![]);
2805            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2806            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2807                match raw_rate_limits.as_array() {
2808                    Some(arr) if arr.is_empty() => None,
2809                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2810                    None => None,
2811                };
2812
2813            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2814
2815            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2816
2817
2818            let response_rate_limits = response.rate_limits.clone();
2819            let response_data = response.data().expect("deserialize data");
2820
2821            assert_eq!(response_rate_limits, expected_rate_limits);
2822            assert_eq!(response_data, expected_data);
2823        });
2824    }
2825
2826    #[test]
2827    fn order_amendments_error_response() {
2828        TOKIO_SHARED_RT.block_on(async {
2829            let (ws_api, conn, mut rx) = setup().await;
2830            let client = AccountApiClient::new(ws_api.clone());
2831
2832            let handle = tokio::spawn(async move {
2833                let params = OrderAmendmentsParams::builder("BNBUSDT".to_string(),1,).build().unwrap();
2834                client.order_amendments(params).await
2835            });
2836
2837            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2838            let Message::Text(text) = sent else { panic!() };
2839            let v: Value = serde_json::from_str(&text).unwrap();
2840            let id = v["id"].as_str().unwrap().to_string();
2841
2842            let resp_json = json!({
2843                "id": id,
2844                "status": 400,
2845                    "error": {
2846                        "code": -2010,
2847                        "msg": "Account has insufficient balance for requested action.",
2848                    },
2849                    "rateLimits": [
2850                        {
2851                            "rateLimitType": "ORDERS",
2852                            "interval": "SECOND",
2853                            "intervalNum": 10,
2854                            "limit": 50,
2855                            "count": 13
2856                        },
2857                    ],
2858            });
2859            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2860
2861            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2862            match join {
2863                Ok(Err(e)) => {
2864                    let msg = e.to_string();
2865                    assert!(
2866                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2867                        "Expected error msg to contain server error, got: {msg}"
2868                    );
2869                }
2870                Ok(Ok(_)) => panic!("Expected error"),
2871                Err(_) => panic!("Task panicked"),
2872            }
2873        });
2874    }
2875
2876    #[test]
2877    fn order_amendments_request_timeout() {
2878        TOKIO_SHARED_RT.block_on(async {
2879            let (ws_api, _conn, mut rx) = setup().await;
2880            let client = AccountApiClient::new(ws_api.clone());
2881
2882            let handle = spawn(async move {
2883                let params = OrderAmendmentsParams::builder("BNBUSDT".to_string(), 1)
2884                    .build()
2885                    .unwrap();
2886                client.order_amendments(params).await
2887            });
2888
2889            let sent = timeout(Duration::from_secs(1), rx.recv())
2890                .await
2891                .expect("send should occur")
2892                .expect("channel closed");
2893            let Message::Text(text) = sent else {
2894                panic!("expected Message Text")
2895            };
2896
2897            let _: Value = serde_json::from_str(&text).unwrap();
2898
2899            let result = handle.await.expect("task completed");
2900            match result {
2901                Err(e) => {
2902                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
2903                        assert!(matches!(inner, WebsocketError::Timeout));
2904                    } else {
2905                        panic!("Unexpected error type: {:?}", e);
2906                    }
2907                }
2908                Ok(_) => panic!("Expected timeout error"),
2909            }
2910        });
2911    }
2912
2913    #[test]
2914    fn order_list_status_success() {
2915        TOKIO_SHARED_RT.block_on(async {
2916            let (ws_api, conn, mut rx) = setup().await;
2917            let client = AccountApiClient::new(ws_api.clone());
2918
2919            let handle = spawn(async move {
2920                let params = OrderListStatusParams::builder().build().unwrap();
2921                client.order_list_status(params).await
2922            });
2923
2924            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
2925            let Message::Text(text) = sent else { panic!() };
2926            let v: Value = serde_json::from_str(&text).unwrap();
2927            let id = v["id"].as_str().unwrap();
2928            assert_eq!(v["method"], "/orderList.status".trim_start_matches('/'));
2929
2930            let mut resp_json: Value = serde_json::from_str(r#"{"id":"b53fd5ff-82c7-4a04-bd64-5f9dc42c2100","status":200,"result":{"orderListId":1274512,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569138902,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us"},{"symbol":"BTCUSDT","orderId":12569138901,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU"}]},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":4}]}"#).unwrap();
2931            resp_json["id"] = id.into();
2932
2933            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
2934            let expected_data: Box<models::AllOrderListsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
2935            let empty_array = Value::Array(vec![]);
2936            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
2937            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
2938                match raw_rate_limits.as_array() {
2939                    Some(arr) if arr.is_empty() => None,
2940                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
2941                    None => None,
2942                };
2943
2944            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2945
2946            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
2947
2948
2949            let response_rate_limits = response.rate_limits.clone();
2950            let response_data = response.data().expect("deserialize data");
2951
2952            assert_eq!(response_rate_limits, expected_rate_limits);
2953            assert_eq!(response_data, expected_data);
2954        });
2955    }
2956
2957    #[test]
2958    fn order_list_status_error_response() {
2959        TOKIO_SHARED_RT.block_on(async {
2960            let (ws_api, conn, mut rx) = setup().await;
2961            let client = AccountApiClient::new(ws_api.clone());
2962
2963            let handle = tokio::spawn(async move {
2964                let params = OrderListStatusParams::builder().build().unwrap();
2965                client.order_list_status(params).await
2966            });
2967
2968            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
2969            let Message::Text(text) = sent else { panic!() };
2970            let v: Value = serde_json::from_str(&text).unwrap();
2971            let id = v["id"].as_str().unwrap().to_string();
2972
2973            let resp_json = json!({
2974                "id": id,
2975                "status": 400,
2976                    "error": {
2977                        "code": -2010,
2978                        "msg": "Account has insufficient balance for requested action.",
2979                    },
2980                    "rateLimits": [
2981                        {
2982                            "rateLimitType": "ORDERS",
2983                            "interval": "SECOND",
2984                            "intervalNum": 10,
2985                            "limit": 50,
2986                            "count": 13
2987                        },
2988                    ],
2989            });
2990            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
2991
2992            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
2993            match join {
2994                Ok(Err(e)) => {
2995                    let msg = e.to_string();
2996                    assert!(
2997                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
2998                        "Expected error msg to contain server error, got: {msg}"
2999                    );
3000                }
3001                Ok(Ok(_)) => panic!("Expected error"),
3002                Err(_) => panic!("Task panicked"),
3003            }
3004        });
3005    }
3006
3007    #[test]
3008    fn order_list_status_request_timeout() {
3009        TOKIO_SHARED_RT.block_on(async {
3010            let (ws_api, _conn, mut rx) = setup().await;
3011            let client = AccountApiClient::new(ws_api.clone());
3012
3013            let handle = spawn(async move {
3014                let params = OrderListStatusParams::builder().build().unwrap();
3015                client.order_list_status(params).await
3016            });
3017
3018            let sent = timeout(Duration::from_secs(1), rx.recv())
3019                .await
3020                .expect("send should occur")
3021                .expect("channel closed");
3022            let Message::Text(text) = sent else {
3023                panic!("expected Message Text")
3024            };
3025
3026            let _: Value = serde_json::from_str(&text).unwrap();
3027
3028            let result = handle.await.expect("task completed");
3029            match result {
3030                Err(e) => {
3031                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
3032                        assert!(matches!(inner, WebsocketError::Timeout));
3033                    } else {
3034                        panic!("Unexpected error type: {:?}", e);
3035                    }
3036                }
3037                Ok(_) => panic!("Expected timeout error"),
3038            }
3039        });
3040    }
3041
3042    #[test]
3043    fn order_status_success() {
3044        TOKIO_SHARED_RT.block_on(async {
3045            let (ws_api, conn, mut rx) = setup().await;
3046            let client = AccountApiClient::new(ws_api.clone());
3047
3048            let handle = spawn(async move {
3049                let params = OrderStatusParams::builder("BNBUSDT".to_string(),).build().unwrap();
3050                client.order_status(params).await
3051            });
3052
3053            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
3054            let Message::Text(text) = sent else { panic!() };
3055            let v: Value = serde_json::from_str(&text).unwrap();
3056            let id = v["id"].as_str().unwrap();
3057            assert_eq!(v["method"], "/order.status".trim_start_matches('/'));
3058
3059            let mut resp_json: Value = serde_json::from_str(r#"{"id":"aa62318a-5a97-4f3b-bdc7-640bbe33b291","status":200,"result":{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157","price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00847000","cummulativeQuoteQty":"198.33521500","status":"FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","trailingDelta":10,"trailingTime":-1,"icebergQty":"0.00000000","time":1660801715639,"updateTime":1660801717945,"isWorking":true,"workingTime":1660801715639,"origQuoteOrderQty":"0.00000000","strategyId":37463720,"strategyType":1000000,"selfTradePreventionMode":"NONE","preventedMatchId":0,"preventedQuantity":"1.200000"},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":4}]}"#).unwrap();
3060            resp_json["id"] = id.into();
3061
3062            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
3063            let expected_data: Box<models::OrderStatusResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
3064            let empty_array = Value::Array(vec![]);
3065            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
3066            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
3067                match raw_rate_limits.as_array() {
3068                    Some(arr) if arr.is_empty() => None,
3069                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
3070                    None => None,
3071                };
3072
3073            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
3074
3075            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");
3076
3077
3078            let response_rate_limits = response.rate_limits.clone();
3079            let response_data = response.data().expect("deserialize data");
3080
3081            assert_eq!(response_rate_limits, expected_rate_limits);
3082            assert_eq!(response_data, expected_data);
3083        });
3084    }
3085
3086    #[test]
3087    fn order_status_error_response() {
3088        TOKIO_SHARED_RT.block_on(async {
3089            let (ws_api, conn, mut rx) = setup().await;
3090            let client = AccountApiClient::new(ws_api.clone());
3091
3092            let handle = tokio::spawn(async move {
3093                let params = OrderStatusParams::builder("BNBUSDT".to_string(),).build().unwrap();
3094                client.order_status(params).await
3095            });
3096
3097            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
3098            let Message::Text(text) = sent else { panic!() };
3099            let v: Value = serde_json::from_str(&text).unwrap();
3100            let id = v["id"].as_str().unwrap().to_string();
3101
3102            let resp_json = json!({
3103                "id": id,
3104                "status": 400,
3105                    "error": {
3106                        "code": -2010,
3107                        "msg": "Account has insufficient balance for requested action.",
3108                    },
3109                    "rateLimits": [
3110                        {
3111                            "rateLimitType": "ORDERS",
3112                            "interval": "SECOND",
3113                            "intervalNum": 10,
3114                            "limit": 50,
3115                            "count": 13
3116                        },
3117                    ],
3118            });
3119            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;
3120
3121            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
3122            match join {
3123                Ok(Err(e)) => {
3124                    let msg = e.to_string();
3125                    assert!(
3126                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
3127                        "Expected error msg to contain server error, got: {msg}"
3128                    );
3129                }
3130                Ok(Ok(_)) => panic!("Expected error"),
3131                Err(_) => panic!("Task panicked"),
3132            }
3133        });
3134    }
3135
3136    #[test]
3137    fn order_status_request_timeout() {
3138        TOKIO_SHARED_RT.block_on(async {
3139            let (ws_api, _conn, mut rx) = setup().await;
3140            let client = AccountApiClient::new(ws_api.clone());
3141
3142            let handle = spawn(async move {
3143                let params = OrderStatusParams::builder("BNBUSDT".to_string())
3144                    .build()
3145                    .unwrap();
3146                client.order_status(params).await
3147            });
3148
3149            let sent = timeout(Duration::from_secs(1), rx.recv())
3150                .await
3151                .expect("send should occur")
3152                .expect("channel closed");
3153            let Message::Text(text) = sent else {
3154                panic!("expected Message Text")
3155            };
3156
3157            let _: Value = serde_json::from_str(&text).unwrap();
3158
3159            let result = handle.await.expect("task completed");
3160            match result {
3161                Err(e) => {
3162                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
3163                        assert!(matches!(inner, WebsocketError::Timeout));
3164                    } else {
3165                        panic!("Unexpected error type: {:?}", e);
3166                    }
3167                }
3168                Ok(_) => panic!("Expected timeout error"),
3169            }
3170        });
3171    }
3172}