Skip to main content

alpaca_mock/
lib.rs

1//! Mock server support for `alpaca-rust` trade mainline flows.
2//!
3//! The crate exposes a runnable binary, `alpaca-mock`, and a thin library
4//! surface for integration tests that need to boot the mock server in-process.
5//!
6//! Runtime configuration:
7//!
8//! - `ALPACA_MOCK_LISTEN_ADDR` defaults to `127.0.0.1:3847`
9//! - market-data-backed flows use `ALPACA_DATA_API_KEY` and
10//!   `ALPACA_DATA_SECRET_KEY`
11//!
12//! ```no_run
13//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
14//! let server = alpaca_mock::spawn_test_server().await;
15//! assert!(server.base_url.starts_with("http://"));
16//! # Ok(())
17//! # }
18//! ```
19//!
20#![forbid(unsafe_code)]
21
22mod auth;
23
24pub mod app;
25pub mod handlers;
26pub mod state;
27
28use tokio::{net::TcpListener, task::JoinHandle};
29
30pub use app::{build_app, build_app_from_env, build_app_with_state};
31pub use state::{
32    AdminStateResponse, DEFAULT_STOCK_SYMBOL, InjectedHttpFault, InstrumentSnapshot,
33    LiveMarketDataBridge, MarketDataBridgeError, MockServerState,
34};
35
36pub const BINARY_NAME: &str = "alpaca-mock";
37
38#[derive(Debug)]
39pub struct TestServer {
40    pub base_url: String,
41    _task: JoinHandle<()>,
42}
43
44pub async fn spawn_test_server() -> TestServer {
45    spawn_test_server_with_state(MockServerState::new()).await
46}
47
48pub async fn spawn_test_server_with_state(state: MockServerState) -> TestServer {
49    let listener = TcpListener::bind("127.0.0.1:0")
50        .await
51        .expect("listener should bind");
52    let address = listener.local_addr().expect("local addr should exist");
53    let app = build_app_with_state(state);
54
55    let task = tokio::spawn(async move {
56        axum::serve(listener, app).await.expect("server should run");
57    });
58
59    TestServer {
60        base_url: format!("http://{address}"),
61        _task: task,
62    }
63}