loadmaster_probe/
check.rs1use serde_json::Value;
4
5use crate::client::Exchange;
6use crate::fixture::{Cond, Ref, Test};
7
8fn exchange(xs: &[Exchange], n: Option<usize>) -> Option<&Exchange> {
9 match n {
10 Some(i) => xs.get(i),
11 None => xs.last(),
12 }
13}
14
15pub fn query(xs: &[Exchange], r: &Ref) -> Vec<Value> {
17 let Some(x) = exchange(xs, r.exchange) else {
18 return Vec::new();
19 };
20 match serde_json_path::JsonPath::parse(&r.path) {
21 Ok(p) => p.query(&x.response).all().into_iter().cloned().collect(),
22 Err(_) => Vec::new(),
23 }
24}
25
26fn as_json(v: &Value) -> Value {
28 match v {
29 Value::String(s) => serde_json::from_str(s).unwrap_or_else(|_| v.clone()),
30 other => other.clone(),
31 }
32}
33
34pub fn eval(c: &Cond, xs: &[Exchange]) -> bool {
35 match c {
36 Cond::Always => true,
37 Cond::All(cs) => cs.iter().all(|c| eval(c, xs)),
38 Cond::Any(cs) => cs.iter().any(|c| eval(c, xs)),
39 Cond::Not(c) => !eval(c, xs),
40 Cond::Ran(n) => xs.len() > *n,
41 Cond::Status {
42 exchange: n,
43 code,
44 class,
45 } => exchange(xs, *n).is_some_and(|x| {
46 code.is_none_or(|c| x.status == c) && class.is_none_or(|k| x.status / 100 == k)
47 }),
48 Cond::StreamDone { exchange: n, done } => {
49 exchange(xs, *n).is_some_and(|x| x.stream.as_ref().is_some_and(|s| s.done == *done))
50 }
51 Cond::Same(a, b) => {
52 let (va, vb) = (query(xs, a), query(xs, b));
53 !va.is_empty()
54 && va.len() == vb.len()
55 && va
56 .iter()
57 .zip(&vb)
58 .all(|(x, y)| !x.is_null() && as_json(x) == as_json(y))
59 }
60 Cond::Path(r, t) => {
61 let ms = query(xs, r);
62 match t {
63 Test::Exists(want) => ms.iter().any(|v| !v.is_null()) == *want,
64 Test::Count { min, max } => {
65 let n = match ms.as_slice() {
66 [Value::Array(a)] => a.len() as u64,
67 [Value::Null] | [] => 0,
68 many => many.len() as u64,
69 };
70 min.is_none_or(|m| n >= m) && max.is_none_or(|m| n <= m)
71 }
72 Test::Equals(v) => ms.first() == Some(v),
73 Test::ContainsAny(needles) => ms
74 .iter()
75 .filter_map(Value::as_str)
76 .any(|s| needles.iter().any(|n| s.contains(n.as_str()))),
77 Test::Json(want) => {
78 !ms.is_empty()
79 && ms.iter().all(|v| {
80 v.as_str()
81 .and_then(|s| serde_json::from_str::<Value>(s).ok())
82 .is_some()
83 }) == *want
84 }
85 Test::Schema { schema, parse } => {
86 let Ok(validator) = jsonschema::validator_for(schema) else {
87 return false;
88 };
89 !ms.is_empty()
90 && ms.iter().all(|v| {
91 let inst = if *parse {
92 v.as_str().and_then(|s| serde_json::from_str(s).ok())
93 } else {
94 Some(v.clone())
95 };
96 inst.is_some_and(|i| validator.is_valid(&i))
97 })
98 }
99 }
100 }
101 }
102}