1#![deny(unsafe_code)]
34
35pub mod batch;
36pub mod expr;
37pub mod graph;
38pub mod index;
39pub mod prelude;
40pub mod projection;
41pub mod query;
42pub mod traversal;
43pub mod value;
44
45#[cfg(test)]
46mod tests {
47 use crate::prelude::*;
48
49 fn query_entry(entry: &BatchEntry) -> &NamedQuery {
50 match entry {
51 BatchEntry::Query(query) => query.as_ref(),
52 BatchEntry::ForEach { .. } => panic!("expected query entry"),
53 }
54 }
55
56 #[test]
57 fn traversal_builds_nested_ast_json() {
58 let batch = read_batch()
59 .var_as(
60 "users",
61 g().n_with_label("User")
62 .where_(Predicate::eq("username", "alice"))
63 .limit(1usize)
64 .value_map(Some(vec!["$id", "username"])),
65 )
66 .returning(["users"]);
67
68 let query = query_entry(&batch.entries()[0]);
69 assert!(matches!(query.root, AstNode::ValueMap { .. }));
70
71 let json = sonic_rs::to_string(&QueryRequest::read(batch)).unwrap();
72 assert!(json.contains(r#""root":{"value_map":{"input":{"limit""#));
73 assert!(json.contains(r#""eq":{"left":{"property":"username"}"#));
74 assert!(!json.contains("steps"));
75 }
76
77 #[test]
78 fn sub_traversal_starts_from_context() {
79 let traversal = g()
80 .n(1u64)
81 .union(vec![sub().out(Some("FOLLOWS")).limit(10usize)]);
82 let AstNode::Union { traversals, .. } = traversal.into_ast() else {
83 panic!("expected union");
84 };
85 let AstNode::Limit { input, .. } = &*traversals[0].root else {
86 panic!("expected limit");
87 };
88 assert!(matches!(input.as_ref(), AstNode::Out { .. }));
89 }
90
91 #[test]
92 fn shortest_path_builds_terminal_ast_json() {
93 let batch = read_batch()
94 .var_as(
95 "path",
96 g().shortest_path_with(
97 NodeRef::id(1),
98 NodeRef::param("target"),
99 Some("FOLLOWS"),
100 ShortestPathDirection::Both,
101 5,
102 ),
103 )
104 .returning(["path"]);
105
106 let query = query_entry(&batch.entries()[0]);
107 assert!(matches!(query.root, AstNode::ShortestPath { .. }));
108
109 let json = sonic_rs::to_string(&QueryRequest::read(batch)).unwrap();
110 assert!(json.contains(r#""shortest_path""#));
111 assert!(json.contains(r#""direction":"both""#));
112 assert!(json.contains(r#""max_depth":5"#));
113 assert!(json.contains(r#""target":{"param":"target"}"#));
114 }
115
116 #[test]
117 fn row_binding_builder_invariants() {
118 assert!(std::panic::catch_unwind(|| {
119 let _ = g().n(1u64).bind("");
120 })
121 .is_err());
122
123 assert!(std::panic::catch_unwind(|| {
124 let _ = BindingProjection::binding("service", "$id", "");
125 })
126 .is_err());
127
128 assert!(std::panic::catch_unwind(|| {
129 let _ = BindingProjection::coalesce(Vec::new(), "workload_id");
130 })
131 .is_err());
132
133 assert!(std::panic::catch_unwind(|| {
134 let _ = g().n(1u64).project_bindings(Vec::new());
135 })
136 .is_err());
137 }
138}