#![cfg(not(target_arch = "wasm32"))]
use std::time::Duration;
use digdigdig3_station::Kind;
#[test]
fn kind_is_poll_only_classification() {
use digdigdig3::core::websocket::KlineInterval;
let lsr = Kind::LongShortRatio.is_poll_only();
assert!(lsr.is_some(), "LongShortRatio must be poll-only");
let lsr_spec = lsr.unwrap();
assert_eq!(
lsr_spec.cadence,
Duration::from_secs(5 * 60),
"LSR cadence must be 5 min"
);
assert_eq!(lsr_spec.jitter_pct, 10, "LSR jitter_pct must be 10");
let hv = Kind::HistoricalVolatility.is_poll_only();
assert!(hv.is_some(), "HistoricalVolatility must be poll-only");
let hv_spec = hv.unwrap();
assert_eq!(
hv_spec.cadence,
Duration::from_secs(60 * 60),
"HV cadence must be 1h"
);
assert_eq!(hv_spec.jitter_pct, 5, "HV jitter_pct must be 5");
assert!(Kind::Trade.is_poll_only().is_none(), "Trade is WS, not poll");
assert!(Kind::Ticker.is_poll_only().is_none(), "Ticker is WS, not poll");
assert!(
Kind::Kline(KlineInterval::new("1m")).is_poll_only().is_none(),
"Kline is WS, not poll"
);
assert!(
Kind::FundingRate.is_poll_only().is_none(),
"FundingRate is WS, not poll"
);
assert!(Kind::Basis.is_poll_only().is_none(), "Basis is derived, not poll");
assert!(
Kind::FundingSettlement.is_poll_only().is_none(),
"FundingSettlement is derived, not poll"
);
}
#[test]
fn dedup_fence_semantics() {
use digdigdig3_station::data::LongShortRatioPoint;
use digdigdig3_station::DataPoint;
let pts = vec![
LongShortRatioPoint { ts_ms: 1_000, ratio: 1.1, long_pct: 0.52, short_pct: 0.48 },
LongShortRatioPoint { ts_ms: 2_000, ratio: 1.2, long_pct: 0.55, short_pct: 0.45 },
LongShortRatioPoint { ts_ms: 3_000, ratio: 1.3, long_pct: 0.57, short_pct: 0.43 },
];
let last_emitted_ms: i64 = 2_000;
let new_pts: Vec<_> = pts.iter().filter(|p| p.timestamp_ms() > last_emitted_ms).collect();
assert_eq!(new_pts.len(), 1, "only ts=3000 is new");
assert_eq!(new_pts[0].ts_ms, 3_000);
}
#[tokio::test]
#[ignore]
async fn lsr_binance_live() {
use digdigdig3::core::types::{AccountType, ExchangeId};
use digdigdig3_station::{Event, Station, Stream, SubscriptionSet};
let station = Station::builder()
.build()
.await
.expect("station build");
let report = station
.subscribe(
SubscriptionSet::new().add(
ExchangeId::Binance,
"BTC-USDT",
AccountType::FuturesCross,
[Stream::LongShortRatio],
),
)
.await
.expect("subscribe");
assert!(report.is_fully_ok(), "subscribe failed: {:?}", report.failed);
let mut handle = report.handle;
let deadline = tokio::time::Instant::now() + Duration::from_secs(360); let mut received = 0usize;
while tokio::time::Instant::now() < deadline {
match tokio::time::timeout(Duration::from_secs(35), handle.recv()).await {
Ok(Some(Event::LongShortRatio { .. })) => {
received += 1;
break; }
Ok(Some(_)) => {} Ok(None) | Err(_) => break,
}
}
assert!(received >= 1, "expected ≥1 LSR event in 6 min; got {received}");
}
#[tokio::test]
#[ignore]
async fn hv_deribit_live() {
use digdigdig3::core::types::{AccountType, ExchangeId};
use digdigdig3_station::{Event, Station, Stream, SubscriptionSet};
let station = Station::builder()
.build()
.await
.expect("station build");
let report = station
.subscribe(
SubscriptionSet::new().add_raw(
ExchangeId::Deribit,
"BTC", AccountType::Spot,
[Stream::HistoricalVolatility],
),
)
.await
.expect("subscribe");
assert!(report.is_fully_ok(), "subscribe failed: {:?}", report.failed);
let mut handle = report.handle;
let event = tokio::time::timeout(Duration::from_secs(90), handle.recv()).await;
assert!(
matches!(event, Ok(Some(Event::HistoricalVolatility { .. }))),
"expected HistoricalVolatility event within 90s; got: {:?}",
event
);
}