#![cfg(all(feature = "blocking", feature = "trading"))]
use crate::common::fixture;
use alpaca_sdk::blocking::Blocking;
use alpaca_sdk::trading::TradingClient;
use alpaca_sdk::{Credentials, RestConfig, RetryConfig};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn account_server() -> MockServer {
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v2/account"))
.respond_with(ResponseTemplate::new(200).set_body_json(fixture(
"trading/test_account_routes__test_get_account__01.json",
)))
.expect(1)
.mount(&server)
.await;
server
})
}
fn blocking_client(server: &MockServer) -> Blocking<TradingClient> {
let credentials = Credentials::new("key", "secret").unwrap();
let client = TradingClient::with_config(
&credentials,
RestConfig::new(server.uri()).retry(RetryConfig::none()),
)
.unwrap();
Blocking::new(client).unwrap()
}
#[test]
fn a_blocking_call_reaches_the_server_and_decodes() {
let server = account_server();
let client = blocking_client(&server);
let account = client.call(|client| client.get_account()).unwrap();
assert!(!account.id.is_nil());
}
#[test]
fn the_runtime_survives_more_than_one_call() {
let runtime = tokio::runtime::Runtime::new().unwrap();
let server = runtime.block_on(async {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v2/account"))
.respond_with(ResponseTemplate::new(200).set_body_json(fixture(
"trading/test_account_routes__test_get_account__01.json",
)))
.expect(2)
.mount(&server)
.await;
server
});
drop(runtime);
let client = blocking_client(&server);
let first = client.call(|client| client.get_account()).unwrap();
let second = client.call(|client| client.get_account()).unwrap();
assert_eq!(first.id, second.id);
}
#[tokio::test]
async fn dropping_inside_a_runtime_does_not_panic() {
let credentials = Credentials::new("key", "secret").unwrap();
let client = TradingClient::with_config(
&credentials,
RestConfig::new("http://127.0.0.1:1").retry(RetryConfig::none()),
)
.unwrap();
drop(Blocking::new(client).unwrap());
}
#[tokio::test]
async fn calling_from_inside_a_runtime_is_an_error_not_a_panic() {
let credentials = Credentials::new("key", "secret").unwrap();
let client = TradingClient::with_config(
&credentials,
RestConfig::new("http://127.0.0.1:1").retry(RetryConfig::none()),
)
.unwrap();
let blocking = Blocking::new(client).unwrap();
let error = blocking.call(|client| client.get_account()).unwrap_err();
match error {
alpaca_sdk::Error::InvalidRequest(message) => {
assert!(message.contains("inside an async runtime"), "{message}");
}
other => panic!("expected InvalidRequest, got {other:?}"),
}
}
#[tokio::test(flavor = "multi_thread")]
async fn a_call_from_spawn_blocking_succeeds() {
let server = tokio::task::spawn_blocking(account_server).await.unwrap();
let uri = server.uri();
let account = tokio::task::spawn_blocking(move || {
let credentials = Credentials::new("key", "secret").unwrap();
let client = TradingClient::with_config(
&credentials,
RestConfig::new(uri).retry(RetryConfig::none()),
)
.unwrap();
Blocking::new(client)
.unwrap()
.call(|client| client.get_account())
})
.await
.unwrap()
.expect("spawn_blocking is the supported bridge into the blocking façade");
assert!(!account.id.is_nil());
drop(server);
}