1#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24 config::ConfigurationRestApi,
25 models::{ParamBuildError, RestApiResponse},
26 utils::send_request,
27};
28use crate::wallet::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait AccountApi: Send + Sync {
34 async fn account_api_trading_status(
35 &self,
36 params: AccountApiTradingStatusParams,
37 ) -> anyhow::Result<RestApiResponse<models::AccountApiTradingStatusResponse>>;
38 async fn account_info(
39 &self,
40 params: AccountInfoParams,
41 ) -> anyhow::Result<RestApiResponse<models::AccountInfoResponse>>;
42 async fn account_status(
43 &self,
44 params: AccountStatusParams,
45 ) -> anyhow::Result<RestApiResponse<models::AccountStatusResponse>>;
46 async fn daily_account_snapshot(
47 &self,
48 params: DailyAccountSnapshotParams,
49 ) -> anyhow::Result<RestApiResponse<models::DailyAccountSnapshotResponse>>;
50 async fn disable_fast_withdraw_switch(
51 &self,
52 params: DisableFastWithdrawSwitchParams,
53 ) -> anyhow::Result<RestApiResponse<Value>>;
54 async fn enable_fast_withdraw_switch(
55 &self,
56 params: EnableFastWithdrawSwitchParams,
57 ) -> anyhow::Result<RestApiResponse<Value>>;
58 async fn get_api_key_permission(
59 &self,
60 params: GetApiKeyPermissionParams,
61 ) -> anyhow::Result<RestApiResponse<models::GetApiKeyPermissionResponse>>;
62}
63
64#[derive(Debug, Clone)]
65pub struct AccountApiClient {
66 configuration: ConfigurationRestApi,
67}
68
69impl AccountApiClient {
70 pub fn new(configuration: ConfigurationRestApi) -> Self {
71 Self { configuration }
72 }
73}
74
75#[derive(Clone, Debug, Builder, Default)]
80#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
81pub struct AccountApiTradingStatusParams {
82 #[builder(setter(into), default)]
87 pub recv_window: Option<i64>,
88}
89
90impl AccountApiTradingStatusParams {
91 #[must_use]
94 pub fn builder() -> AccountApiTradingStatusParamsBuilder {
95 AccountApiTradingStatusParamsBuilder::default()
96 }
97}
98#[derive(Clone, Debug, Builder, Default)]
103#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
104pub struct AccountInfoParams {
105 #[builder(setter(into), default)]
110 pub recv_window: Option<i64>,
111}
112
113impl AccountInfoParams {
114 #[must_use]
117 pub fn builder() -> AccountInfoParamsBuilder {
118 AccountInfoParamsBuilder::default()
119 }
120}
121#[derive(Clone, Debug, Builder, Default)]
126#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
127pub struct AccountStatusParams {
128 #[builder(setter(into), default)]
133 pub recv_window: Option<i64>,
134}
135
136impl AccountStatusParams {
137 #[must_use]
140 pub fn builder() -> AccountStatusParamsBuilder {
141 AccountStatusParamsBuilder::default()
142 }
143}
144#[derive(Clone, Debug, Builder)]
149#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
150pub struct DailyAccountSnapshotParams {
151 #[builder(setter(into))]
156 pub r#type: String,
157 #[builder(setter(into), default)]
162 pub start_time: Option<i64>,
163 #[builder(setter(into), default)]
168 pub end_time: Option<i64>,
169 #[builder(setter(into), default)]
173 pub limit: Option<i64>,
174 #[builder(setter(into), default)]
179 pub recv_window: Option<i64>,
180}
181
182impl DailyAccountSnapshotParams {
183 #[must_use]
190 pub fn builder(r#type: String) -> DailyAccountSnapshotParamsBuilder {
191 DailyAccountSnapshotParamsBuilder::default().r#type(r#type)
192 }
193}
194#[derive(Clone, Debug, Builder, Default)]
199#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
200pub struct DisableFastWithdrawSwitchParams {
201 #[builder(setter(into), default)]
206 pub recv_window: Option<i64>,
207}
208
209impl DisableFastWithdrawSwitchParams {
210 #[must_use]
213 pub fn builder() -> DisableFastWithdrawSwitchParamsBuilder {
214 DisableFastWithdrawSwitchParamsBuilder::default()
215 }
216}
217#[derive(Clone, Debug, Builder, Default)]
222#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
223pub struct EnableFastWithdrawSwitchParams {
224 #[builder(setter(into), default)]
229 pub recv_window: Option<i64>,
230}
231
232impl EnableFastWithdrawSwitchParams {
233 #[must_use]
236 pub fn builder() -> EnableFastWithdrawSwitchParamsBuilder {
237 EnableFastWithdrawSwitchParamsBuilder::default()
238 }
239}
240#[derive(Clone, Debug, Builder, Default)]
245#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
246pub struct GetApiKeyPermissionParams {
247 #[builder(setter(into), default)]
252 pub recv_window: Option<i64>,
253}
254
255impl GetApiKeyPermissionParams {
256 #[must_use]
259 pub fn builder() -> GetApiKeyPermissionParamsBuilder {
260 GetApiKeyPermissionParamsBuilder::default()
261 }
262}
263
264#[async_trait]
265impl AccountApi for AccountApiClient {
266 async fn account_api_trading_status(
267 &self,
268 params: AccountApiTradingStatusParams,
269 ) -> anyhow::Result<RestApiResponse<models::AccountApiTradingStatusResponse>> {
270 let AccountApiTradingStatusParams { recv_window } = params;
271
272 let mut query_params = BTreeMap::new();
273 let body_params = BTreeMap::new();
274
275 if let Some(rw) = recv_window {
276 query_params.insert("recvWindow".to_string(), json!(rw));
277 }
278
279 send_request::<models::AccountApiTradingStatusResponse>(
280 &self.configuration,
281 "/sapi/v1/account/apiTradingStatus",
282 reqwest::Method::GET,
283 query_params,
284 body_params,
285 if HAS_TIME_UNIT {
286 self.configuration.time_unit
287 } else {
288 None
289 },
290 true,
291 )
292 .await
293 }
294
295 async fn account_info(
296 &self,
297 params: AccountInfoParams,
298 ) -> anyhow::Result<RestApiResponse<models::AccountInfoResponse>> {
299 let AccountInfoParams { recv_window } = params;
300
301 let mut query_params = BTreeMap::new();
302 let body_params = BTreeMap::new();
303
304 if let Some(rw) = recv_window {
305 query_params.insert("recvWindow".to_string(), json!(rw));
306 }
307
308 send_request::<models::AccountInfoResponse>(
309 &self.configuration,
310 "/sapi/v1/account/info",
311 reqwest::Method::GET,
312 query_params,
313 body_params,
314 if HAS_TIME_UNIT {
315 self.configuration.time_unit
316 } else {
317 None
318 },
319 true,
320 )
321 .await
322 }
323
324 async fn account_status(
325 &self,
326 params: AccountStatusParams,
327 ) -> anyhow::Result<RestApiResponse<models::AccountStatusResponse>> {
328 let AccountStatusParams { recv_window } = params;
329
330 let mut query_params = BTreeMap::new();
331 let body_params = BTreeMap::new();
332
333 if let Some(rw) = recv_window {
334 query_params.insert("recvWindow".to_string(), json!(rw));
335 }
336
337 send_request::<models::AccountStatusResponse>(
338 &self.configuration,
339 "/sapi/v1/account/status",
340 reqwest::Method::GET,
341 query_params,
342 body_params,
343 if HAS_TIME_UNIT {
344 self.configuration.time_unit
345 } else {
346 None
347 },
348 true,
349 )
350 .await
351 }
352
353 async fn daily_account_snapshot(
354 &self,
355 params: DailyAccountSnapshotParams,
356 ) -> anyhow::Result<RestApiResponse<models::DailyAccountSnapshotResponse>> {
357 let DailyAccountSnapshotParams {
358 r#type,
359 start_time,
360 end_time,
361 limit,
362 recv_window,
363 } = params;
364
365 let mut query_params = BTreeMap::new();
366 let body_params = BTreeMap::new();
367
368 query_params.insert("type".to_string(), json!(r#type));
369
370 if let Some(rw) = start_time {
371 query_params.insert("startTime".to_string(), json!(rw));
372 }
373
374 if let Some(rw) = end_time {
375 query_params.insert("endTime".to_string(), json!(rw));
376 }
377
378 if let Some(rw) = limit {
379 query_params.insert("limit".to_string(), json!(rw));
380 }
381
382 if let Some(rw) = recv_window {
383 query_params.insert("recvWindow".to_string(), json!(rw));
384 }
385
386 send_request::<models::DailyAccountSnapshotResponse>(
387 &self.configuration,
388 "/sapi/v1/accountSnapshot",
389 reqwest::Method::GET,
390 query_params,
391 body_params,
392 if HAS_TIME_UNIT {
393 self.configuration.time_unit
394 } else {
395 None
396 },
397 true,
398 )
399 .await
400 }
401
402 async fn disable_fast_withdraw_switch(
403 &self,
404 params: DisableFastWithdrawSwitchParams,
405 ) -> anyhow::Result<RestApiResponse<Value>> {
406 let DisableFastWithdrawSwitchParams { recv_window } = params;
407
408 let mut query_params = BTreeMap::new();
409 let body_params = BTreeMap::new();
410
411 if let Some(rw) = recv_window {
412 query_params.insert("recvWindow".to_string(), json!(rw));
413 }
414
415 send_request::<Value>(
416 &self.configuration,
417 "/sapi/v1/account/disableFastWithdrawSwitch",
418 reqwest::Method::POST,
419 query_params,
420 body_params,
421 if HAS_TIME_UNIT {
422 self.configuration.time_unit
423 } else {
424 None
425 },
426 true,
427 )
428 .await
429 }
430
431 async fn enable_fast_withdraw_switch(
432 &self,
433 params: EnableFastWithdrawSwitchParams,
434 ) -> anyhow::Result<RestApiResponse<Value>> {
435 let EnableFastWithdrawSwitchParams { recv_window } = params;
436
437 let mut query_params = BTreeMap::new();
438 let body_params = BTreeMap::new();
439
440 if let Some(rw) = recv_window {
441 query_params.insert("recvWindow".to_string(), json!(rw));
442 }
443
444 send_request::<Value>(
445 &self.configuration,
446 "/sapi/v1/account/enableFastWithdrawSwitch",
447 reqwest::Method::POST,
448 query_params,
449 body_params,
450 if HAS_TIME_UNIT {
451 self.configuration.time_unit
452 } else {
453 None
454 },
455 true,
456 )
457 .await
458 }
459
460 async fn get_api_key_permission(
461 &self,
462 params: GetApiKeyPermissionParams,
463 ) -> anyhow::Result<RestApiResponse<models::GetApiKeyPermissionResponse>> {
464 let GetApiKeyPermissionParams { recv_window } = params;
465
466 let mut query_params = BTreeMap::new();
467 let body_params = BTreeMap::new();
468
469 if let Some(rw) = recv_window {
470 query_params.insert("recvWindow".to_string(), json!(rw));
471 }
472
473 send_request::<models::GetApiKeyPermissionResponse>(
474 &self.configuration,
475 "/sapi/v1/account/apiRestrictions",
476 reqwest::Method::GET,
477 query_params,
478 body_params,
479 if HAS_TIME_UNIT {
480 self.configuration.time_unit
481 } else {
482 None
483 },
484 true,
485 )
486 .await
487 }
488}
489
490#[cfg(all(test, feature = "wallet"))]
491mod tests {
492 use super::*;
493 use crate::TOKIO_SHARED_RT;
494 use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
495 use async_trait::async_trait;
496 use std::collections::HashMap;
497
498 struct DummyRestApiResponse<T> {
499 inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
500 status: u16,
501 headers: HashMap<String, String>,
502 rate_limits: Option<Vec<RestApiRateLimit>>,
503 }
504
505 impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
506 fn from(dummy: DummyRestApiResponse<T>) -> Self {
507 Self {
508 data_fn: dummy.inner,
509 status: dummy.status,
510 headers: dummy.headers,
511 rate_limits: dummy.rate_limits,
512 }
513 }
514 }
515
516 struct MockAccountApiClient {
517 force_error: bool,
518 }
519
520 #[async_trait]
521 impl AccountApi for MockAccountApiClient {
522 async fn account_api_trading_status(
523 &self,
524 _params: AccountApiTradingStatusParams,
525 ) -> anyhow::Result<RestApiResponse<models::AccountApiTradingStatusResponse>> {
526 if self.force_error {
527 return Err(ConnectorError::ConnectorClientError {
528 msg: "ResponseError".to_string(),
529 code: None,
530 }
531 .into());
532 }
533
534 let resp_json: Value = serde_json::from_str(r#"{"data":{"isLocked":false,"plannedRecoverTime":0,"triggerCondition":{"GCR":150,"IFER":150,"UFR":300},"updateTime":1547630471725}}"#).unwrap();
535 let dummy_response: models::AccountApiTradingStatusResponse =
536 serde_json::from_value(resp_json.clone())
537 .expect("should parse into models::AccountApiTradingStatusResponse");
538
539 let dummy = DummyRestApiResponse {
540 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
541 status: 200,
542 headers: HashMap::new(),
543 rate_limits: None,
544 };
545
546 Ok(dummy.into())
547 }
548
549 async fn account_info(
550 &self,
551 _params: AccountInfoParams,
552 ) -> anyhow::Result<RestApiResponse<models::AccountInfoResponse>> {
553 if self.force_error {
554 return Err(ConnectorError::ConnectorClientError {
555 msg: "ResponseError".to_string(),
556 code: None,
557 }
558 .into());
559 }
560
561 let resp_json: Value = serde_json::from_str(r#"{"vipLevel":0,"isMarginEnabled":true,"isFutureEnabled":true,"isOptionsEnabled":true,"isPortfolioMarginRetailEnabled":true}"#).unwrap();
562 let dummy_response: models::AccountInfoResponse =
563 serde_json::from_value(resp_json.clone())
564 .expect("should parse into models::AccountInfoResponse");
565
566 let dummy = DummyRestApiResponse {
567 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
568 status: 200,
569 headers: HashMap::new(),
570 rate_limits: None,
571 };
572
573 Ok(dummy.into())
574 }
575
576 async fn account_status(
577 &self,
578 _params: AccountStatusParams,
579 ) -> anyhow::Result<RestApiResponse<models::AccountStatusResponse>> {
580 if self.force_error {
581 return Err(ConnectorError::ConnectorClientError {
582 msg: "ResponseError".to_string(),
583 code: None,
584 }
585 .into());
586 }
587
588 let resp_json: Value = serde_json::from_str(r#"{"data":"Normal"}"#).unwrap();
589 let dummy_response: models::AccountStatusResponse =
590 serde_json::from_value(resp_json.clone())
591 .expect("should parse into models::AccountStatusResponse");
592
593 let dummy = DummyRestApiResponse {
594 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
595 status: 200,
596 headers: HashMap::new(),
597 rate_limits: None,
598 };
599
600 Ok(dummy.into())
601 }
602
603 async fn daily_account_snapshot(
604 &self,
605 _params: DailyAccountSnapshotParams,
606 ) -> anyhow::Result<RestApiResponse<models::DailyAccountSnapshotResponse>> {
607 if self.force_error {
608 return Err(ConnectorError::ConnectorClientError {
609 msg: "ResponseError".to_string(),
610 code: None,
611 }
612 .into());
613 }
614
615 let resp_json: Value = serde_json::from_str(r#"{"code":200,"msg":"","snapshotVos":[{"data":{"balances":[{"asset":"BTC","free":"0.09905021","locked":"0.00000000"},{"asset":"USDT","free":"1.89109409","locked":"0.00000000"}],"totalAssetOfBtc":"0.09942700"},"type":"spot","updateTime":1576281599000},{"data":{"marginLevel":"2748.02909813","totalAssetOfBtc":"0.00274803","totalLiabilityOfBtc":"0.00000100","totalNetAssetOfBtc":"0.00274750","userAssets":[{"asset":"XRP","borrowed":"0.00000000","free":"1.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"1.00000000"}]},"type":"margin","updateTime":1576281599000},{"data":{"assets":[{"asset":"USDT","marginBalance":"118.99782335","walletBalance":"120.23811389"}],"position":[{"entryPrice":"7130.41000000","markPrice":"7257.66239673","positionAmt":"0.01000000","symbol":"BTCUSDT","unRealizedProfit":"1.24029054"}]},"type":"futures","updateTime":1576281599000}]}"#).unwrap();
616 let dummy_response: models::DailyAccountSnapshotResponse =
617 serde_json::from_value(resp_json.clone())
618 .expect("should parse into models::DailyAccountSnapshotResponse");
619
620 let dummy = DummyRestApiResponse {
621 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
622 status: 200,
623 headers: HashMap::new(),
624 rate_limits: None,
625 };
626
627 Ok(dummy.into())
628 }
629
630 async fn disable_fast_withdraw_switch(
631 &self,
632 _params: DisableFastWithdrawSwitchParams,
633 ) -> anyhow::Result<RestApiResponse<Value>> {
634 if self.force_error {
635 return Err(ConnectorError::ConnectorClientError {
636 msg: "ResponseError".to_string(),
637 code: None,
638 }
639 .into());
640 }
641
642 let dummy_response = Value::Null;
643
644 let dummy = DummyRestApiResponse {
645 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
646 status: 200,
647 headers: HashMap::new(),
648 rate_limits: None,
649 };
650
651 Ok(dummy.into())
652 }
653
654 async fn enable_fast_withdraw_switch(
655 &self,
656 _params: EnableFastWithdrawSwitchParams,
657 ) -> anyhow::Result<RestApiResponse<Value>> {
658 if self.force_error {
659 return Err(ConnectorError::ConnectorClientError {
660 msg: "ResponseError".to_string(),
661 code: None,
662 }
663 .into());
664 }
665
666 let dummy_response = Value::Null;
667
668 let dummy = DummyRestApiResponse {
669 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
670 status: 200,
671 headers: HashMap::new(),
672 rate_limits: None,
673 };
674
675 Ok(dummy.into())
676 }
677
678 async fn get_api_key_permission(
679 &self,
680 _params: GetApiKeyPermissionParams,
681 ) -> anyhow::Result<RestApiResponse<models::GetApiKeyPermissionResponse>> {
682 if self.force_error {
683 return Err(ConnectorError::ConnectorClientError {
684 msg: "ResponseError".to_string(),
685 code: None,
686 }
687 .into());
688 }
689
690 let resp_json: Value = serde_json::from_str(r#"{"ipRestrict":false,"createTime":1698645219000,"enableReading":true,"enableWithdrawals":false,"enableInternalTransfer":false,"enableMargin":false,"enableFutures":false,"permitsUniversalTransfer":false,"enableVanillaOptions":false,"enableFixApiTrade":false,"enableFixReadOnly":true,"enableSpotAndMarginTrading":false,"enablePortfolioMarginTrading":true}"#).unwrap();
691 let dummy_response: models::GetApiKeyPermissionResponse =
692 serde_json::from_value(resp_json.clone())
693 .expect("should parse into models::GetApiKeyPermissionResponse");
694
695 let dummy = DummyRestApiResponse {
696 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
697 status: 200,
698 headers: HashMap::new(),
699 rate_limits: None,
700 };
701
702 Ok(dummy.into())
703 }
704 }
705
706 #[test]
707 fn account_api_trading_status_required_params_success() {
708 TOKIO_SHARED_RT.block_on(async {
709 let client = MockAccountApiClient { force_error: false };
710
711 let params = AccountApiTradingStatusParams::builder().build().unwrap();
712
713 let resp_json: Value = serde_json::from_str(r#"{"data":{"isLocked":false,"plannedRecoverTime":0,"triggerCondition":{"GCR":150,"IFER":150,"UFR":300},"updateTime":1547630471725}}"#).unwrap();
714 let expected_response : models::AccountApiTradingStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountApiTradingStatusResponse");
715
716 let resp = client.account_api_trading_status(params).await.expect("Expected a response");
717 let data_future = resp.data();
718 let actual_response = data_future.await.unwrap();
719 assert_eq!(actual_response, expected_response);
720 });
721 }
722
723 #[test]
724 fn account_api_trading_status_optional_params_success() {
725 TOKIO_SHARED_RT.block_on(async {
726 let client = MockAccountApiClient { force_error: false };
727
728 let params = AccountApiTradingStatusParams::builder().recv_window(5000).build().unwrap();
729
730 let resp_json: Value = serde_json::from_str(r#"{"data":{"isLocked":false,"plannedRecoverTime":0,"triggerCondition":{"GCR":150,"IFER":150,"UFR":300},"updateTime":1547630471725}}"#).unwrap();
731 let expected_response : models::AccountApiTradingStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountApiTradingStatusResponse");
732
733 let resp = client.account_api_trading_status(params).await.expect("Expected a response");
734 let data_future = resp.data();
735 let actual_response = data_future.await.unwrap();
736 assert_eq!(actual_response, expected_response);
737 });
738 }
739
740 #[test]
741 fn account_api_trading_status_response_error() {
742 TOKIO_SHARED_RT.block_on(async {
743 let client = MockAccountApiClient { force_error: true };
744
745 let params = AccountApiTradingStatusParams::builder().build().unwrap();
746
747 match client.account_api_trading_status(params).await {
748 Ok(_) => panic!("Expected an error"),
749 Err(err) => {
750 assert_eq!(err.to_string(), "Connector client error: ResponseError");
751 }
752 }
753 });
754 }
755
756 #[test]
757 fn account_info_required_params_success() {
758 TOKIO_SHARED_RT.block_on(async {
759 let client = MockAccountApiClient { force_error: false };
760
761 let params = AccountInfoParams::builder().build().unwrap();
762
763 let resp_json: Value = serde_json::from_str(r#"{"vipLevel":0,"isMarginEnabled":true,"isFutureEnabled":true,"isOptionsEnabled":true,"isPortfolioMarginRetailEnabled":true}"#).unwrap();
764 let expected_response : models::AccountInfoResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountInfoResponse");
765
766 let resp = client.account_info(params).await.expect("Expected a response");
767 let data_future = resp.data();
768 let actual_response = data_future.await.unwrap();
769 assert_eq!(actual_response, expected_response);
770 });
771 }
772
773 #[test]
774 fn account_info_optional_params_success() {
775 TOKIO_SHARED_RT.block_on(async {
776 let client = MockAccountApiClient { force_error: false };
777
778 let params = AccountInfoParams::builder().recv_window(5000).build().unwrap();
779
780 let resp_json: Value = serde_json::from_str(r#"{"vipLevel":0,"isMarginEnabled":true,"isFutureEnabled":true,"isOptionsEnabled":true,"isPortfolioMarginRetailEnabled":true}"#).unwrap();
781 let expected_response : models::AccountInfoResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountInfoResponse");
782
783 let resp = client.account_info(params).await.expect("Expected a response");
784 let data_future = resp.data();
785 let actual_response = data_future.await.unwrap();
786 assert_eq!(actual_response, expected_response);
787 });
788 }
789
790 #[test]
791 fn account_info_response_error() {
792 TOKIO_SHARED_RT.block_on(async {
793 let client = MockAccountApiClient { force_error: true };
794
795 let params = AccountInfoParams::builder().build().unwrap();
796
797 match client.account_info(params).await {
798 Ok(_) => panic!("Expected an error"),
799 Err(err) => {
800 assert_eq!(err.to_string(), "Connector client error: ResponseError");
801 }
802 }
803 });
804 }
805
806 #[test]
807 fn account_status_required_params_success() {
808 TOKIO_SHARED_RT.block_on(async {
809 let client = MockAccountApiClient { force_error: false };
810
811 let params = AccountStatusParams::builder().build().unwrap();
812
813 let resp_json: Value = serde_json::from_str(r#"{"data":"Normal"}"#).unwrap();
814 let expected_response: models::AccountStatusResponse =
815 serde_json::from_value(resp_json.clone())
816 .expect("should parse into models::AccountStatusResponse");
817
818 let resp = client
819 .account_status(params)
820 .await
821 .expect("Expected a response");
822 let data_future = resp.data();
823 let actual_response = data_future.await.unwrap();
824 assert_eq!(actual_response, expected_response);
825 });
826 }
827
828 #[test]
829 fn account_status_optional_params_success() {
830 TOKIO_SHARED_RT.block_on(async {
831 let client = MockAccountApiClient { force_error: false };
832
833 let params = AccountStatusParams::builder()
834 .recv_window(5000)
835 .build()
836 .unwrap();
837
838 let resp_json: Value = serde_json::from_str(r#"{"data":"Normal"}"#).unwrap();
839 let expected_response: models::AccountStatusResponse =
840 serde_json::from_value(resp_json.clone())
841 .expect("should parse into models::AccountStatusResponse");
842
843 let resp = client
844 .account_status(params)
845 .await
846 .expect("Expected a response");
847 let data_future = resp.data();
848 let actual_response = data_future.await.unwrap();
849 assert_eq!(actual_response, expected_response);
850 });
851 }
852
853 #[test]
854 fn account_status_response_error() {
855 TOKIO_SHARED_RT.block_on(async {
856 let client = MockAccountApiClient { force_error: true };
857
858 let params = AccountStatusParams::builder().build().unwrap();
859
860 match client.account_status(params).await {
861 Ok(_) => panic!("Expected an error"),
862 Err(err) => {
863 assert_eq!(err.to_string(), "Connector client error: ResponseError");
864 }
865 }
866 });
867 }
868
869 #[test]
870 fn daily_account_snapshot_required_params_success() {
871 TOKIO_SHARED_RT.block_on(async {
872 let client = MockAccountApiClient { force_error: false };
873
874 let params = DailyAccountSnapshotParams::builder("r#type_example".to_string(),).build().unwrap();
875
876 let resp_json: Value = serde_json::from_str(r#"{"code":200,"msg":"","snapshotVos":[{"data":{"balances":[{"asset":"BTC","free":"0.09905021","locked":"0.00000000"},{"asset":"USDT","free":"1.89109409","locked":"0.00000000"}],"totalAssetOfBtc":"0.09942700"},"type":"spot","updateTime":1576281599000},{"data":{"marginLevel":"2748.02909813","totalAssetOfBtc":"0.00274803","totalLiabilityOfBtc":"0.00000100","totalNetAssetOfBtc":"0.00274750","userAssets":[{"asset":"XRP","borrowed":"0.00000000","free":"1.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"1.00000000"}]},"type":"margin","updateTime":1576281599000},{"data":{"assets":[{"asset":"USDT","marginBalance":"118.99782335","walletBalance":"120.23811389"}],"position":[{"entryPrice":"7130.41000000","markPrice":"7257.66239673","positionAmt":"0.01000000","symbol":"BTCUSDT","unRealizedProfit":"1.24029054"}]},"type":"futures","updateTime":1576281599000}]}"#).unwrap();
877 let expected_response : models::DailyAccountSnapshotResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DailyAccountSnapshotResponse");
878
879 let resp = client.daily_account_snapshot(params).await.expect("Expected a response");
880 let data_future = resp.data();
881 let actual_response = data_future.await.unwrap();
882 assert_eq!(actual_response, expected_response);
883 });
884 }
885
886 #[test]
887 fn daily_account_snapshot_optional_params_success() {
888 TOKIO_SHARED_RT.block_on(async {
889 let client = MockAccountApiClient { force_error: false };
890
891 let params = DailyAccountSnapshotParams::builder("r#type_example".to_string(),).start_time(1623319461670).end_time(1641782889000).limit(7).recv_window(5000).build().unwrap();
892
893 let resp_json: Value = serde_json::from_str(r#"{"code":200,"msg":"","snapshotVos":[{"data":{"balances":[{"asset":"BTC","free":"0.09905021","locked":"0.00000000"},{"asset":"USDT","free":"1.89109409","locked":"0.00000000"}],"totalAssetOfBtc":"0.09942700"},"type":"spot","updateTime":1576281599000},{"data":{"marginLevel":"2748.02909813","totalAssetOfBtc":"0.00274803","totalLiabilityOfBtc":"0.00000100","totalNetAssetOfBtc":"0.00274750","userAssets":[{"asset":"XRP","borrowed":"0.00000000","free":"1.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"1.00000000"}]},"type":"margin","updateTime":1576281599000},{"data":{"assets":[{"asset":"USDT","marginBalance":"118.99782335","walletBalance":"120.23811389"}],"position":[{"entryPrice":"7130.41000000","markPrice":"7257.66239673","positionAmt":"0.01000000","symbol":"BTCUSDT","unRealizedProfit":"1.24029054"}]},"type":"futures","updateTime":1576281599000}]}"#).unwrap();
894 let expected_response : models::DailyAccountSnapshotResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DailyAccountSnapshotResponse");
895
896 let resp = client.daily_account_snapshot(params).await.expect("Expected a response");
897 let data_future = resp.data();
898 let actual_response = data_future.await.unwrap();
899 assert_eq!(actual_response, expected_response);
900 });
901 }
902
903 #[test]
904 fn daily_account_snapshot_response_error() {
905 TOKIO_SHARED_RT.block_on(async {
906 let client = MockAccountApiClient { force_error: true };
907
908 let params = DailyAccountSnapshotParams::builder("r#type_example".to_string())
909 .build()
910 .unwrap();
911
912 match client.daily_account_snapshot(params).await {
913 Ok(_) => panic!("Expected an error"),
914 Err(err) => {
915 assert_eq!(err.to_string(), "Connector client error: ResponseError");
916 }
917 }
918 });
919 }
920
921 #[test]
922 fn disable_fast_withdraw_switch_required_params_success() {
923 TOKIO_SHARED_RT.block_on(async {
924 let client = MockAccountApiClient { force_error: false };
925
926 let params = DisableFastWithdrawSwitchParams::builder().build().unwrap();
927
928 let expected_response = Value::Null;
929
930 let resp = client
931 .disable_fast_withdraw_switch(params)
932 .await
933 .expect("Expected a response");
934 let data_future = resp.data();
935 let actual_response = data_future.await.unwrap();
936 assert_eq!(actual_response, expected_response);
937 });
938 }
939
940 #[test]
941 fn disable_fast_withdraw_switch_optional_params_success() {
942 TOKIO_SHARED_RT.block_on(async {
943 let client = MockAccountApiClient { force_error: false };
944
945 let params = DisableFastWithdrawSwitchParams::builder()
946 .recv_window(5000)
947 .build()
948 .unwrap();
949
950 let expected_response = Value::Null;
951
952 let resp = client
953 .disable_fast_withdraw_switch(params)
954 .await
955 .expect("Expected a response");
956 let data_future = resp.data();
957 let actual_response = data_future.await.unwrap();
958 assert_eq!(actual_response, expected_response);
959 });
960 }
961
962 #[test]
963 fn disable_fast_withdraw_switch_response_error() {
964 TOKIO_SHARED_RT.block_on(async {
965 let client = MockAccountApiClient { force_error: true };
966
967 let params = DisableFastWithdrawSwitchParams::builder().build().unwrap();
968
969 match client.disable_fast_withdraw_switch(params).await {
970 Ok(_) => panic!("Expected an error"),
971 Err(err) => {
972 assert_eq!(err.to_string(), "Connector client error: ResponseError");
973 }
974 }
975 });
976 }
977
978 #[test]
979 fn enable_fast_withdraw_switch_required_params_success() {
980 TOKIO_SHARED_RT.block_on(async {
981 let client = MockAccountApiClient { force_error: false };
982
983 let params = EnableFastWithdrawSwitchParams::builder().build().unwrap();
984
985 let expected_response = Value::Null;
986
987 let resp = client
988 .enable_fast_withdraw_switch(params)
989 .await
990 .expect("Expected a response");
991 let data_future = resp.data();
992 let actual_response = data_future.await.unwrap();
993 assert_eq!(actual_response, expected_response);
994 });
995 }
996
997 #[test]
998 fn enable_fast_withdraw_switch_optional_params_success() {
999 TOKIO_SHARED_RT.block_on(async {
1000 let client = MockAccountApiClient { force_error: false };
1001
1002 let params = EnableFastWithdrawSwitchParams::builder()
1003 .recv_window(5000)
1004 .build()
1005 .unwrap();
1006
1007 let expected_response = Value::Null;
1008
1009 let resp = client
1010 .enable_fast_withdraw_switch(params)
1011 .await
1012 .expect("Expected a response");
1013 let data_future = resp.data();
1014 let actual_response = data_future.await.unwrap();
1015 assert_eq!(actual_response, expected_response);
1016 });
1017 }
1018
1019 #[test]
1020 fn enable_fast_withdraw_switch_response_error() {
1021 TOKIO_SHARED_RT.block_on(async {
1022 let client = MockAccountApiClient { force_error: true };
1023
1024 let params = EnableFastWithdrawSwitchParams::builder().build().unwrap();
1025
1026 match client.enable_fast_withdraw_switch(params).await {
1027 Ok(_) => panic!("Expected an error"),
1028 Err(err) => {
1029 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1030 }
1031 }
1032 });
1033 }
1034
1035 #[test]
1036 fn get_api_key_permission_required_params_success() {
1037 TOKIO_SHARED_RT.block_on(async {
1038 let client = MockAccountApiClient { force_error: false };
1039
1040 let params = GetApiKeyPermissionParams::builder().build().unwrap();
1041
1042 let resp_json: Value = serde_json::from_str(r#"{"ipRestrict":false,"createTime":1698645219000,"enableReading":true,"enableWithdrawals":false,"enableInternalTransfer":false,"enableMargin":false,"enableFutures":false,"permitsUniversalTransfer":false,"enableVanillaOptions":false,"enableFixApiTrade":false,"enableFixReadOnly":true,"enableSpotAndMarginTrading":false,"enablePortfolioMarginTrading":true}"#).unwrap();
1043 let expected_response : models::GetApiKeyPermissionResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetApiKeyPermissionResponse");
1044
1045 let resp = client.get_api_key_permission(params).await.expect("Expected a response");
1046 let data_future = resp.data();
1047 let actual_response = data_future.await.unwrap();
1048 assert_eq!(actual_response, expected_response);
1049 });
1050 }
1051
1052 #[test]
1053 fn get_api_key_permission_optional_params_success() {
1054 TOKIO_SHARED_RT.block_on(async {
1055 let client = MockAccountApiClient { force_error: false };
1056
1057 let params = GetApiKeyPermissionParams::builder().recv_window(5000).build().unwrap();
1058
1059 let resp_json: Value = serde_json::from_str(r#"{"ipRestrict":false,"createTime":1698645219000,"enableReading":true,"enableWithdrawals":false,"enableInternalTransfer":false,"enableMargin":false,"enableFutures":false,"permitsUniversalTransfer":false,"enableVanillaOptions":false,"enableFixApiTrade":false,"enableFixReadOnly":true,"enableSpotAndMarginTrading":false,"enablePortfolioMarginTrading":true}"#).unwrap();
1060 let expected_response : models::GetApiKeyPermissionResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetApiKeyPermissionResponse");
1061
1062 let resp = client.get_api_key_permission(params).await.expect("Expected a response");
1063 let data_future = resp.data();
1064 let actual_response = data_future.await.unwrap();
1065 assert_eq!(actual_response, expected_response);
1066 });
1067 }
1068
1069 #[test]
1070 fn get_api_key_permission_response_error() {
1071 TOKIO_SHARED_RT.block_on(async {
1072 let client = MockAccountApiClient { force_error: true };
1073
1074 let params = GetApiKeyPermissionParams::builder().build().unwrap();
1075
1076 match client.get_api_key_permission(params).await {
1077 Ok(_) => panic!("Expected an error"),
1078 Err(err) => {
1079 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1080 }
1081 }
1082 });
1083 }
1084}