@@ -13,3 +13,14 @@ pub fn strip(content: &str) -> String {
|
||||
let content = content.trim();
|
||||
content.to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_strip() {
|
||||
let content = "<p> <b> Hello, </b> <i> World! </i> </p>";
|
||||
assert_eq!(strip(content), "Hello, World!");
|
||||
}
|
||||
}
|
||||
|
@@ -223,3 +223,53 @@ impl Order {
|
||||
orders
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_normalize() {
|
||||
let order_template = Order {
|
||||
id: Uuid::new_v4(),
|
||||
client_order_id: Uuid::new_v4(),
|
||||
created_at: OffsetDateTime::now_utc(),
|
||||
updated_at: None,
|
||||
submitted_at: OffsetDateTime::now_utc(),
|
||||
filled_at: None,
|
||||
expired_at: None,
|
||||
cancel_requested_at: None,
|
||||
canceled_at: None,
|
||||
failed_at: None,
|
||||
replaced_at: None,
|
||||
replaced_by: None,
|
||||
replaces: None,
|
||||
asset_id: Uuid::new_v4(),
|
||||
symbol: "AAPL".to_string(),
|
||||
asset_class: super::super::asset::Class::UsEquity,
|
||||
notional: None,
|
||||
qty: None,
|
||||
filled_qty: 0.0,
|
||||
filled_avg_price: None,
|
||||
order_class: Class::Simple,
|
||||
order_type: Type::Market,
|
||||
side: Side::Buy,
|
||||
time_in_force: TimeInForce::Day,
|
||||
limit_price: None,
|
||||
stop_price: None,
|
||||
status: Status::New,
|
||||
extended_hours: false,
|
||||
legs: None,
|
||||
trail_percent: None,
|
||||
trail_price: None,
|
||||
hwm: None,
|
||||
};
|
||||
|
||||
let mut order = order_template.clone();
|
||||
order.legs = Some(vec![order_template.clone(), order_template.clone()]);
|
||||
order.legs.as_mut().unwrap()[0].legs = Some(vec![order_template.clone()]);
|
||||
let orders = order.normalize();
|
||||
|
||||
assert_eq!(orders.len(), 4);
|
||||
}
|
||||
}
|
||||
|
@@ -86,7 +86,7 @@ where
|
||||
struct TimeVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for TimeVisitor {
|
||||
type Value = time::Time;
|
||||
type Value = Time;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a string in the format HH:MM")
|
||||
@@ -102,3 +102,92 @@ where
|
||||
|
||||
deserializer.deserialize_str(TimeVisitor)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Deserialize;
|
||||
use serde_test::{assert_de_tokens, Token};
|
||||
use time::Time;
|
||||
|
||||
#[test]
|
||||
fn test_add_slash() {
|
||||
assert_eq!(add_slash("BTCUSD"), "BTC/USD");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_slash_skip() {
|
||||
assert_eq!(add_slash("ABTC"), "ABTC");
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct AddSlashToSymbol {
|
||||
#[serde(deserialize_with = "add_slash_to_symbol")]
|
||||
symbol: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_slash_to_symbol() {
|
||||
assert_de_tokens::<AddSlashToSymbol>(
|
||||
&AddSlashToSymbol {
|
||||
symbol: String::from("BTC/USD"),
|
||||
},
|
||||
&[Token::Str("BTCUSD")],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_slash_to_symbol_skip() {
|
||||
assert_de_tokens::<AddSlashToSymbol>(
|
||||
&AddSlashToSymbol {
|
||||
symbol: String::from("ABTC"),
|
||||
},
|
||||
&[Token::Str("ABTC")],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct AddSlashToSymbols {
|
||||
#[serde(deserialize_with = "add_slash_to_symbols")]
|
||||
symbols: Vec<String>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_slash_to_symbols() {
|
||||
assert_de_tokens::<AddSlashToSymbols>(
|
||||
&AddSlashToSymbols {
|
||||
symbols: vec![
|
||||
String::from("BTC/USD"),
|
||||
String::from("ETH/USD"),
|
||||
String::from("ABTC"),
|
||||
],
|
||||
},
|
||||
&[
|
||||
Token::Seq { len: Some(3) },
|
||||
Token::Str("BTCUSD"),
|
||||
Token::Str("ETHUSD"),
|
||||
Token::Str("ABTC"),
|
||||
Token::SeqEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct HumanTime {
|
||||
#[serde(deserialize_with = "human_time_hh_mm")]
|
||||
time: Time,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_human_time_hh_mm() {
|
||||
assert_de_tokens::<HumanTime>(
|
||||
&HumanTime {
|
||||
time: Time::from_hms(12, 34, 0).unwrap(),
|
||||
},
|
||||
&[Token::Str("12:34")],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -5,27 +5,48 @@ pub fn timeframe<S>(timeframe: &Duration, serializer: S) -> Result<S::Ok, S::Err
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mins = timeframe.as_secs() / 60;
|
||||
let secs = timeframe.as_secs();
|
||||
|
||||
if secs < 60 || secs % 60 != 0 {
|
||||
return Err(serde::ser::Error::custom("Invalid timeframe duration"));
|
||||
}
|
||||
|
||||
let mins = secs / 60;
|
||||
|
||||
if mins < 60 {
|
||||
return serializer.serialize_str(&format!("{mins}Min"));
|
||||
}
|
||||
|
||||
if mins % 60 != 0 {
|
||||
return Err(serde::ser::Error::custom("Invalid timeframe duration"));
|
||||
}
|
||||
|
||||
let hours = mins / 60;
|
||||
|
||||
if hours < 24 {
|
||||
return serializer.serialize_str(&format!("{hours}Hour"));
|
||||
}
|
||||
|
||||
if hours % 24 != 0 {
|
||||
return Err(serde::ser::Error::custom("Invalid timeframe duration"));
|
||||
}
|
||||
|
||||
let days = hours / 24;
|
||||
|
||||
if days == 1 {
|
||||
return serializer.serialize_str("1Day");
|
||||
}
|
||||
|
||||
let weeks = days / 7;
|
||||
if weeks == 1 {
|
||||
if days == 7 {
|
||||
return serializer.serialize_str("1Week");
|
||||
}
|
||||
|
||||
if days < 30 || days % 30 != 0 {
|
||||
return Err(serde::ser::Error::custom("Invalid timeframe duration"));
|
||||
}
|
||||
|
||||
let months = days / 30;
|
||||
|
||||
if [1, 2, 3, 4, 6, 12].contains(&months) {
|
||||
return serializer.serialize_str(&format!("{months}Month"));
|
||||
};
|
||||
@@ -87,3 +108,174 @@ where
|
||||
|
||||
join_symbols(&symbols, serializer)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Serialize;
|
||||
use serde_test::{assert_ser_tokens, assert_ser_tokens_error, Token};
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct Timeframe {
|
||||
#[serde(serialize_with = "timeframe")]
|
||||
duration: Duration,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_30_mins() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(60 * 30),
|
||||
};
|
||||
|
||||
assert_ser_tokens(&timeframe, &[Token::Str("30Min")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_2_hours() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(60 * 60 * 2),
|
||||
};
|
||||
|
||||
assert_ser_tokens(&timeframe, &[Token::Str("2Hour")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_1_day() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(60 * 60 * 24),
|
||||
};
|
||||
|
||||
assert_ser_tokens(&timeframe, &[Token::Str("1Day")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_1_week() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(60 * 60 * 24 * 7),
|
||||
};
|
||||
|
||||
assert_ser_tokens(&timeframe, &[Token::Str("1Week")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_6_months() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(60 * 60 * 24 * 30 * 6),
|
||||
};
|
||||
|
||||
assert_ser_tokens(&timeframe, &[Token::Str("6Month")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_invalid_1_second() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(1),
|
||||
};
|
||||
|
||||
assert_ser_tokens_error(&timeframe, &[], "Invalid timeframe duration");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_invalid_61_seconds() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(61),
|
||||
};
|
||||
|
||||
assert_ser_tokens_error(&timeframe, &[], "Invalid timeframe duration");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeframe_invalid_6_days() {
|
||||
let timeframe = Timeframe {
|
||||
duration: Duration::from_secs(60 * 60 * 24 * 6),
|
||||
};
|
||||
|
||||
assert_ser_tokens_error(&timeframe, &[], "Invalid timeframe duration");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_slash() {
|
||||
let pair = "BTC/USDT";
|
||||
assert_eq!(remove_slash(pair), "BTCUSDT");
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct JoinSymbols {
|
||||
#[serde(serialize_with = "join_symbols")]
|
||||
symbols: Vec<String>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_symbols() {
|
||||
let symbols = JoinSymbols {
|
||||
symbols: vec![String::from("BTC/USD"), String::from("ETH/USD")],
|
||||
};
|
||||
|
||||
assert_ser_tokens(&symbols, &[Token::Str("BTC/USD,ETH/USD")]);
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct JoinSymbolsOption {
|
||||
#[serde(serialize_with = "join_symbols_option")]
|
||||
symbols: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_symbols_option_some() {
|
||||
let symbols = JoinSymbolsOption {
|
||||
symbols: Some(vec![String::from("BTC/USD"), String::from("ETH/USD")]),
|
||||
};
|
||||
|
||||
assert_ser_tokens(&symbols, &[Token::Str("BTC/USD,ETH/USD")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_symbols_option_none() {
|
||||
let symbols = JoinSymbolsOption { symbols: None };
|
||||
|
||||
assert_ser_tokens(&symbols, &[Token::None]);
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct RemoveSlashFromSymbols {
|
||||
#[serde(serialize_with = "remove_slash_from_symbols")]
|
||||
symbols: Vec<String>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_slash_from_symbols() {
|
||||
let symbols = RemoveSlashFromSymbols {
|
||||
symbols: vec![String::from("BTC/USD"), String::from("ETH/USD")],
|
||||
};
|
||||
|
||||
assert_ser_tokens(
|
||||
&symbols,
|
||||
&[
|
||||
Token::Seq { len: Some(2) },
|
||||
Token::Str("BTCUSD"),
|
||||
Token::Str("ETHUSD"),
|
||||
Token::SeqEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct RemoveSlashAndJoinSymbols {
|
||||
#[serde(serialize_with = "remove_slash_and_join_symbols")]
|
||||
symbols: Vec<String>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_slash_and_join_symbols() {
|
||||
let symbols = RemoveSlashAndJoinSymbols {
|
||||
symbols: vec![String::from("BTC/USD"), String::from("ETH/USD")],
|
||||
};
|
||||
|
||||
assert_ser_tokens(&symbols, &[Token::Str("BTCUSD,ETHUSD")]);
|
||||
}
|
||||
}
|
||||
|
@@ -26,3 +26,21 @@ pub fn duration_until(time: OffsetDateTime) -> Duration {
|
||||
Duration::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_duration_until_future() {
|
||||
let future = OffsetDateTime::now_utc() + Duration::from_secs(60);
|
||||
let duration = duration_until(future).as_secs();
|
||||
assert!((59..=61).contains(&duration));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_until_past() {
|
||||
let past = OffsetDateTime::now_utc() - Duration::from_secs(60);
|
||||
assert_eq!(duration_until(past).as_secs(), 0);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user