graph_api_test/
lib.rs

1pub extern crate proptest;
2pub use proptest::*;
3pub mod fuzz;
4pub mod graph;
5pub mod index;
6pub mod steps;
7
8use graph_api_derive::{EdgeExt, VertexExt};
9use graph_api_lib::ElementId;
10#[allow(unused_imports)]
11use graph_api_lib::{
12    SupportsClear, SupportsEdgeAdjacentLabelIndex, SupportsEdgeHashIndex, SupportsEdgeLabelIndex,
13    SupportsEdgeRangeIndex, SupportsVertexFullTextIndex, SupportsVertexHashIndex,
14    SupportsVertexLabelIndex, SupportsVertexRangeIndex,
15};
16use std::collections::HashSet;
17use std::fmt::{Debug, Display, Formatter};
18use thiserror::Error;
19use uuid::Uuid;
20
21#[derive(Debug, Clone, VertexExt)]
22pub enum Vertex {
23    Person {
24        #[index(hash)]
25        name: String,
26        #[index(range)]
27        age: u64,
28        #[index(hash)]
29        unique_id: Uuid,
30        #[index(range)]
31        username: String,
32        #[index(full_text)]
33        biography: String,
34    },
35    Project(Project),
36    Rust,
37}
38
39#[derive(Debug, Clone)]
40pub struct Project {
41    pub name: String,
42}
43
44#[derive(Debug, Clone, EdgeExt)]
45pub enum Edge {
46    Knows { since: i32 },
47    Created,
48    Language(Language),
49}
50#[derive(Debug, Clone)]
51pub struct Language {
52    pub name: String,
53}
54pub struct Refs<Graph>
55where
56    Graph: graph_api_lib::Graph,
57{
58    pub bryn: Graph::VertexId,
59    pub julia: Graph::VertexId,
60    pub graph_api: Graph::VertexId,
61    pub rust: Graph::VertexId,
62    pub bryn_knows_julia: Graph::EdgeId,
63    pub julia_knows_bryn: Graph::EdgeId,
64    pub bryn_created_graph_api: Graph::EdgeId,
65    pub graph_api_language_rust: Graph::EdgeId,
66}
67
68pub fn populate_graph<Graph>(graph: &mut Graph) -> Refs<Graph>
69where
70    Graph: graph_api_lib::Graph<Vertex = Vertex, Edge = Edge>,
71{
72    // Hey Tinkerpop folks, long time no see! Hope things are going well!
73    let bryn = graph.add_vertex(Vertex::Person {
74        name: "Bryn".to_string(),
75        age: 45,
76        unique_id: Uuid::from_u128(1),
77        username: "bryn".to_string(),
78        biography: "Did some graph stuff".to_string(),
79    });
80    let julia = graph.add_vertex(Vertex::Person {
81        name: "Julia".to_string(),
82        age: 48,
83        unique_id: Uuid::from_u128(2),
84        username: "julia".to_string(),
85        biography: "Mastered the English language".to_string(),
86    });
87    let graph_api = graph.add_vertex(Vertex::Project(Project {
88        name: "GraphApi".to_string(),
89    }));
90
91    let rust = graph.add_vertex(Vertex::Rust);
92
93    let bryn_knows_julia = graph.add_edge(bryn, julia, Edge::Knows { since: 1999 });
94    let julia_knows_bryn = graph.add_edge(julia, bryn, Edge::Knows { since: 1999 });
95    let bryn_created_graph_api = graph.add_edge(bryn, graph_api, Edge::Created);
96    let graph_api_language_rust = graph.add_edge(
97        graph_api,
98        rust,
99        Edge::Language(Language {
100            name: "Rust".to_string(),
101        }),
102    );
103
104    Refs {
105        bryn,
106        julia,
107        graph_api,
108        rust,
109        bryn_knows_julia,
110        julia_knows_bryn,
111        bryn_created_graph_api,
112        graph_api_language_rust,
113    }
114}
115
116#[derive(Error, Debug)]
117pub enum TestError {
118    Mismatch {
119        missing: Vec<String>,
120        extra: Vec<String>,
121        expected: Vec<String>,
122    },
123    MoreThanOneElement {
124        expected: Vec<String>,
125        actual: Vec<String>,
126    },
127}
128
129#[macro_export]
130macro_rules! general_test {
131    ($setup:expr, $name:ident, $path:path) => {
132        #[test]
133        fn $name() {
134            let mut g = $setup;
135            $path(&mut g);
136        }
137    };
138}
139
140#[macro_export]
141macro_rules! test_suite {
142    ($setup:expr) => {
143        $crate::general_test!{$setup, graph_test_add_vertex, $crate::graph::test_add_vertex}
144        $crate::general_test!{$setup, graph_test_mutate_vertex, $crate::graph::test_mutate_vertex}
145        $crate::general_test!{$setup, graph_test_remove_vertex, $crate::graph::test_remove_vertex}
146        $crate::general_test!{$setup, graph_test_add_edge, $crate::graph::test_add_edge}
147        $crate::general_test!{$setup, graph_test_mutate_edge, $crate::graph::test_mutate_edge}
148        $crate::general_test!{$setup, graph_test_remove_edge, $crate::graph::test_remove_edge}
149        $crate::general_test!{$setup, graph_test_remove_vertex_with_edges, $crate::graph::test_remove_vertex_with_edges}
150        $crate::general_test!{$setup, filter_test_vertices_filter, $crate::steps::filter::test_vertices_filter}
151        $crate::general_test!{$setup, filter_test_edges_filter, $crate::steps::filter::test_edges_filter}
152        $crate::general_test!{$setup, vertices_test_vertices_collect, $crate::steps::collect::test_vertices_collect}
153        $crate::general_test!{$setup, vertices_test_edges_collect, $crate::steps::collect::test_edges_collect}
154        $crate::general_test!{$setup, edges_test_out_edges, $crate::steps::edges::test_out_edges}
155        $crate::general_test!{$setup, edges_test_out_edges_limit, $crate::steps::edges::test_out_edges_limit}
156        $crate::general_test!{$setup, edges_test_in_edges, $crate::steps::edges::test_in_edges}
157        $crate::general_test!{$setup, edges_test_in_edges_limit, $crate::steps::edges::test_in_edges_limit}
158        $crate::general_test!{$setup, edges_test_all_edges, $crate::steps::edges::test_all_edges}
159        $crate::general_test!{$setup, edges_test_all_edges_limit, $crate::steps::edges::test_all_edges_limit}
160        $crate::general_test!{$setup, edges_test_out_edges_filtered, $crate::steps::edges::test_out_edges_filtered}
161        $crate::general_test!{$setup, edges_test_out_edges_filtered_limit, $crate::steps::edges::test_out_edges_filtered_limit}
162        $crate::general_test!{$setup, edges_test_in_edges_filtered, $crate::steps::edges::test_in_edges_filtered}
163        $crate::general_test!{$setup, edges_test_in_edges_filtered_limit, $crate::steps::edges::test_in_edges_filtered_limit}
164        $crate::general_test!{$setup, edges_test_all_edges_filtered, $crate::steps::edges::test_all_edges_filtered}
165        $crate::general_test!{$setup, edges_test_all_edges_filtered_limit, $crate::steps::edges::test_all_edges_filtered_limit}
166        $crate::general_test!{$setup, context_test_vertices_context, $crate::steps::context::test_vertices_context}
167        $crate::general_test!{$setup, vertices_test_take, $crate::steps::vertices::test_take}
168        $crate::general_test!{$setup, vertices_test_head, $crate::steps::vertices::test_head}
169        $crate::general_test!{$setup, vertices_test_tail, $crate::steps::vertices::test_tail}
170        $crate::general_test!{$setup, mutate_context_vertex, $crate::steps::mutate_context::test_vertex_mutate_context}
171        $crate::general_test!{$setup, mutate_context_edge, $crate::steps::mutate_context::test_edge_mutate_context}
172        $crate::general_test!{$setup, mutation_test_mutation, $crate::steps::mutation::test_mutation}
173        $crate::general_test!{$setup, mutation_test_edge_mutation, $crate::steps::mutation::test_edge_mutation}
174        $crate::general_test!{$setup, count_test_vertices_count, $crate::steps::count::test_vertices_count}
175        $crate::general_test!{$setup, count_test_edges_count, $crate::steps::count::test_edges_count}
176        $crate::general_test!{$setup, take_test_vertices_take, $crate::steps::take::test_vertices_take}
177        $crate::general_test!{$setup, take_test_edges_take, $crate::steps::take::test_edges_take}
178        $crate::general_test!{$setup, first_test_vertices_first, $crate::steps::first::test_vertices_first}
179        $crate::general_test!{$setup, first_test_edges_first, $crate::steps::first::test_edges_first}
180        $crate::general_test!{$setup, fold_test_vertices_fold, $crate::steps::fold::test_vertices_fold}
181        $crate::general_test!{$setup, fold_test_edges_fold, $crate::steps::fold::test_edges_fold}
182        $crate::general_test!{$setup, reduce_test_vertices_reduce, $crate::steps::reduce::test_vertices_reduce}
183        $crate::general_test!{$setup, reduce_test_edges_reduce, $crate::steps::reduce::test_edges_reduce}
184        $crate::general_test!{$setup, detour_test_vertices_detour, $crate::steps::detour::test_vertices_detour}
185        $crate::general_test!{$setup, filter_derive_test_vertices_filter, $crate::steps::filter_derive::test_vertices_filter}
186        $crate::general_test!{$setup, filter_derive_test_edges_filter, $crate::steps::filter_derive::test_edges_filter}
187        $crate::general_test!{$setup, probe_test_vertices_probe, $crate::steps::probe::test_vertices_probe}
188        $crate::general_test!{$setup, probe_test_edges_probe, $crate::steps::probe::test_edges_probe}
189        $crate::general_test!{$setup, control_flow_test_vertices_control_flow, $crate::steps::control_flow::test_vertices_control_flow}
190        $crate::general_test!{$setup, control_flow_test_edges_control_flow, $crate::steps::control_flow::test_edges_control_flow}
191        $crate::general_test!{$setup, index_edge_label_test_index, $crate::index::edge_label::test_index}
192        $crate::general_test!{$setup, index_edge_label_test_index_limit, $crate::index::edge_label::test_index_limit}
193        $crate::general_test!{$setup, index_vertex_label_test_index, $crate::index::vertex_label::test_index}
194        $crate::general_test!{$setup, index_vertex_label_test_index_limit, $crate::index::vertex_label::test_index_limit}
195        $crate::general_test!{$setup, index_vertex_hash_test_index, $crate::index::vertex_hash::test_index}
196        $crate::general_test!{$setup, index_vertex_hash_test_index_remove, $crate::index::vertex_hash::test_index_remove}
197        $crate::general_test!{$setup, index_vertex_hash_test_index_update, $crate::index::vertex_hash::test_index_update}
198        $crate::general_test!{$setup, index_vertex_full_text_test_index, $crate::index::vertex_full_text::test_index}
199        $crate::general_test!{$setup, index_vertex_full_text_test_index_remove, $crate::index::vertex_full_text::test_index_remove}
200        $crate::general_test!{$setup, index_vertex_full_text_test_index_update, $crate::index::vertex_full_text::test_index_update}
201        $crate::general_test!{$setup, index_vertex_range_test_index, $crate::index::vertex_range::test_index}
202        $crate::general_test!{$setup, index_vertex_range_test_index_remove, $crate::index::vertex_range::test_index_remove}
203        $crate::general_test!{$setup, index_vertex_range_test_index_update, $crate::index::vertex_range::test_index_update}
204
205        $crate::proptest! {
206            #[test]
207            fn fuzz_test(operations in $crate::collection::vec($crate::fuzz::arb_graph_operation(), 0..100)) {
208                $crate::fuzz::test_fuzz($setup, operations);
209            }
210        }
211    };
212}
213
214impl Display for TestError {
215    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
216        match self {
217            TestError::Mismatch {
218                missing,
219                extra,
220                expected,
221            } => {
222                if !missing.is_empty() {
223                    write!(f, "Missing elements:\n{}\n", missing.join("\n"),)?;
224                }
225                if !extra.is_empty() {
226                    write!(f, "Extra elements:\n{}\n", extra.join("\n"),)?;
227                }
228                write!(f, "Expected elements:\n{}", expected.join("\n"))?;
229            }
230            TestError::MoreThanOneElement { actual, expected } => {
231                write!(
232                    f,
233                    "Expected one of:\n{}\nBut got:\n{}",
234                    expected.join("\n"),
235                    actual.join("\n")
236                )?;
237            }
238        }
239
240        Ok(())
241    }
242}
243
244pub fn assert_elements_one_of<Graph>(
245    graph: &Graph,
246    actual: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
247    expected: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
248) -> Result<(), TestError>
249where
250    Graph: graph_api_lib::Graph,
251{
252    let actual: Vec<ElementId<Graph>> = actual.into_iter().map(Into::into).collect();
253    let expected: Vec<ElementId<Graph>> = expected.into_iter().map(Into::into).collect();
254
255    // First convert to debug strings for error reporting
256    let actual_strings: Vec<String> = actual.iter().map(|e| graph.dbg(*e)).collect();
257    let expected_strings: Vec<String> = expected.iter().map(|e| graph.dbg(*e)).collect();
258
259    if actual.len() != 1 {
260        return Err(TestError::MoreThanOneElement {
261            expected: expected_strings,
262            actual: actual_strings,
263        });
264    }
265
266    Ok(())
267}
268
269pub fn assert_elements_eq<Graph>(
270    graph: &Graph,
271    actual: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
272    expected: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
273) -> Result<(), TestError>
274where
275    Graph: graph_api_lib::Graph,
276{
277    let actual = actual.into_iter().map(Into::into).collect::<HashSet<_>>();
278    let expected = expected.into_iter().map(Into::into).collect::<HashSet<_>>();
279    if actual != expected {
280        let missing: Vec<String> = expected
281            .difference(&actual)
282            .map(|e| graph.dbg(*e))
283            .collect();
284        let extra: Vec<String> = actual
285            .difference(&expected)
286            .map(|e| graph.dbg(*e))
287            .collect();
288
289        let expected: Vec<String> = expected.iter().map(|e| graph.dbg(*e)).collect();
290
291        return Err(TestError::Mismatch {
292            missing,
293            extra,
294            expected,
295        });
296    }
297    Ok(())
298}
299
300#[macro_export]
301macro_rules! assert_elements_eq {
302    ($graph:expr, $actual:expr, $expected:expr) => {
303        if let Err(e) = $crate::assert_elements_eq($graph, $actual, $expected) {
304            panic!("{}", e);
305        }
306    };
307}
308
309#[macro_export]
310macro_rules! assert_elements_one_of {
311    ($graph:expr, $actual:expr, $expected:expr) => {
312        if let Err(e) = $crate::assert_elements_one_of($graph, $actual, $expected) {
313            panic!("{}", e);
314        }
315    };
316}