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, boxed_test_simple, $crate::steps::boxed::test_boxed_simple}
151        $crate::general_test!{$setup, boxed_test_complex_traversal, $crate::steps::boxed::test_boxed_complex_traversal}
152        $crate::general_test!{$setup, boxed_test_ultra_long_traversal, $crate::steps::boxed::test_boxed_ultra_long_traversal}
153        $crate::general_test!{$setup, boxed_test_mixed_operations, $crate::steps::boxed::test_boxed_mixed_operations}
154        $crate::general_test!{$setup, boxed_test_edge_walker, $crate::steps::boxed::test_boxed_edge_walker}
155        $crate::general_test!{$setup, boxed_test_performance_equivalence, $crate::steps::boxed::test_boxed_performance_equivalence}
156        $crate::general_test!{$setup, boxed_test_with_context, $crate::steps::boxed::test_boxed_with_context}
157        $crate::general_test!{$setup, filter_test_vertices_filter, $crate::steps::filter::test_vertices_filter}
158        $crate::general_test!{$setup, filter_test_edges_filter, $crate::steps::filter::test_edges_filter}
159        $crate::general_test!{$setup, vertices_test_vertices_collect, $crate::steps::collect::test_vertices_collect}
160        $crate::general_test!{$setup, vertices_test_edges_collect, $crate::steps::collect::test_edges_collect}
161        $crate::general_test!{$setup, edges_test_out_edges, $crate::steps::edges::test_out_edges}
162        $crate::general_test!{$setup, edges_test_out_edges_limit, $crate::steps::edges::test_out_edges_limit}
163        $crate::general_test!{$setup, edges_test_in_edges, $crate::steps::edges::test_in_edges}
164        $crate::general_test!{$setup, edges_test_in_edges_limit, $crate::steps::edges::test_in_edges_limit}
165        $crate::general_test!{$setup, edges_test_all_edges, $crate::steps::edges::test_all_edges}
166        $crate::general_test!{$setup, edges_test_all_edges_limit, $crate::steps::edges::test_all_edges_limit}
167        $crate::general_test!{$setup, edges_test_out_edges_filtered, $crate::steps::edges::test_out_edges_filtered}
168        $crate::general_test!{$setup, edges_test_out_edges_filtered_limit, $crate::steps::edges::test_out_edges_filtered_limit}
169        $crate::general_test!{$setup, edges_test_in_edges_filtered, $crate::steps::edges::test_in_edges_filtered}
170        $crate::general_test!{$setup, edges_test_in_edges_filtered_limit, $crate::steps::edges::test_in_edges_filtered_limit}
171        $crate::general_test!{$setup, edges_test_all_edges_filtered, $crate::steps::edges::test_all_edges_filtered}
172        $crate::general_test!{$setup, edges_test_all_edges_filtered_limit, $crate::steps::edges::test_all_edges_filtered_limit}
173        $crate::general_test!{$setup, context_test_vertices_context, $crate::steps::context::test_vertices_context}
174        $crate::general_test!{$setup, vertices_test_take, $crate::steps::vertices::test_take}
175        $crate::general_test!{$setup, vertices_test_head, $crate::steps::vertices::test_head}
176        $crate::general_test!{$setup, vertices_test_tail, $crate::steps::vertices::test_tail}
177        $crate::general_test!{$setup, mutate_context_vertex, $crate::steps::mutate_context::test_vertex_mutate_context}
178        $crate::general_test!{$setup, mutate_context_edge, $crate::steps::mutate_context::test_edge_mutate_context}
179        $crate::general_test!{$setup, mutation_test_mutation, $crate::steps::mutation::test_mutation}
180        $crate::general_test!{$setup, mutation_test_edge_mutation, $crate::steps::mutation::test_edge_mutation}
181        $crate::general_test!{$setup, count_test_vertices_count, $crate::steps::count::test_vertices_count}
182        $crate::general_test!{$setup, count_test_edges_count, $crate::steps::count::test_edges_count}
183        $crate::general_test!{$setup, take_test_vertices_take, $crate::steps::take::test_vertices_take}
184        $crate::general_test!{$setup, take_test_edges_take, $crate::steps::take::test_edges_take}
185        $crate::general_test!{$setup, first_test_vertices_first, $crate::steps::first::test_vertices_first}
186        $crate::general_test!{$setup, first_test_edges_first, $crate::steps::first::test_edges_first}
187        $crate::general_test!{$setup, fold_test_vertices_fold, $crate::steps::fold::test_vertices_fold}
188        $crate::general_test!{$setup, fold_test_edges_fold, $crate::steps::fold::test_edges_fold}
189        $crate::general_test!{$setup, reduce_test_vertices_reduce, $crate::steps::reduce::test_vertices_reduce}
190        $crate::general_test!{$setup, reduce_test_edges_reduce, $crate::steps::reduce::test_edges_reduce}
191        $crate::general_test!{$setup, detour_test_vertices_detour, $crate::steps::detour::test_vertices_detour}
192        $crate::general_test!{$setup, filter_derive_test_vertices_filter, $crate::steps::filter_derive::test_vertices_filter}
193        $crate::general_test!{$setup, filter_derive_test_edges_filter, $crate::steps::filter_derive::test_edges_filter}
194        $crate::general_test!{$setup, probe_test_vertices_probe, $crate::steps::probe::test_vertices_probe}
195        $crate::general_test!{$setup, probe_test_edges_probe, $crate::steps::probe::test_edges_probe}
196        $crate::general_test!{$setup, control_flow_test_vertices_control_flow, $crate::steps::control_flow::test_vertices_control_flow}
197        $crate::general_test!{$setup, control_flow_test_edges_control_flow, $crate::steps::control_flow::test_edges_control_flow}
198        $crate::general_test!{$setup, index_edge_label_test_index, $crate::index::edge_label::test_index}
199        $crate::general_test!{$setup, index_edge_label_test_index_limit, $crate::index::edge_label::test_index_limit}
200        $crate::general_test!{$setup, index_vertex_label_test_index, $crate::index::vertex_label::test_index}
201        $crate::general_test!{$setup, index_vertex_label_test_index_limit, $crate::index::vertex_label::test_index_limit}
202        $crate::general_test!{$setup, index_vertex_hash_test_index, $crate::index::vertex_hash::test_index}
203        $crate::general_test!{$setup, index_vertex_hash_test_index_remove, $crate::index::vertex_hash::test_index_remove}
204        $crate::general_test!{$setup, index_vertex_hash_test_index_update, $crate::index::vertex_hash::test_index_update}
205        $crate::general_test!{$setup, index_vertex_full_text_test_index, $crate::index::vertex_full_text::test_index}
206        $crate::general_test!{$setup, index_vertex_full_text_test_index_remove, $crate::index::vertex_full_text::test_index_remove}
207        $crate::general_test!{$setup, index_vertex_full_text_test_index_update, $crate::index::vertex_full_text::test_index_update}
208        $crate::general_test!{$setup, index_vertex_range_test_index, $crate::index::vertex_range::test_index}
209        $crate::general_test!{$setup, index_vertex_range_test_index_remove, $crate::index::vertex_range::test_index_remove}
210        $crate::general_test!{$setup, index_vertex_range_test_index_update, $crate::index::vertex_range::test_index_update}
211
212        $crate::proptest! {
213            #[test]
214            fn fuzz_test(operations in $crate::collection::vec($crate::fuzz::arb_graph_operation(), 0..100)) {
215                $crate::fuzz::test_fuzz($setup, operations);
216            }
217        }
218    };
219}
220
221impl Display for TestError {
222    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
223        match self {
224            TestError::Mismatch {
225                missing,
226                extra,
227                expected,
228            } => {
229                if !missing.is_empty() {
230                    write!(f, "Missing elements:\n{}\n", missing.join("\n"),)?;
231                }
232                if !extra.is_empty() {
233                    write!(f, "Extra elements:\n{}\n", extra.join("\n"),)?;
234                }
235                write!(f, "Expected elements:\n{}", expected.join("\n"))?;
236            }
237            TestError::MoreThanOneElement { actual, expected } => {
238                write!(
239                    f,
240                    "Expected one of:\n{}\nBut got:\n{}",
241                    expected.join("\n"),
242                    actual.join("\n")
243                )?;
244            }
245        }
246
247        Ok(())
248    }
249}
250
251pub fn assert_elements_one_of<Graph>(
252    graph: &Graph,
253    actual: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
254    expected: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
255) -> Result<(), TestError>
256where
257    Graph: graph_api_lib::Graph,
258{
259    let actual: Vec<ElementId<Graph>> = actual.into_iter().map(Into::into).collect();
260    let expected: Vec<ElementId<Graph>> = expected.into_iter().map(Into::into).collect();
261
262    // First convert to debug strings for error reporting
263    let actual_strings: Vec<String> = actual.iter().map(|e| graph.dbg(*e)).collect();
264    let expected_strings: Vec<String> = expected.iter().map(|e| graph.dbg(*e)).collect();
265
266    if actual.len() != 1 {
267        return Err(TestError::MoreThanOneElement {
268            expected: expected_strings,
269            actual: actual_strings,
270        });
271    }
272
273    Ok(())
274}
275
276pub fn assert_elements_eq<Graph>(
277    graph: &Graph,
278    actual: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
279    expected: impl IntoIterator<Item = impl Into<ElementId<Graph>>>,
280) -> Result<(), TestError>
281where
282    Graph: graph_api_lib::Graph,
283{
284    let actual = actual.into_iter().map(Into::into).collect::<HashSet<_>>();
285    let expected = expected.into_iter().map(Into::into).collect::<HashSet<_>>();
286    if actual != expected {
287        let missing: Vec<String> = expected
288            .difference(&actual)
289            .map(|e| graph.dbg(*e))
290            .collect();
291        let extra: Vec<String> = actual
292            .difference(&expected)
293            .map(|e| graph.dbg(*e))
294            .collect();
295
296        let expected: Vec<String> = expected.iter().map(|e| graph.dbg(*e)).collect();
297
298        return Err(TestError::Mismatch {
299            missing,
300            extra,
301            expected,
302        });
303    }
304    Ok(())
305}
306
307#[macro_export]
308macro_rules! assert_elements_eq {
309    ($graph:expr, $actual:expr, $expected:expr) => {
310        if let Err(e) = $crate::assert_elements_eq($graph, $actual, $expected) {
311            panic!("{}", e);
312        }
313    };
314}
315
316#[macro_export]
317macro_rules! assert_elements_one_of {
318    ($graph:expr, $actual:expr, $expected:expr) => {
319        if let Err(e) = $crate::assert_elements_one_of($graph, $actual, $expected) {
320            panic!("{}", e);
321        }
322    };
323}