Add unit tests for into_json
(#2254)
This commit is contained in:
parent
b3a1b39236
commit
c998584f49
1 changed files with 440 additions and 21 deletions
|
@ -194,9 +194,17 @@ fn into_json(value: Value, simplify: bool) -> JsonValue {
|
||||||
coordinates: JsonValue,
|
coordinates: JsonValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct GeometryCollection;
|
struct GeometryCollection;
|
||||||
|
|
||||||
|
impl Serialize for GeometryCollection {
|
||||||
|
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
s.serialize_str("GeometryCollection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Geometries {
|
struct Geometries {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -335,35 +343,48 @@ fn into_json(value: Value, simplify: bool) -> JsonValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
|
// These value types are simple values which
|
||||||
|
// can be used in query responses sent to
|
||||||
|
// the client.
|
||||||
Value::None | Value::Null => JsonValue::Null,
|
Value::None | Value::Null => JsonValue::Null,
|
||||||
Value::Bool(boolean) => boolean.into(),
|
Value::Bool(boolean) => boolean.into(),
|
||||||
Value::Number(Number::Int(n)) => n.into(),
|
Value::Number(number) => match number {
|
||||||
Value::Number(Number::Float(n)) => n.into(),
|
Number::Int(int) => int.into(),
|
||||||
Value::Number(Number::Decimal(n)) => json!(n),
|
Number::Float(float) => float.into(),
|
||||||
Value::Strand(strand) => match simplify {
|
Number::Decimal(decimal) => json!(decimal),
|
||||||
true => strand.to_raw().into(),
|
|
||||||
false => json!(strand),
|
|
||||||
},
|
},
|
||||||
Value::Duration(d) => match simplify {
|
Value::Strand(strand) => strand.0.into(),
|
||||||
true => d.to_string().into(),
|
Value::Duration(duration) => match simplify {
|
||||||
false => json!(d),
|
true => duration.to_raw().into(),
|
||||||
|
false => json!(duration.0),
|
||||||
},
|
},
|
||||||
Value::Datetime(d) => d.to_raw().into(),
|
Value::Datetime(datetime) => json!(datetime.0),
|
||||||
Value::Uuid(uuid) => json!(uuid),
|
Value::Uuid(uuid) => json!(uuid.0),
|
||||||
Value::Array(arr) => JsonValue::Array(Array::from((arr, simplify)).0),
|
Value::Array(array) => JsonValue::Array(Array::from((array, simplify)).0),
|
||||||
Value::Object(obj) => JsonValue::Object(Object::from((obj, simplify)).0),
|
Value::Object(object) => JsonValue::Object(Object::from((object, simplify)).0),
|
||||||
Value::Geometry(geo) => match simplify {
|
Value::Geometry(geo) => match simplify {
|
||||||
true => Geometry::from(geo).0,
|
true => Geometry::from(geo).0,
|
||||||
false => json!(geo),
|
false => match geo {
|
||||||
|
sql::Geometry::Point(geo) => json!(geo),
|
||||||
|
sql::Geometry::Line(geo) => json!(geo),
|
||||||
|
sql::Geometry::Polygon(geo) => json!(geo),
|
||||||
|
sql::Geometry::MultiPoint(geo) => json!(geo),
|
||||||
|
sql::Geometry::MultiLine(geo) => json!(geo),
|
||||||
|
sql::Geometry::MultiPolygon(geo) => json!(geo),
|
||||||
|
sql::Geometry::Collection(geo) => json!(geo),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Value::Bytes(bytes) => json!(bytes),
|
Value::Bytes(bytes) => json!(bytes.0),
|
||||||
Value::Param(param) => json!(param),
|
|
||||||
Value::Idiom(idiom) => json!(idiom),
|
|
||||||
Value::Table(table) => json!(table),
|
|
||||||
Value::Thing(thing) => match simplify {
|
Value::Thing(thing) => match simplify {
|
||||||
true => thing.to_string().into(),
|
true => thing.to_string().into(),
|
||||||
false => json!(thing),
|
false => json!(thing),
|
||||||
},
|
},
|
||||||
|
// These Value types are un-computed values
|
||||||
|
// and are not used in query responses sent
|
||||||
|
// to the client.
|
||||||
|
Value::Param(param) => json!(param),
|
||||||
|
Value::Idiom(idiom) => json!(idiom),
|
||||||
|
Value::Table(table) => json!(table),
|
||||||
Value::Model(model) => json!(model),
|
Value::Model(model) => json!(model),
|
||||||
Value::Regex(regex) => json!(regex),
|
Value::Regex(regex) => json!(regex),
|
||||||
Value::Block(block) => json!(block),
|
Value::Block(block) => json!(block),
|
||||||
|
@ -372,8 +393,8 @@ fn into_json(value: Value, simplify: bool) -> JsonValue {
|
||||||
Value::Future(future) => json!(future),
|
Value::Future(future) => json!(future),
|
||||||
Value::Constant(constant) => match simplify {
|
Value::Constant(constant) => match simplify {
|
||||||
true => match constant.value() {
|
true => match constant.value() {
|
||||||
ConstantValue::Datetime(d) => json!(d),
|
ConstantValue::Datetime(datetime) => json!(datetime.0),
|
||||||
ConstantValue::Float(f) => f.into(),
|
ConstantValue::Float(float) => float.into(),
|
||||||
},
|
},
|
||||||
false => json!(constant),
|
false => json!(constant),
|
||||||
},
|
},
|
||||||
|
@ -395,3 +416,401 @@ where
|
||||||
error: error.to_string(),
|
error: error.to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
mod into_json {
|
||||||
|
use crate::opt::from_value;
|
||||||
|
use crate::opt::into_json;
|
||||||
|
use crate::sql;
|
||||||
|
use crate::sql::Value;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono::Utc;
|
||||||
|
use geo::line_string;
|
||||||
|
use geo::point;
|
||||||
|
use geo::polygon;
|
||||||
|
use geo::LineString;
|
||||||
|
use geo::MultiLineString;
|
||||||
|
use geo::MultiPoint;
|
||||||
|
use geo::MultiPolygon;
|
||||||
|
use geo::Point;
|
||||||
|
use geo::Polygon;
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn none_or_null() {
|
||||||
|
for value in [Value::None, Value::Null] {
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(null));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(null));
|
||||||
|
|
||||||
|
let response: Option<String> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool() {
|
||||||
|
for boolean in [true, false] {
|
||||||
|
let value = Value::Bool(boolean);
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(boolean));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(boolean));
|
||||||
|
|
||||||
|
let response: bool = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, boolean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn number_int() {
|
||||||
|
for num in [i64::MIN, 0, i64::MAX] {
|
||||||
|
let value = Value::Number(sql::Number::Int(num));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(num));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(num));
|
||||||
|
|
||||||
|
let response: i64 = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn number_float() {
|
||||||
|
for num in [f64::NEG_INFINITY, f64::MIN, 0.0, f64::MAX, f64::INFINITY, f64::NAN] {
|
||||||
|
let value = Value::Number(sql::Number::Float(num));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(num));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(num));
|
||||||
|
|
||||||
|
if num.is_finite() {
|
||||||
|
let response: f64 = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, num);
|
||||||
|
} else {
|
||||||
|
let response: Option<f64> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn number_decimal() {
|
||||||
|
for num in [i64::MIN, 0, i64::MAX] {
|
||||||
|
let num = Decimal::new(num, 0);
|
||||||
|
let value = Value::Number(sql::Number::Decimal(num));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(num.to_string()));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(num));
|
||||||
|
|
||||||
|
let response: Decimal = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strand() {
|
||||||
|
for str in ["", "foo"] {
|
||||||
|
let value = Value::Strand(str.into());
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(str));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(str));
|
||||||
|
|
||||||
|
let response: String = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duration() {
|
||||||
|
for duration in [Duration::ZERO, Duration::MAX] {
|
||||||
|
let value = Value::Duration(duration.into());
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(sql::Duration(duration).to_raw()));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(duration));
|
||||||
|
|
||||||
|
let response: Duration = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn datetime() {
|
||||||
|
for datetime in [DateTime::<Utc>::MIN_UTC, DateTime::<Utc>::MAX_UTC] {
|
||||||
|
let value = Value::Datetime(datetime.into());
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(datetime));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(datetime));
|
||||||
|
|
||||||
|
let response: DateTime<Utc> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, datetime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uuid() {
|
||||||
|
for uuid in [Uuid::nil(), Uuid::max()] {
|
||||||
|
let value = Value::Uuid(uuid.into());
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(uuid));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(uuid));
|
||||||
|
|
||||||
|
let response: Uuid = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array() {
|
||||||
|
for vec in [vec![], vec![true, false]] {
|
||||||
|
let value =
|
||||||
|
Value::Array(sql::Array(vec.iter().copied().map(Value::from).collect()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(vec));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(vec));
|
||||||
|
|
||||||
|
let response: Vec<bool> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object() {
|
||||||
|
for map in [BTreeMap::new(), map!("done".to_owned() => true)] {
|
||||||
|
let value = Value::Object(sql::Object(
|
||||||
|
map.iter().map(|(key, value)| (key.clone(), Value::from(*value))).collect(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(map));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(map));
|
||||||
|
|
||||||
|
let response: BTreeMap<String, bool> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_point() {
|
||||||
|
let point = point! { x: 10., y: 20. };
|
||||||
|
let value = Value::Geometry(sql::Geometry::Point(point));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!({ "type": "Point", "coordinates": [10., 20.]}));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(point));
|
||||||
|
|
||||||
|
let response: Point = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_line() {
|
||||||
|
let line_string = line_string![
|
||||||
|
( x: 0., y: 0. ),
|
||||||
|
( x: 10., y: 0. ),
|
||||||
|
];
|
||||||
|
let value = Value::Geometry(sql::Geometry::Line(line_string.clone()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(
|
||||||
|
simple_json,
|
||||||
|
json!({ "type": "LineString", "coordinates": [[0., 0.], [10., 0.]]})
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(line_string));
|
||||||
|
|
||||||
|
let response: LineString = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, line_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_polygon() {
|
||||||
|
let polygon = polygon![
|
||||||
|
(x: -111., y: 45.),
|
||||||
|
(x: -111., y: 41.),
|
||||||
|
(x: -104., y: 41.),
|
||||||
|
(x: -104., y: 45.),
|
||||||
|
];
|
||||||
|
let value = Value::Geometry(sql::Geometry::Polygon(polygon.clone()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(
|
||||||
|
simple_json,
|
||||||
|
json!({ "type": "Polygon", "coordinates": [[
|
||||||
|
[-111., 45.],
|
||||||
|
[-111., 41.],
|
||||||
|
[-104., 41.],
|
||||||
|
[-104., 45.],
|
||||||
|
[-111., 45.],
|
||||||
|
]]})
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(polygon));
|
||||||
|
|
||||||
|
let response: Polygon = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, polygon);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_multi_point() {
|
||||||
|
let multi_point: MultiPoint =
|
||||||
|
vec![point! { x: 0., y: 0. }, point! { x: 1., y: 2. }].into();
|
||||||
|
let value = Value::Geometry(sql::Geometry::MultiPoint(multi_point.clone()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(
|
||||||
|
simple_json,
|
||||||
|
json!({ "type": "MultiPoint", "coordinates": [[0., 0.], [1., 2.]]})
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(multi_point));
|
||||||
|
|
||||||
|
let response: MultiPoint = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, multi_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_multi_line() {
|
||||||
|
let multi_line = MultiLineString::new(vec![line_string![
|
||||||
|
( x: 0., y: 0. ),
|
||||||
|
( x: 1., y: 2. ),
|
||||||
|
]]);
|
||||||
|
let value = Value::Geometry(sql::Geometry::MultiLine(multi_line.clone()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(
|
||||||
|
simple_json,
|
||||||
|
json!({ "type": "MultiLineString", "coordinates": [[[0., 0.], [1., 2.]]]})
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(multi_line));
|
||||||
|
|
||||||
|
let response: MultiLineString = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, multi_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_multi_polygon() {
|
||||||
|
let multi_polygon: MultiPolygon = vec![polygon![
|
||||||
|
(x: -111., y: 45.),
|
||||||
|
(x: -111., y: 41.),
|
||||||
|
(x: -104., y: 41.),
|
||||||
|
(x: -104., y: 45.),
|
||||||
|
]]
|
||||||
|
.into();
|
||||||
|
let value = Value::Geometry(sql::Geometry::MultiPolygon(multi_polygon.clone()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(
|
||||||
|
simple_json,
|
||||||
|
json!({ "type": "MultiPolygon", "coordinates": [[[
|
||||||
|
[-111., 45.],
|
||||||
|
[-111., 41.],
|
||||||
|
[-104., 41.],
|
||||||
|
[-104., 45.],
|
||||||
|
[-111., 45.],
|
||||||
|
]]]})
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(multi_polygon));
|
||||||
|
|
||||||
|
let response: MultiPolygon = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, multi_polygon);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn geometry_collection() {
|
||||||
|
for geometries in [vec![], vec![sql::Geometry::Point(point! { x: 10., y: 20. })]] {
|
||||||
|
let value = Value::Geometry(geometries.clone().into());
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(
|
||||||
|
simple_json,
|
||||||
|
json!({
|
||||||
|
"type": "GeometryCollection",
|
||||||
|
"geometries": geometries.clone().into_iter().map(|geo| into_json(Value::from(geo), true)).collect::<Vec<_>>(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(geometries));
|
||||||
|
|
||||||
|
let response: Vec<sql::Geometry> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, geometries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes() {
|
||||||
|
for bytes in [vec![], b"foo".to_vec()] {
|
||||||
|
let value = Value::Bytes(sql::Bytes(bytes.clone()));
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(bytes));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(bytes));
|
||||||
|
|
||||||
|
let response: Vec<u8> = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thing() {
|
||||||
|
let record_id = "foo:bar";
|
||||||
|
let thing = sql::thing(record_id).unwrap();
|
||||||
|
let value = Value::Thing(thing.clone());
|
||||||
|
|
||||||
|
let simple_json = into_json(value.clone(), true);
|
||||||
|
assert_eq!(simple_json, json!(record_id));
|
||||||
|
|
||||||
|
let json = into_json(value.clone(), false);
|
||||||
|
assert_eq!(json, json!(thing));
|
||||||
|
|
||||||
|
let response: sql::Thing = from_value(value).unwrap();
|
||||||
|
assert_eq!(response, thing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue