Add tests

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2024-03-20 10:19:36 +00:00
parent d072b849c0
commit 733e6373e9
8 changed files with 436 additions and 52 deletions

View File

@@ -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!");
}
}

View File

@@ -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);
}
}

View File

@@ -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")],
);
}
}

View File

@@ -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")]);
}
}

View File

@@ -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);
}
}