use serde_json::Value;
use super::super::{Stage, pipeline};
use super::Direction;
use crate::serve::query::words::{self, Role, Status};
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn match_word_describes_the_labelled_graph_grammar() -> TestResult {
let word = words::WORDS
.iter()
.find(|word| word.name == "match")
.ok_or("match word missing")?;
assert_eq!(word.role, Role::Source);
assert_eq!(word.status, Status::Served);
assert_eq!(
word.arguments,
&[
"nodes: (binding:collection) or up to eight labelled nodes joined by directed relations",
"binding: distinct names matching [a-z_][a-z0-9_]*",
"relation: -[:kind]-> or <-[:kind]- at each of up to seven hops",
]
);
assert_eq!(
word.meaning,
"Match a labelled collection or a fixed chain of directed relations first or immediately after its collection."
);
assert!(word.refusal.is_none());
let fixture: Vec<Value> = serde_json::from_str(include_str!("../words.json"))?;
let fixture_word = fixture
.iter()
.find(|word| word["name"] == "match")
.ok_or("match fixture word missing")?;
assert_eq!(*fixture_word, serde_json::to_value(word)?);
assert_eq!(
words::WORDS
.iter()
.filter(|word| word.name == "match")
.count(),
1
);
Ok(())
}
#[test]
fn match_library_examples_have_complete_defaults_and_exact_graph_shapes() -> TestResult {
let group: Value = serde_json::from_str(include_str!("../library/match.json"))?;
assert_eq!(group["group"], "Matching relationships");
let entries = group["entries"].as_array().ok_or("entries missing")?;
assert_eq!(entries.len(), 4);
for (entry, (name, labels, directions, kinds)) in entries.iter().zip([
("Labelled patients", vec!["patients"], vec![], vec![]),
(
"Patients admitted to wards",
vec!["patients", "wards"],
vec![Direction::Outgoing],
vec!["admitted_to"],
),
(
"Wards admitting patients",
vec!["wards", "patients"],
vec![Direction::Incoming],
vec!["admitted_to"],
),
(
"Patients with ward nurses",
vec!["patients", "wards", "nurses"],
vec![Direction::Outgoing, Direction::Incoming],
vec!["admitted_to", "staffed_by"],
),
]) {
assert_eq!(entry["name"], name);
let mut query = entry["query"].as_str().ok_or("query missing")?.to_owned();
let change = entry["change"].as_str().ok_or("change missing")?;
assert!(
!entry["sentence"]
.as_str()
.ok_or("sentence missing")?
.is_empty()
);
for (key, value) in entry["defaults"].as_object().ok_or("defaults missing")? {
let token = format!("${{{key}}}");
let value = value.as_str().ok_or("default is not a string")?;
assert!(!value.is_empty(), "{name}: {key}");
assert!(query.contains(&token), "{name}: unused default {key}");
assert!(change.contains(&token), "{name}: absent change {key}");
query = query.replace(&token, value);
}
assert!(!query.contains("${"), "{name}: unresolved query {query}");
let plan = pipeline(&query).map_err(|error| error.to_string())?;
assert_eq!(plan.stages.len(), 1, "{name}");
let stage = plan.stages.first().ok_or("match stage missing")?;
assert_eq!(stage.span.start, 0);
assert_eq!(stage.span.end, query.len());
let Stage::Match { nodes, relations } = &stage.value else {
return Err(format!("{name}: example did not parse as match").into());
};
assert_eq!(
nodes
.iter()
.map(|node| node.label.value.as_str())
.collect::<Vec<_>>(),
labels
);
assert_eq!(
relations
.iter()
.map(|relation| relation.direction)
.collect::<Vec<_>>(),
directions
);
assert_eq!(
relations
.iter()
.map(|relation| relation.kind.value.as_str())
.collect::<Vec<_>>(),
kinds
);
}
Ok(())
}