From 687b72e4b3791deb25ec1af8b20ace67cf68bc85 Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Date: Tue, 7 Nov 2023 14:41:38 +0100 Subject: [PATCH] Unit tests for conversions --- .../driver-adapters/src/conversion.rs | 2 +- .../driver-adapters/src/conversion/mysql.rs | 116 ++++++++++++++---- .../src/conversion/postgres.rs | 92 ++++++++++++++ .../driver-adapters/src/conversion/sqlite.rs | 111 ++++++++++++++--- 4 files changed, 277 insertions(+), 44 deletions(-) diff --git a/query-engine/driver-adapters/src/conversion.rs b/query-engine/driver-adapters/src/conversion.rs index 797ed8cc4ebe..983998db5d6d 100644 --- a/query-engine/driver-adapters/src/conversion.rs +++ b/query-engine/driver-adapters/src/conversion.rs @@ -7,7 +7,7 @@ use napi::NapiValue; use serde::Serialize; use serde_json::value::Value as JsonValue; -#[derive(Debug, Serialize)] +#[derive(Debug, PartialEq, Serialize)] #[serde(untagged)] pub enum JSArg { RawString(String), diff --git a/query-engine/driver-adapters/src/conversion/mysql.rs b/query-engine/driver-adapters/src/conversion/mysql.rs index 9804897224a5..17281edf06ab 100644 --- a/query-engine/driver-adapters/src/conversion/mysql.rs +++ b/query-engine/driver-adapters/src/conversion/mysql.rs @@ -1,35 +1,18 @@ use crate::conversion::JSArg; use serde_json::value::Value as JsonValue; -const DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; +const DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; +const DATE_FORMAT: &str = "%Y-%m-%d"; const TIME_FORMAT: &str = "%H:%M:%S"; pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result { let res = match &value.typed { - quaint::ValueType::Json(s) => match s { - Some(ref s) => { - let json_str = serde_json::to_string(s)?; - JSArg::RawString(json_str) - } - None => JsonValue::Null.into(), - }, - quaint::ValueType::Bytes(bytes) => match bytes { - Some(bytes) => JSArg::Buffer(bytes.to_vec()), - None => JsonValue::Null.into(), - }, - quaint::ValueType::Numeric(bd) => match bd { - // converting decimal to string to preserve the precision - Some(bd) => JSArg::RawString(bd.to_string()), - None => JsonValue::Null.into(), - }, - quaint::ValueType::DateTime(dt) => match dt { - Some(dt) => JSArg::RawString(dt.format(DATE_FORMAT).to_string()), - None => JsonValue::Null.into(), - }, - quaint::ValueType::Time(dt) => match dt { - Some(dt) => JSArg::RawString(dt.format(TIME_FORMAT).to_string()), - None => JsonValue::Null.into(), - }, + quaint::ValueType::Numeric(Some(bd)) => JSArg::RawString(bd.to_string()), + quaint::ValueType::Json(Some(ref s)) => JSArg::RawString(serde_json::to_string(s)?), + quaint::ValueType::Bytes(Some(bytes)) => JSArg::Buffer(bytes.to_vec()), + quaint::ValueType::Date(Some(d)) => JSArg::RawString(d.format(DATE_FORMAT).to_string()), + quaint::ValueType::DateTime(Some(dt)) => JSArg::RawString(dt.format(DATETIME_FORMAT).to_string()), + quaint::ValueType::Time(Some(t)) => JSArg::RawString(t.format(TIME_FORMAT).to_string()), quaint::ValueType::Array(Some(ref items)) => JSArg::Array( items .iter() @@ -40,3 +23,86 @@ pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result { }; Ok(res) } + +#[cfg(test)] +mod test { + use super::*; + use bigdecimal::BigDecimal; + use chrono::*; + use quaint::ValueType; + use std::str::FromStr; + + #[test] + #[rustfmt::skip] + fn test_value_to_js_arg() { + let test_cases = vec![ + ( + ValueType::Numeric(Some(1.into())), + JSArg::RawString("1".to_string()) + ), + ( + ValueType::Numeric(Some(BigDecimal::from_str("-1.1").unwrap())), + JSArg::RawString("-1.1".to_string()), + ), + ( + ValueType::Numeric(None), + JSArg::Value(serde_json::Value::Null) + ), + ( + ValueType::Json(Some(serde_json::json!({"a": 1}))), + JSArg::RawString(r#"{"a":1}"#.to_string()), + ), + ( + ValueType::Json(None), + JSArg::Value(serde_json::Value::Null) + ), + ( + ValueType::Date(Some(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap())), + JSArg::RawString("2020-02-29".to_string()), + ), + ( + ValueType::Date(None), + JSArg::Value(serde_json::Value::Null) + ), + ( + ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())), + JSArg::RawString("2020-01-01 23:13:01".to_string()), + ), + ( + ValueType::DateTime(None), + JSArg::Value(serde_json::Value::Null) + ), + ( + ValueType::Time(Some(NaiveTime::from_hms_opt(23, 13, 1).unwrap())), + JSArg::RawString("23:13:01".to_string()), + ), + ( + ValueType::Time(None), + JSArg::Value(serde_json::Value::Null) + ), + ( + ValueType::Array(Some(vec!( + ValueType::Numeric(Some(1.into())).into_value(), + ValueType::Numeric(None).into_value(), + ValueType::Time(Some(NaiveTime::from_hms_opt(23, 13, 1).unwrap())).into_value(), + ValueType::Time(None).into_value(), + ))), + JSArg::Array(vec!( + JSArg::RawString("1".to_string()), + JSArg::Value(serde_json::Value::Null), + JSArg::RawString("23:13:01".to_string()), + JSArg::Value(serde_json::Value::Null), + )) + ), + ]; + + let mut errors: Vec = vec![]; + for (val, expected) in test_cases { + let actual = value_to_js_arg(&val.clone().into_value()).unwrap(); + if actual != expected { + errors.push(format!("transforming: {:?}, expected: {:?}, actual: {:?}", &val, expected, actual)); + } + } + assert_eq!(errors.len(), 0, "{}", errors.join("\n")); + } +} diff --git a/query-engine/driver-adapters/src/conversion/postgres.rs b/query-engine/driver-adapters/src/conversion/postgres.rs index 9f018c02d66b..17d21cacf753 100644 --- a/query-engine/driver-adapters/src/conversion/postgres.rs +++ b/query-engine/driver-adapters/src/conversion/postgres.rs @@ -50,3 +50,95 @@ pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result { Ok(res) } + +#[cfg(test)] +mod test { + use super::*; + use bigdecimal::BigDecimal; + use chrono::*; + use quaint::ValueType; + use serde_json::Value; + use std::str::FromStr; + + #[test] + #[rustfmt::skip] + fn test_value_to_js_arg() { + let test_cases: Vec<(quaint::Value, JSArg)> = vec![ + ( + ValueType::Numeric(Some(1.into())).into_value(), + JSArg::RawString("1".to_string()) + ), + ( + ValueType::Numeric(Some(BigDecimal::from_str("-1.1").unwrap())).into_value(), + JSArg::RawString("-1.1".to_string()), + ), + ( + ValueType::Numeric(None).into_value(), + JSArg::Value(Value::Null) + ), + ( + ValueType::Json(Some(serde_json::json!({"a": 1}))).into_value(), + JSArg::RawString(r#"{"a":1}"#.to_string()), + ), + ( + ValueType::Json(None).into_value(), + JSArg::Value(Value::Null) + ), + ( + ValueType::Date(Some(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap())).into_value(), + JSArg::Value(Value::String("2020-02-29".to_string())), + ), + ( + ValueType::Date(None).into_value(), + JSArg::Value(Value::Null) + ), + ( + ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value().with_native_column_type(Some("DATE")), + JSArg::RawString("2020-01-01".to_string()), + ), + ( + ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value().with_native_column_type(Some("TIME")), + JSArg::RawString("23:13:01".to_string()), + ), + ( + ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value().with_native_column_type(Some("TIMETZ")), + JSArg::RawString("23:13:01".to_string()), + ), + ( + ValueType::DateTime(None).into_value(), + JSArg::Value(Value::Null) + ), + ( + ValueType::Time(Some(NaiveTime::from_hms_opt(23, 13, 1).unwrap())).into_value(), + JSArg::Value(Value::String("23:13:01".to_string())), + ), + ( + ValueType::Time(None).into_value(), + JSArg::Value(Value::Null) + ), + ( + ValueType::Array(Some(vec!( + ValueType::Numeric(Some(1.into())).into_value(), + ValueType::Numeric(None).into_value(), + ValueType::Time(Some(NaiveTime::from_hms_opt(23, 13, 1).unwrap())).into_value(), + ValueType::Time(None).into_value(), + ))).into_value(), + JSArg::Array(vec!( + JSArg::RawString("1".to_string()), + JSArg::Value(Value::Null), + JSArg::Value(Value::String("23:13:01".to_string())), + JSArg::Value(Value::Null), + )) + ), + ]; + + let mut errors: Vec = vec![]; + for (val, expected) in test_cases { + let actual = value_to_js_arg(&val).unwrap(); + if actual != expected { + errors.push(format!("transforming: {:?}, expected: {:?}, actual: {:?}", &val, expected, actual)); + } + } + assert_eq!(errors.len(), 0, "{}", errors.join("\n")); + } +} diff --git a/query-engine/driver-adapters/src/conversion/sqlite.rs b/query-engine/driver-adapters/src/conversion/sqlite.rs index d2c963b32925..e338ae8e4313 100644 --- a/query-engine/driver-adapters/src/conversion/sqlite.rs +++ b/query-engine/driver-adapters/src/conversion/sqlite.rs @@ -3,25 +3,15 @@ use serde_json::value::Value as JsonValue; pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result { let res = match &value.typed { - quaint::ValueType::Numeric(bd) => match bd { - // converting decimal to string to preserve the precision - Some(bd) => match bd.to_string().parse::() { - Ok(double) => JSArg::from(JsonValue::from(double)), - Err(_) => JSArg::from(JsonValue::from(value.clone())), - }, - None => JsonValue::Null.into(), - }, - quaint::ValueType::Json(s) => match s { - Some(ref s) => { - let json_str = serde_json::to_string(s)?; - JSArg::RawString(json_str) - } - None => JsonValue::Null.into(), - }, - quaint::ValueType::Bytes(bytes) => match bytes { - Some(bytes) => JSArg::Buffer(bytes.to_vec()), - None => JsonValue::Null.into(), + quaint::ValueType::Numeric(Some(bd)) => match bd.to_string().parse::() { + Ok(double) => JSArg::from(JsonValue::from(double)), + Err(_) => JSArg::from(JsonValue::from(value.clone())), }, + quaint::ValueType::Json(Some(ref s)) => { + let json_str = serde_json::to_string(s)?; + JSArg::RawString(json_str) + } + quaint::ValueType::Bytes(Some(bytes)) => JSArg::Buffer(bytes.to_vec()), quaint::ValueType::Array(Some(ref items)) => JSArg::Array( items .iter() @@ -33,3 +23,88 @@ pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result { Ok(res) } + +// unit tests for value_to_js_arg +#[cfg(test)] +mod test { + use super::*; + use bigdecimal::BigDecimal; + use chrono::*; + use quaint::ValueType; + use serde_json::Value; + use std::str::FromStr; + + #[test] + #[rustfmt::skip] + fn test_value_to_js_arg() { + let test_cases = vec![ + ( + ValueType::Numeric(Some(1.into())), + JSArg::Value(Value::Number("1.0".parse().unwrap())) + ), + ( + ValueType::Numeric(Some(BigDecimal::from_str("-1.1").unwrap())), + JSArg::Value(Value::Number("-1.1".parse().unwrap())), + ), + ( + ValueType::Numeric(None), + JSArg::Value(Value::Null) + ), + ( + ValueType::Json(Some(serde_json::json!({"a": 1}))), + JSArg::RawString(r#"{"a":1}"#.to_string()), + ), + ( + ValueType::Json(None), + JSArg::Value(Value::Null) + ), + ( + ValueType::Date(Some(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap())), + JSArg::Value(Value::String("2020-02-29".to_string())), + ), + ( + ValueType::Date(None), + JSArg::Value(Value::Null) + ), + ( + ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())), + JSArg::Value(Value::String("2020-01-01T23:13:01+00:00".to_string())), + ), + ( + ValueType::DateTime(None), + JSArg::Value(Value::Null) + ), + ( + ValueType::Time(Some(NaiveTime::from_hms_opt(23, 13, 1).unwrap())), + JSArg::Value(Value::String("23:13:01".to_string())), + ), + ( + ValueType::Time(None), + JSArg::Value(Value::Null) + ), + ( + ValueType::Array(Some(vec!( + ValueType::Numeric(Some(1.into())).into_value(), + ValueType::Numeric(None).into_value(), + ValueType::Time(Some(NaiveTime::from_hms_opt(23, 13, 1).unwrap())).into_value(), + ValueType::Time(None).into_value(), + ))), + JSArg::Array(vec!( + JSArg::Value(Value::Number("1.0".parse().unwrap())), + JSArg::Value(Value::Null), + JSArg::Value(Value::String("23:13:01".to_string())), + JSArg::Value(Value::Null), + )) + ), + ]; + + let mut errors: Vec = vec![]; + for (val, expected) in test_cases { + let actual = value_to_js_arg(&val.clone().into_value()).unwrap(); + if actual != expected { + errors.push(format!("transforming: {:?}, expected: {:?}, actual: {:?}", &val, expected, actual)); + } + } + assert_eq!(errors.len(), 0, "{}", errors.join("\n")); + } +}