#![cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
use digdigdig3::connector_manager::ExchangeHub;
use digdigdig3::core::types::{AccountType, ExchangeId, SymbolInput};
use digdigdig3_station::{Station, Stream, SubscriptionSet};
fn cors_proxy_template() -> &'static str {
option_env!("DIG3_CORS_PROXY").unwrap_or("https://api.codetabs.com/v1/proxy/?quest={url}")
}
#[wasm_bindgen_test]
async fn rest_via_encoded_proxy_get_klines() {
let hub = ExchangeHub::new();
hub.set_rest_base_override(
ExchangeId::Binance,
cors_proxy_template().to_string(),
);
hub.connect_public(ExchangeId::Binance, false)
.await
.expect("connect_public must succeed with codetabs encoded-proxy override set");
let rest = hub
.rest(ExchangeId::Binance)
.expect("REST connector must be present after connect_public");
let klines = rest
.get_klines(
SymbolInput::Raw("BTCUSDT"),
"1m",
Some(1),
AccountType::Spot,
None,
)
.await
.expect("get_klines via codetabs encoded-proxy must succeed");
web_sys::console::log_1(
&format!("rest_via_encoded_proxy_get_klines: {} kline(s) received", klines.len()).into()
);
assert!(
!klines.is_empty(),
"expected ≥1 kline from Binance BTCUSDT via codetabs encoded proxy; got 0"
);
let k = &klines[0];
assert!(
k.open > 0.0,
"kline.open must be positive; got {}",
k.open
);
}
#[wasm_bindgen_test]
async fn polling_lsr_returns_unsupported_on_wasm() {
let station = Station::builder()
.build()
.await
.expect("Station::build must succeed on wasm");
let set = SubscriptionSet::new().add(
ExchangeId::Binance,
"BTC-USDT",
AccountType::FuturesCross,
[Stream::LongShortRatio],
);
let report = station
.subscribe(set)
.await
.expect("subscribe must not return Err at the batch level");
assert!(
report.ok.is_empty(),
"LSR must not succeed on wasm; ok = {:?}",
report.ok
);
assert!(
!report.failed.is_empty(),
"LSR subscribe must produce a failure entry on wasm"
);
let failure = &report.failed[0];
let err_str = failure.error.to_string().to_lowercase();
assert!(
err_str.contains("not supported") || err_str.contains("notsupported"),
"failure reason must indicate stream-not-supported; got: {:?}",
failure.error
);
}
#[wasm_bindgen_test]
async fn persistence_station_builder_wasm_path() {
use digdigdig3_station::PersistenceConfig;
let station = Station::builder()
.persistence(PersistenceConfig::default())
.build()
.await
.expect("Station with default PersistenceConfig must build on wasm");
assert_eq!(
station.active_streams(),
0,
"newly built station must have 0 active streams"
);
let _root = station.storage_root();
}
#[wasm_bindgen_test]
async fn gap_heal_module_compiles_and_config_constructs() {
use digdigdig3_station::GapHealConfig;
use digdigdig3_station::gap_heal::{heal_limit, kline_interval_to_duration};
let cfg = GapHealConfig::default();
assert!(!cfg.enabled, "default GapHealConfig must be disabled");
assert_eq!(cfg.default_limit, 300);
assert_eq!(cfg.max_limit, 1000);
let cfg_on = GapHealConfig::on().default_limit(50).max_limit(500);
assert!(cfg_on.enabled);
assert_eq!(cfg_on.default_limit, 50);
assert_eq!(cfg_on.max_limit, 500);
use std::time::Duration;
assert_eq!(kline_interval_to_duration("1m"), Some(Duration::from_secs(60)));
assert_eq!(kline_interval_to_duration("1h"), Some(Duration::from_secs(3600)));
assert_eq!(kline_interval_to_duration("1d"), Some(Duration::from_secs(86400)));
assert_eq!(kline_interval_to_duration("bad"), None);
let limit = heal_limit(&cfg_on, "1m", 0, 0);
assert_eq!(limit, cfg_on.default_limit.min(cfg_on.max_limit));
let now_ms = 1_700_000_600_000i64;
let last_ms = now_ms - 10 * 60 * 1000; let limit2 = heal_limit(&cfg_on, "1m", last_ms, now_ms);
assert!(limit2 >= 10, "heal_limit must be ≥ gap/interval = 10; got {limit2}");
assert!(limit2 <= cfg_on.max_limit, "heal_limit must not exceed max_limit");
}
#[wasm_bindgen_test]
async fn rest_override_persists_after_connect_public() {
let hub = ExchangeHub::new();
let proxy = cors_proxy_template().to_string();
hub.set_rest_base_override(ExchangeId::Binance, proxy.clone());
hub.connect_public(ExchangeId::Binance, false)
.await
.expect("connect_public must not error with override set");
assert!(
hub.rest(ExchangeId::Binance).is_some(),
"rest() must return Some after connect_public"
);
assert_eq!(
hub.get_rest_base_override(ExchangeId::Binance),
Some(proxy),
"override must survive connect_public"
);
}
#[wasm_bindgen_test]
async fn ws_kucoin_via_proxy() {
use digdigdig3::core::types::{StreamType, SubscriptionRequest, Symbol};
use futures_util::future::{select, Either};
use futures_util::{pin_mut, StreamExt};
use std::time::Duration;
let hub = ExchangeHub::new();
hub.set_rest_base_override(ExchangeId::KuCoin, cors_proxy_template().to_string());
let connect = hub.connect_websocket(ExchangeId::KuCoin, AccountType::Spot, false);
let ct = gloo_timers::future::sleep(Duration::from_secs(20));
pin_mut!(connect, ct);
match select(connect, ct).await {
Either::Left((Ok(()), _)) => {}
Either::Left((Err(e), _)) => panic!("KuCoin WS connect (bullet-token via proxy) failed: {e}"),
Either::Right(_) => panic!("KuCoin WS connect timed out"),
}
let ws = hub
.ws(ExchangeId::KuCoin, AccountType::Spot)
.expect("ws present after connect_websocket");
ws.subscribe(SubscriptionRequest {
symbol: Symbol::with_raw("BTC", "USDT", "BTC-USDT".to_string()),
stream_type: StreamType::Trade,
account_type: AccountType::Spot,
depth: None,
update_speed_ms: None,
})
.await
.expect("KuCoin WS subscribe");
let mut stream = ws.event_stream();
let deadline = gloo_timers::future::sleep(Duration::from_secs(20));
pin_mut!(deadline);
let mut got = false;
loop {
let next = stream.next();
pin_mut!(next);
match select(next, &mut deadline).await {
Either::Left((Some(Ok(_)), _)) => {
got = true;
break;
}
Either::Left((_, _)) => break,
Either::Right(_) => break,
}
}
web_sys::console::log_1(&format!("[kucoin-proxy] got_event={got}").into());
assert!(got, "KuCoin WS via proxy should deliver >=1 event");
}