use serde_json::json;
use super::super::{Argument, Source, pipeline};
#[test]
fn collection_arguments_keep_every_json_type() -> Result<(), String> {
let text = concat!(
"patients(n: -2.5 and yes: true and no: false and empty: null and ",
r#"text: "and ) | \" café" and array: [1, "and", {"x": ")"}] and object: {"a": [false]})"#
);
let plan = pipeline(text).map_err(|error| error.to_string())?;
let expected = [
("n", json!(-2.5)),
("yes", json!(true)),
("no", json!(false)),
("empty", json!(null)),
("text", json!("and ) | \" café")),
("array", json!([1, "and", {"x": ")"}])),
("object", json!({"a": [false]})),
];
assert_eq!(
plan.source,
Source::Collection {
name: "patients".to_owned(),
arguments: expected
.into_iter()
.map(|(field, value)| Argument {
field: field.to_owned(),
value
})
.collect(),
}
);
assert!(plan.stages.is_empty());
Ok(())
}
#[test]
fn explicit_collection_names_decode_json_and_can_name_reserved_words() -> Result<(), String> {
for (text, expected) in [
(r#"collection("ward \"4B\"")"#, "ward \"4B\""),
(r#"collection("caf\u00e9")"#, "café"),
(r#"collection("sort_by")"#, "sort_by"),
] {
let plan = pipeline(text).map_err(|error| error.to_string())?;
assert_eq!(
plan.source,
Source::Collection {
name: expected.to_owned(),
arguments: Vec::new(),
}
);
assert!(plan.stages.is_empty());
}
Ok(())
}
#[test]
fn malformed_collection_arguments_are_named_and_positioned() {
let cases = [
("patients()", "top-level field"),
(r#"patients(.ward: "4B")"#, "top-level field"),
(r#"patients(ward "4B")"#, "separates its field"),
("patients(ward: )", "JSON value"),
("patients(ward: unknown)", "JSON value"),
("patients(ward: 1, status: true)", "JSON value"),
("patients(ward: 1 and)", "JSON value"),
("patients(ward: 1 and status:)", "JSON value"),
("patients(ward: 1 and status: 2", "closed by `)`"),
("patients(ward: [1, 2)", "JSON value"),
(r#"patients(ward: "unclosed)"#, "JSON value"),
("patients(ward: $ward)", "JSON value"),
("patients(ward: 1)garbage", "collection is followed"),
];
for (text, expected) in cases {
let error = pipeline(text).err();
assert!(
error.is_some_and(|error| {
let span = error.span();
error.code() == "query_parse"
&& error.to_string().contains(expected)
&& span.start <= span.end
&& text.get(span.start..span.end).is_some()
}),
"{text}: expected {expected}"
);
}
}
#[test]
fn empty_nul_unquoted_and_unclosed_explicit_names_are_refused() {
for text in [
r#"collection("")"#,
r#"collection("\u0000")"#,
"collection(name)",
r#"collection("unclosed)"#,
r#"collection("name""#,
] {
assert!(pipeline(text).is_err(), "{text}");
}
}
#[test]
fn invalid_explicit_names_mark_the_written_name() {
for text in [r#"collection("")"#, r#"collection("\u0000")"#] {
let error = pipeline(text).err();
assert!(
error.is_some_and(|error| {
error.span().start == 11 && error.span().end == text.len() - 1
}),
"{text}"
);
}
}
#[test]
fn collection_argument_errors_use_byte_positions_after_unicode() {
let text = "\u{2003}patients(ward: \"café\" and status: nope)";
let error = pipeline(text).err();
let start = text.find("nope");
assert!(error.is_some_and(|error| {
Some(error.span().start) == start && error.span().end == start.map_or(0, |start| start + 4)
}));
}