use quarb::{QueryResult, Value};
fn doc() -> quarb_json::JsonAdapter {
quarb_json::JsonAdapter::parse(
r#"{"books":[
{"title":"Iliad","genre":"epic","year":-750},
{"title":"Odyssey","genre":"epic","year":-725},
{"title":"Emma","genre":"novel","year":1815},
{"title":"Hamlet","genre":"tragedy","year":1603}]}"#,
)
.unwrap()
}
fn rows(q: &str) -> Vec<String> {
let run = quarb::Run::ambient();
let out = quarb::Query::parse(q, &doc())
.unwrap_or_else(|e| panic!("{q}: {e}"))
.run(&doc(), &run)
.unwrap_or_else(|e| panic!("{q}: {e}"));
match out.result {
QueryResult::Values(vs) => vs.iter().map(Value::display_form).collect(),
QueryResult::Nodes(ns) => ns.iter().map(|n| format!("{n:?}")).collect(),
}
}
fn err(q: &str) -> String {
match quarb::Query::parse(q, &doc()) {
Err(e) => e.to_string(),
Ok(_) => panic!("{q}: parsed"),
}
}
#[test]
fn the_body_runs_over_the_whole_context_and_files_on_every_thread() {
assert_eq!(
rows("/books/* | ::year | .n@(count) | %(y = $_; n = $.n)"),
[
"%(y = -750; n = 4)",
"%(y = -725; n = 4)",
"%(y = 1815; n = 4)",
"%(y = 1603; n = 4)"
]
);
assert_eq!(
rows("/books/* | ::title | .all@(join(\", \")) | $.all @| first"),
["Iliad, Odyssey, Emma, Hamlet"]
);
assert_eq!(
rows("/books/* | ::year | .@(sum) | $."),
["1943", "1943", "1943", "1943"]
);
}
#[test]
fn per_partition_the_body_runs_over_each_group_in_the_threads_order() {
assert_eq!(
rows("/books/* | .g(::genre) | ::year | .n@{.g}(count) | %(g = $.g; y = $_; n = $.n)"),
[
"%(g = \"epic\"; y = -750; n = 2)",
"%(g = \"epic\"; y = -725; n = 2)",
"%(g = \"novel\"; y = 1815; n = 1)",
"%(g = \"tragedy\"; y = 1603; n = 1)"
]
);
assert_eq!(
rows(
"/books/* | .g(::genre) | .old(::year < 0) | ::year \
| .n@{.g; .old}(count) | $.n"
),
["2", "2", "1", "1"]
);
assert_eq!(
rows("/books/* | .g(::genre) | ::title | .all@{.g}(join(\"+\")) | $.all @| unique"),
["Iliad+Odyssey", "Emma", "Hamlet"]
);
}
#[test]
fn the_aggregation_pipe_push_collapses_to_the_one_thread_the_pipe_answers() {
assert_eq!(
rows("/books/* | ::year @| .n(count) | %(n = $.n; t = $_)"),
["%(n = 4; t = @(-750; -725; 1815; 1603))"]
);
assert_eq!(
rows("/books/* | .g(::genre) | ::year @|{.g} .n(count) | %(g = $.g; n = $.n; t = $_)"),
[
"%(g = \"epic\"; n = 2; t = @(-750; -725))",
"%(g = \"novel\"; n = 1; t = @(1815))",
"%(g = \"tragedy\"; n = 1; t = @(1603))"
]
);
assert_eq!(
rows("/books/* | ::genre @| .total(count) | .unique(unique @| count) | %."),
["%(total = 4; unique = 3)"]
);
let e = err("/books/* | .n@{.nothere}(count)");
assert!(e.contains("no push '.nothere'"), "{e}");
let e = err("/books/* | .n@");
assert!(e.contains("takes a body"), "{e}");
}
#[test]
fn the_canonical_text_and_the_reflection() {
for q in [
"/books/* | .n@(count)",
"/books/* | .g(::genre) | .n@{.g}(count)",
"/books/* | .g(::genre) | .h(::year) | .n@{.g; .h}(count)",
"/books/* @| .n(count)",
"/books/* | .g(::genre) @|{.g} .n(count)",
] {
let once = quarb::expand(q, &quarb::Defs::default()).unwrap();
assert_eq!(once, q);
}
let arbor =
quarb::reflect::QueryArbor::parse("/books/* | .g(::genre) | .n@{.g}(count)").unwrap();
match quarb::run("/query/pipeline/agg-push::group", &arbor).unwrap() {
QueryResult::Values(vs) => assert_eq!(vs, [Value::Str(".g".into())]),
other => panic!("{other:?}"),
}
}
#[test]
fn threads_prints_the_aggregate_push_as_a_step() {
let text = rows("/books/* | ::year | .n@(count) @| threads").remove(0);
assert!(text.contains("└─ .n@(count) (…; 1603)"), "{text}");
assert!(!text.contains("\n.n@(count)"), "{text}");
}
#[test]
fn the_rounded_spelling_is_the_sigil_spelling() {
assert_eq!(
rows("/books/* | ::year | .n*(count) | $.n"),
rows("/books/* | ::year | .n@(count) | $.n")
);
assert_eq!(
rows("/books/* | .g(::genre) | ::year | .n*(.g)(count) | $.n"),
rows("/books/* | .g(::genre) | ::year | .n@{.g}(count) | $.n")
);
assert_eq!(
quarb::expand(
"/books/* | .g(::genre) | .n*(.g)(count)",
&quarb::Defs::default()
)
.unwrap(),
"/books/* | .g(::genre) | .n@{.g}(count)"
);
assert_eq!(
rows("/books/* | ::year | .h*(.5 + 1) | $.h @| first"),
["1.5"]
);
}
#[test]
fn a_body_call_with_a_surplus_operand_reads_it_as_the_input() {
assert_eq!(
rows("/books/* | %(n = ::year) | .tot@(sum(:n)) | $.tot @| first"),
["1943"]
);
assert_eq!(
rows("/books/* | .g(::genre) | %(n = ::year) | .tot@{.g}(sum(:n)) | $.tot @| unique"),
["-1475", "1815", "1603"]
);
assert_eq!(
rows("/books/* | %(n = ::year) @| .tot(sum(:n)) | $.tot"),
["1943"]
);
assert_eq!(
rows("/books/* | ::title | .two@(first(2)) | $.two @| first"),
["@(\"Iliad\"; \"Odyssey\")"]
);
}
#[test]
fn a_nested_record_keeps_the_context_in_reach() {
assert_eq!(
rows(
"/books/* @| %(y = %(min = (@@_::year @| min); max = (@@_::year @| max)); n = (@@_ @| count))"
),
["%(y = %(min = -750; max = 1815); n = 4)"]
);
}