From 0bd9f9a06e083933cb3fbcf241e577d2787ae784 Mon Sep 17 00:00:00 2001 From: Allan Clements Date: Tue, 12 Nov 2024 23:32:41 -0600 Subject: [PATCH] Consolidated integration_client tests --- gremlin-client/src/conversion.rs | 87 +++- gremlin-client/src/io/graph_binary_v1.rs | 87 +++- gremlin-client/src/io/mod.rs | 2 +- gremlin-client/tests/common.rs | 14 +- gremlin-client/tests/integration_client.rs | 238 +++++---- gremlin-client/tests/integration_client_v2.rs | 466 ------------------ 6 files changed, 306 insertions(+), 588 deletions(-) delete mode 100644 gremlin-client/tests/integration_client_v2.rs diff --git a/gremlin-client/src/conversion.rs b/gremlin-client/src/conversion.rs index 7d2f3fdc..03333ff3 100644 --- a/gremlin-client/src/conversion.rs +++ b/gremlin-client/src/conversion.rs @@ -129,8 +129,6 @@ impl_from_gvalue!(bool, GValue::Bool); impl_from_gvalue!(uuid::Uuid, GValue::Uuid); impl_from_gvalue!(Metric, GValue::Metric); impl_from_gvalue!(TraversalMetrics, GValue::TraversalMetrics); -impl_from_gvalue!(TraversalExplanation, GValue::TraversalExplanation); -impl_from_gvalue!(IntermediateRepr, GValue::IntermediateRepr); impl_from_gvalue!(chrono::DateTime, GValue::Date); impl_from_gvalue!(Traverser, GValue::Traverser); @@ -139,9 +137,90 @@ impl FromGValue for Null { match v { GValue::Null => Ok(crate::structure::Null {}), _ => Err(GremlinError::Cast(format!( - "Cannot convert {:?} to {}", + "Cannot convert {:?} to Null", v, - stringify!($t) + ))), + } + } +} + +impl FromGValue for TraversalExplanation { + fn from_gvalue(v: GValue) -> GremlinResult { + match v { + GValue::TraversalExplanation(traversal_explaination) => Ok(traversal_explaination), + //GraphBinary sends TraversalExplainations as just a map instead of as a distinct type, + //so handle converting it here + GValue::Map(mut map) => { + let original = map.remove("original").ok_or(GremlinError::Cast(format!( + "Missing expected \"original\" TraversalExplaination property" + )))?; + let intermediate = + map.remove("intermediate") + .ok_or(GremlinError::Cast(format!( + "Missing expected \"intermediate\" TraversalExplaination property" + )))?; + let final_ = map.remove("final").ok_or(GremlinError::Cast(format!( + "Missing expected \"final\" TraversalExplaination property" + )))?; + + let original = ::from_gvalue(original)?; + let intermediate = ::from_gvalue(intermediate)?; + let final_ = ::from_gvalue(final_)?; + + let original: GremlinResult> = original + .into_iter() + .map(|val| ::from_gvalue(val)) + .collect(); + let final_: GremlinResult> = final_ + .into_iter() + .map(|val| ::from_gvalue(val)) + .collect(); + let intermediate: GremlinResult> = intermediate + .into_iter() + .map(|val| IntermediateRepr::from_gvalue(val)) + .collect(); + + Ok(TraversalExplanation::new(original?, final_?, intermediate?)) + } + _ => Err(GremlinError::Cast(format!( + "Cannot convert {:?} to TraversalExplanation", + v + ))), + } + } +} + +impl FromGValue for IntermediateRepr { + fn from_gvalue(v: GValue) -> GremlinResult { + match v { + GValue::IntermediateRepr(ir) => Ok(ir), + //GraphBinary sends TraversalExplainations as just a map instead of as a distinct type, + //so handle converting it here + GValue::Map(mut map) => { + let traversal = map.remove("traversal").ok_or(GremlinError::Cast(format!( + "Missing expected \"traversal\" IntermediateRepr property" + )))?; + let category = map.remove("category").ok_or(GremlinError::Cast(format!( + "Missing expected \"category\" IntermediateRepr property" + )))?; + let strategy = map.remove("strategy").ok_or(GremlinError::Cast(format!( + "Missing expected \"strategy\" IntermediateRepr property" + )))?; + + let traversal: GremlinResult> = + ::from_gvalue(traversal)? + .into_iter() + .map(|val| ::from_gvalue(val)) + .collect(); + Ok(IntermediateRepr::new( + traversal?, + ::from_gvalue(strategy)?, + ::from_gvalue(category)?, + )) + } + _ => Err(GremlinError::Cast(format!( + "Cannot convert {:?} to IntermediateRepr", + v ))), } } diff --git a/gremlin-client/src/io/graph_binary_v1.rs b/gremlin-client/src/io/graph_binary_v1.rs index cc48704e..1182fc87 100644 --- a/gremlin-client/src/io/graph_binary_v1.rs +++ b/gremlin-client/src/io/graph_binary_v1.rs @@ -9,8 +9,8 @@ use crate::{ message::{ReponseStatus, Response, ResponseResult}, process::traversal::{Instruction, Order, Scope}, structure::{Column, Direction, Merge, Pop, TextP, Traverser, T}, - Cardinality, Edge, GKey, GValue, GremlinError, GremlinResult, Path, ToGValue, Vertex, - VertexProperty, GID, + Cardinality, Edge, GKey, GValue, GremlinError, GremlinResult, Metric, Path, ToGValue, + TraversalMetrics, Vertex, VertexProperty, GID, }; use super::IoProtocol; @@ -55,6 +55,8 @@ const T: u8 = 0x20; const TRAVERSER: u8 = 0x21; const BOOLEAN: u8 = 0x27; const TEXTP: u8 = 0x28; +const MERTRICS: u8 = 0x2C; +const TRAVERSAL_MERTRICS: u8 = 0x2D; const MERGE: u8 = 0x2E; const UNSPECIFIED_NULL_OBEJECT: u8 = 0xFE; @@ -691,6 +693,18 @@ impl GraphBinaryV1Deser for GValue { .map(|val| GValue::Traverser(val)) .unwrap_or(GValue::Null)) } + BOOLEAN => Ok(match bool::from_be_bytes_nullable(bytes)? { + Some(value) => GValue::Bool(value), + None => GValue::Null, + }), + MERTRICS => Ok(match Metric::from_be_bytes_nullable(bytes)? { + Some(value) => GValue::Metric(value), + None => GValue::Null, + }), + TRAVERSAL_MERTRICS => Ok(match TraversalMetrics::from_be_bytes_nullable(bytes)? { + Some(value) => GValue::TraversalMetrics(value), + None => GValue::Null, + }), UNSPECIFIED_NULL_OBEJECT => { //Need to confirm the null-ness with the next byte being a 1 match bytes.next().cloned() { @@ -707,6 +721,75 @@ impl GraphBinaryV1Deser for GValue { } } +impl GraphBinaryV1Deser for TraversalMetrics { + fn from_be_bytes<'a, S: Iterator>(bytes: &mut S) -> GremlinResult { + //Format: {duration}{metrics} + + //{duration} is a Long describing the duration in nanoseconds + let duration: i64 = GraphBinaryV1Deser::from_be_bytes(bytes)?; + + //{metrics} is a List composed by Metrics items + let metrics: Vec = GraphBinaryV1Deser::from_be_bytes(bytes)?; + + let metrics: Result, GremlinError> = metrics + .into_iter() + .map(|value| Metric::from_gvalue(value)) + .collect(); + + //It doesn't appear documented but assuming the duration unit inherited from GraphSON is ms, so convert here + let duration_ms = duration as f64 / 1_000.0; + Ok(TraversalMetrics::new(duration_ms, metrics?)) + } +} + +impl GraphBinaryV1Deser for Metric { + fn from_be_bytes<'a, S: Iterator>(bytes: &mut S) -> GremlinResult { + //Format: {id}{name}{duration}{counts}{annotations}{nested_metrics} + + //{id} is a String representing the identifier + let id = String::from_be_bytes(bytes)?; + //{name} is a String representing the name + let name = String::from_be_bytes(bytes)?; + + //{duration} is a Long describing the duration in nanoseconds + let duration: i64 = GraphBinaryV1Deser::from_be_bytes(bytes)?; + + //{counts} is a Map composed by String keys and Long values + let mut counts: HashMap = GraphBinaryV1Deser::from_be_bytes(bytes)?; + + //{annotations} is a Map composed by String keys and a value of any type + let mut annotations: HashMap = GraphBinaryV1Deser::from_be_bytes(bytes)?; + + //{nested_metrics} is a List composed by Metrics items + let nested = GraphBinaryV1Deser::from_be_bytes(bytes)?; + + let traverser_count = + i64::from_gvalue(counts.remove(&GKey::from("traverserCount")).ok_or( + GremlinError::Cast(format!("Missing expected traverserCount property")), + )?)?; + + let element_count = i64::from_gvalue(counts.remove(&GKey::from("elementCount")).ok_or( + GremlinError::Cast(format!("Missing expected elementCount property")), + )?)?; + let percent_dur = f64::from_gvalue(annotations.remove(&GKey::from("percentDur")).ok_or( + GremlinError::Cast(format!("Missing expected percentDur property")), + )?)?; + + //It doesn't appear documented but assuming the duration unit inherited from GraphSON is ms, so convert here + let duration_ms = duration as f64 / 1_000.0; + + Ok(Metric::new( + id, + name, + duration_ms, + element_count, + traverser_count, + percent_dur, + nested, + )) + } +} + impl GraphBinaryV1Deser for T { fn from_be_bytes<'a, S: Iterator>(bytes: &mut S) -> GremlinResult { let literal = GValue::from_be_bytes(bytes)?; diff --git a/gremlin-client/src/io/mod.rs b/gremlin-client/src/io/mod.rs index 926a8afd..846c46d9 100644 --- a/gremlin-client/src/io/mod.rs +++ b/gremlin-client/src/io/mod.rs @@ -19,7 +19,7 @@ use uuid::Uuid; use crate::{io::graph_binary_v1::GraphBinaryV1Ser, GKey, GremlinError, GremlinResult, Message}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum IoProtocol { GraphSONV2, GraphSONV3, diff --git a/gremlin-client/tests/common.rs b/gremlin-client/tests/common.rs index 099a2c75..122a908a 100644 --- a/gremlin-client/tests/common.rs +++ b/gremlin-client/tests/common.rs @@ -1,4 +1,5 @@ use gremlin_client::{structure::T, Map}; +use rstest_reuse::template; pub fn assert_map_property(element_map: &Map, expected_key: &str, expected_value: &str) { let actual_prop_value: &String = element_map @@ -15,6 +16,13 @@ pub fn assert_map_property(element_map: &Map, expected_key: &str, expected_value assert_eq!(expected_value, actual_prop_value); } +#[template] +#[rstest] +#[case::graphson_v2(IoProtocol::GraphSONV2)] +#[case::graphson_v3(IoProtocol::GraphSONV3)] +#[case::graph_binary_v1(IoProtocol::GraphBinaryV1)] +fn serializers(#[case] protocol: IoProtocol) {} + #[allow(dead_code)] pub mod io { use gremlin_client::{ @@ -57,12 +65,6 @@ pub mod io { connect_serializer(serializer).expect("It should connect") } - pub fn graph() -> GremlinClient { - let client = expect_client(); - - client - } - pub fn graph_serializer(serializer: IoProtocol) -> GremlinClient { let client = expect_client_serializer(serializer); diff --git a/gremlin-client/tests/integration_client.rs b/gremlin-client/tests/integration_client.rs index 8232f4c0..db2577a0 100644 --- a/gremlin-client/tests/integration_client.rs +++ b/gremlin-client/tests/integration_client.rs @@ -6,32 +6,31 @@ use std::iter::FromIterator; use chrono::{offset::TimeZone, DateTime, Utc}; use gremlin_client::{ ConnectionOptions, GremlinClient, GremlinError, List, TlsOptions, ToGValue, - TraversalExplanation, TraversalMetrics, VertexProperty, + TraversalExplanation, TraversalMetrics, VertexProperty, GID, }; -use gremlin_client::{Edge, GValue, Map, Vertex}; +use gremlin_client::{Edge, GValue, IoProtocol, Map, Vertex}; -use common::io::{create_edge, create_vertex, expect_client, graph}; +use common::io::{create_edge, create_vertex, graph_serializer}; +use rstest::*; +use rstest_reuse::apply; +use serial_test::serial; -#[test] -fn test_client_connection_ok() { - expect_client(); -} - -#[test] -fn test_empty_query() { +#[apply(common::serializers)] +fn test_empty_query(protocol: IoProtocol) { + let client = graph_serializer(protocol); assert_eq!( 0, - graph() + client .execute("g.V().hasLabel('NotFound')", &[]) .expect("It should execute a traversal") .count() ) } -#[test] -fn test_session_empty_query() { - let mut graph = graph(); - let mut sessioned_graph = graph +#[apply(common::serializers)] +#[serial(test_session_empty_query)] +fn test_session_empty_query(protocol: IoProtocol) { + let mut sessioned_graph = graph_serializer(protocol) .create_session("test-session".to_string()) .expect("It should create a session."); assert_eq!( @@ -46,8 +45,8 @@ fn test_session_empty_query() { .expect("It should close the session."); } -#[test] -fn test_ok_credentials() { +#[apply(common::serializers)] +fn test_ok_credentials(#[case] protocol: IoProtocol) { let client = GremlinClient::connect( ConnectionOptions::builder() .host("localhost") @@ -57,6 +56,8 @@ fn test_ok_credentials() { .tls_options(TlsOptions { accept_invalid_certs: true, }) + .serializer(protocol.clone()) + .deserializer(protocol) .build(), ) .expect("Cannot connect"); @@ -65,8 +66,8 @@ fn test_ok_credentials() { assert!(result.is_ok(), "{:?}", result); } -#[test] -fn test_ko_credentials() { +#[apply(common::serializers)] +fn test_ko_credentials(#[case] protocol: IoProtocol) { let client = GremlinClient::connect( ConnectionOptions::builder() .host("localhost") @@ -76,6 +77,8 @@ fn test_ko_credentials() { .tls_options(TlsOptions { accept_invalid_certs: true, }) + .serializer(protocol.clone()) + .deserializer(protocol) .build(), ) .expect("Cannot connect"); @@ -84,9 +87,9 @@ fn test_ko_credentials() { assert!(result.is_err(), "{:?}", result); } -#[test] -fn test_wrong_query() { - let error = graph() +#[apply(common::serializers)] +fn test_wrong_query(protocol: IoProtocol) { + let error = graph_serializer(protocol) .execute("g.V", &[]) .expect_err("it should return an error"); @@ -99,9 +102,9 @@ fn test_wrong_query() { } } -#[test] -fn test_wrong_alias() { - let error = graph() +#[apply(common::serializers)] +fn test_wrong_alias(protocol: IoProtocol) { + let error = graph_serializer(protocol) .alias("foo") .execute("g.V()", &[]) .expect_err("it should return an error"); @@ -115,11 +118,9 @@ fn test_wrong_alias() { } } -#[test] - -fn test_vertex_query() { - let graph = graph(); - let vertices = graph +#[apply(common::serializers)] +fn test_vertex_query(protocol: IoProtocol) { + let vertices = graph_serializer(protocol) .execute( "g.V().hasLabel('person').has('name',name)", &[("name", &"marko")], @@ -132,10 +133,10 @@ fn test_vertex_query() { assert_eq!("person", vertices[0].label()); } -#[test] -fn test_edge_query() { - let graph = graph(); - let edges = graph + +#[apply(common::serializers)] +fn test_edge_query(protocol: IoProtocol) { + let edges = graph_serializer(protocol) .execute("g.E().hasLabel('knows').limit(1)", &[]) .expect("it should execute a query") .filter_map(Result::ok) @@ -146,14 +147,14 @@ fn test_edge_query() { assert_eq!("knows", edges[0].label()); } -#[test] -fn test_vertex_creation() { - let graph = graph(); - let mark = create_vertex(&graph, "mark"); +#[apply(common::serializers)] +fn test_vertex_creation(protocol: IoProtocol) { + let client = graph_serializer(protocol); + let mark = create_vertex(&client, "mark"); assert_eq!("person", mark.label()); - let value_map = graph + let value_map = client .execute("g.V(identity).valueMap()", &[("identity", mark.id())]) .expect("should fetch valueMap with properties") .filter_map(Result::ok) @@ -169,10 +170,9 @@ fn test_vertex_creation() { ); } -#[test] -fn test_complex_vertex_creation_with_option_none_properties() { - let graph = graph(); - let properties = graph +#[apply(common::serializers)] +fn test_complex_vertex_creation_with_option_none_properties(protocol: IoProtocol) { + let properties = graph_serializer(protocol) .execute(r#"g.addV('person').valueMap()"#, &[]) .expect("it should execute addV") .filter_map(Result::ok) @@ -216,9 +216,12 @@ fn test_complex_vertex_creation_with_option_none_properties() { .is_none()); } -#[test] -fn test_complex_vertex_creation_with_option_some_properties() { - let graph = graph(); +#[apply(common::serializers)] +fn test_complex_vertex_creation_with_option_some_properties(protocol: IoProtocol) { + //GraphSON V2 doesn't have maps, so skip it + if protocol == IoProtocol::GraphSONV2 { + return; + } let q = r#" g.addV('person') .property('name',name) @@ -242,7 +245,7 @@ fn test_complex_vertex_creation_with_option_some_properties() { ("uuid", &uuid), ("date", &now), ]; - let properties = graph + let properties = graph_serializer(protocol) .execute(q, params) .expect("it should execute addV") .filter_map(Result::ok) @@ -313,10 +316,8 @@ fn test_complex_vertex_creation_with_option_some_properties() { ); } -#[test] -fn test_complex_vertex_creation_with_properties() { - let graph = graph(); - +#[apply(common::serializers)] +fn test_complex_vertex_creation_with_properties(protocol: IoProtocol) { let q = r#" g.addV('person') .property('id',UUID.randomUUID()) @@ -339,7 +340,7 @@ fn test_complex_vertex_creation_with_properties() { ("dateTime", &chrono::Utc.timestamp(1551825863, 0)), ("date", &(1551825863 as i64)), ]; - let results = graph + let results = graph_serializer(protocol) .execute(q, params) .expect("it should execute addV") .filter_map(Result::ok) @@ -421,20 +422,18 @@ fn test_complex_vertex_creation_with_properties() { ); } -#[test] -fn test_inserting_date_with_milisecond_precision() { +#[apply(common::serializers)] +fn test_inserting_date_with_milisecond_precision(protocol: IoProtocol) { use chrono::offset::TimeZone; use chrono::DateTime; use chrono::Utc; - let graph = graph(); - let q = r#"g.addV('person').property('dateTime',dateTime).propertyMap()"#; let expected = chrono::Utc.timestamp(1551825863, 0); let params: &[(&str, &dyn ToGValue)] = &[("dateTime", &expected)]; - let results = graph + let results = graph_serializer(protocol) .execute(q, params) .expect("it should execute addV") .filter_map(Result::ok) @@ -456,10 +455,13 @@ fn test_inserting_date_with_milisecond_precision() { ); } -#[test] -fn test_list_cardinality() { - let graph = graph(); - +#[apply(common::serializers)] +fn test_list_cardinality(protocol: IoProtocol) { + //GraphSON V2 doesn't have lists, so skip it + if protocol == IoProtocol::GraphSONV2 { + return; + } + let client = graph_serializer(protocol); //split into 2 queries due to the bindings limit let q1 = r#" @@ -545,7 +547,7 @@ fn test_list_cardinality() { ("bool_4", &true), ]; - let results1 = graph + let results1 = client .execute(q1, params1) .expect("it should execute addV") .filter_map(Result::ok) @@ -566,7 +568,7 @@ fn test_list_cardinality() { let f32_list = properties1["float1"].clone().take::>().unwrap(); assert_eq!(f32_list, vec![1.1, 1.1, 2.3, 3.4]); - let results2 = graph + let results2 = client .execute(q2, params2) .expect("it should execute addV") .filter_map(Result::ok) @@ -595,10 +597,13 @@ fn test_list_cardinality() { assert_eq!(boolean_list, vec![false, true, false, true]); } -#[test] -fn test_set_cardinality() { - let graph = graph(); - +#[apply(common::serializers)] +fn test_set_cardinality(protocol: IoProtocol) { + //GraphSON V2 doesn't have sets, so skip it + if protocol == IoProtocol::GraphSONV2 { + return; + } + let client = graph_serializer(protocol); //split into 2 queries due to the bindings limit let q1 = r#" @@ -667,7 +672,7 @@ fn test_set_cardinality() { ("bool_4", &true), ]; - let results1 = graph + let results1 = client .execute(q1, params1) .expect("it should execute addV") .filter_map(Result::ok) @@ -705,7 +710,7 @@ fn test_set_cardinality() { .unwrap(); assert_eq!(i64_set, HashSet::from_iter(vec![4, 5, 6].iter().cloned())); - let results2 = graph + let results2 = client .execute(q2, params2) .expect("it should execute addV") .filter_map(Result::ok) @@ -743,20 +748,20 @@ fn test_set_cardinality() { ); } -#[test] -fn test_edge_creation() { - let graph = graph(); - let mark = create_vertex(&graph, "mark"); - let frank = create_vertex(&graph, "frank"); +#[apply(common::serializers)] +fn test_edge_creation(protocol: IoProtocol) { + let client = graph_serializer(protocol); + let mark = create_vertex(&client, "mark"); + let frank = create_vertex(&client, "frank"); - let edge = create_edge(&graph, &mark, &frank, "knows"); + let edge = create_edge(&client, &mark, &frank, "knows"); assert_eq!("knows", edge.label()); assert_eq!(&mark, edge.out_v()); assert_eq!(&frank, edge.in_v()); - let edges = graph + let edges = client .execute("g.V(identity).outE()", &[("identity", mark.id())]) .expect("should fetch edge") .filter_map(Result::ok) @@ -774,11 +779,9 @@ fn test_edge_creation() { assert_eq!(&frank, edge.in_v()); } -#[test] -fn test_profile() { - let graph = graph(); - - let metrics = graph +#[apply(common::serializers)] +fn test_profile(protocol: IoProtocol) { + let metrics = graph_serializer(protocol) .execute("g.V().limit(1).profile()", &[]) .expect("should return a profile") .filter_map(Result::ok) @@ -806,11 +809,9 @@ fn test_profile() { ); } -#[test] -fn test_explain() { - let graph = graph(); - - let metrics = graph +#[apply(common::serializers)] +fn test_explain(protocol: IoProtocol) { + let metrics = graph_serializer(protocol) .execute("g.V().limit(1).explain()", &[]) .expect("should return a profile") .filter_map(Result::ok) @@ -840,16 +841,15 @@ fn test_explain() { ); } -#[test] - -fn test_group_count_vertex() { - let graph = graph(); - let mark = create_vertex(&graph, "mark"); - let frank = create_vertex(&graph, "frank"); +#[apply(common::serializers)] +fn test_group_count_vertex(protocol: IoProtocol) { + let client = graph_serializer(protocol.clone()); + let mark = create_vertex(&client, "mark"); + let frank = create_vertex(&client, "frank"); - create_edge(&graph, &mark, &frank, "knows"); + create_edge(&client, &mark, &frank, "knows"); - let map = graph + let map = client .execute( "g.V(identity).out().groupCount()", &[("identity", mark.id())], @@ -866,21 +866,31 @@ fn test_group_count_vertex() { assert_eq!(1, first.len()); - let count = first.get(&frank); + let count = if protocol == IoProtocol::GraphSONV2 { + //GraphSONV2 just sends a simplified map, + //so we need to look up by the edge's id as a simplified type + //instead of as a GID + first.get(match frank.id() { + GID::String(s) => s.to_string(), + GID::Int32(i) => i.to_string(), + GID::Int64(i) => i.to_string(), + }) + } else { + first.get(&frank) + }; assert_eq!(Some(&GValue::Int64(1)), count); } -#[test] - -fn test_group_count_edge() { - let graph = graph(); - let mark = create_vertex(&graph, "mark"); - let frank = create_vertex(&graph, "frank"); +#[apply(common::serializers)] +fn test_group_count_edge(protocol: IoProtocol) { + let client = graph_serializer(protocol.clone()); + let mark = create_vertex(&client, "mark"); + let frank = create_vertex(&client, "frank"); - let edge = create_edge(&graph, &mark, &frank, "knows"); + let edge = create_edge(&client, &mark, &frank, "knows"); - let map = graph + let map = client .execute( "g.V(identity).outE().groupCount()", &[("identity", mark.id())], @@ -897,15 +907,25 @@ fn test_group_count_edge() { assert_eq!(1, first.len()); - let count = first.get(&edge); + let count = if protocol == IoProtocol::GraphSONV2 { + //GraphSONV2 just sends a simplified map, + //so we need to look up by the edge's id as a simplified type + //instead of as a GID + first.get(match edge.id() { + GID::String(s) => s.to_string(), + GID::Int32(i) => i.to_string(), + GID::Int64(i) => i.to_string(), + }) + } else { + first.get(&edge) + }; assert_eq!(Some(&GValue::Int64(1)), count); } -#[test] +#[apply(common::serializers)] #[cfg(feature = "derive")] -fn test_vertex_mapping() { - let graph = graph(); +fn test_vertex_mapping(protocol: IoProtocol) { use gremlin_client::derive::FromGValue; use std::convert::TryFrom; @@ -930,7 +950,7 @@ fn test_vertex_mapping() { ("dateTime", &chrono::Utc.timestamp(1551825863, 0)), ("date", &(1551825863 as i64)), ]; - let mark = graph + let mark = client .execute(q, params) .expect("should create a vertex") .filter_map(Result::ok) @@ -948,7 +968,7 @@ fn test_vertex_mapping() { assert_eq!("person", mark[0].label()); - let value_map = graph + let value_map = client .execute("g.V(identity).valueMap()", &[("identity", mark[0].id())]) .expect("should fetch valueMap with properties") .filter_map(Result::ok) diff --git a/gremlin-client/tests/integration_client_v2.rs b/gremlin-client/tests/integration_client_v2.rs deleted file mode 100644 index bdc3f966..00000000 --- a/gremlin-client/tests/integration_client_v2.rs +++ /dev/null @@ -1,466 +0,0 @@ -mod common; - -use gremlin_client::{ - ConnectionOptions, GremlinClient, GremlinError, IoProtocol, List, TlsOptions, ToGValue, - TraversalExplanation, TraversalMetrics, VertexProperty, -}; -use gremlin_client::{Edge, GKey, GValue, Map, Vertex, GID}; - -use common::io::{create_edge, create_vertex, expect_client_serializer, graph_serializer}; - -#[test] -fn test_client_connection_ok_v2() { - expect_client_serializer(IoProtocol::GraphSONV2); -} - -#[test] -fn test_empty_query_v2() { - assert_eq!( - 0, - graph_serializer(IoProtocol::GraphSONV2) - .execute("g.V().hasLabel('NotFound')", &[]) - .expect("It should execute a traversal") - .count() - ) -} - -#[test] -fn test_ok_credentials_v2() { - let client = GremlinClient::connect( - ConnectionOptions::builder() - .host("localhost") - .port(8183) - .credentials("stephen", "password") - .ssl(true) - .tls_options(TlsOptions { - accept_invalid_certs: true, - }) - .serializer(IoProtocol::GraphSONV2) - .deserializer(IoProtocol::GraphSONV2) - .build(), - ) - .expect("Cannot connect"); - - let result = client.execute("g.V().limit(1)", &[]); - assert!(result.is_ok(), "{:?}", result); -} - -#[test] -fn test_ko_credentials_v2() { - let client = GremlinClient::connect( - ConnectionOptions::builder() - .host("localhost") - .port(8183) - .credentials("stephen", "pwd") - .ssl(true) - .tls_options(TlsOptions { - accept_invalid_certs: true, - }) - .serializer(IoProtocol::GraphSONV2) - .build(), - ) - .expect("Cannot connect"); - - let result = client.execute("g.V().limit(1)", &[]); - assert!(result.is_err(), "{:?}", result); -} - -#[test] -fn test_wrong_query_v2() { - let error = graph_serializer(IoProtocol::GraphSONV2) - .execute("g.V", &[]) - .expect_err("it should return an error"); - - match error { - GremlinError::Request((code, message)) => { - assert_eq!(597, code); - assert_eq!("No such property: V for class: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource",message) - } - _ => panic!("wrong error type"), - } -} - -#[test] -fn test_wrong_alias_v2() { - let error = graph_serializer(IoProtocol::GraphSONV2) - .alias("foo") - .execute("g.V()", &[]) - .expect_err("it should return an error"); - - match error { - GremlinError::Request((code, message)) => { - assert_eq!(499, code); - assert_eq!("Could not alias [g] to [foo] as [foo] not in the Graph or TraversalSource global bindings",message) - } - _ => panic!("wrong error type"), - } -} - -#[test] - -fn test_vertex_query_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - let vertices = graph - .execute( - "g.V().hasLabel('person').has('name',name)", - &[("name", &"marko")], - ) - .expect("it should execute a query") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!("person", vertices[0].label()); -} -#[test] -fn test_edge_query_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - let edges = graph - .execute("g.E().hasLabel('knows').limit(1)", &[]) - .expect("it should execute a query") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!("knows", edges[0].label()); -} - -#[test] -fn test_vertex_creation_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - let mark = create_vertex(&graph, "mark"); - - assert_eq!("person", mark.label()); - - let value_map = graph - .execute("g.V(identity).valueMap()", &[("identity", mark.id())]) - .expect("should fetch valueMap with properties") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!(1, value_map.len()); - - assert_eq!( - Some(&GValue::List(vec![String::from("mark").into()].into())), - value_map[0].get("name") - ); -} - -#[test] -fn test_inserting_date_with_milisecond_precision() { - use chrono::offset::TimeZone; - use chrono::DateTime; - use chrono::Utc; - - let graph = graph_serializer(IoProtocol::GraphSONV2); - - let q = r#"g.addV('person').property('dateTime',dateTime).propertyMap()"#; - - let expected = chrono::Utc.timestamp(1551825863, 0); - let params: &[(&str, &dyn ToGValue)] = &[("dateTime", &expected)]; - - let results = graph - .execute(q, params) - .expect("it should execute addV") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - let properties = &results[0]; - assert_eq!(1, properties.len()); - - assert_eq!( - &expected, - properties["dateTime"].get::().unwrap()[0] - .get::() - .unwrap() - .get::>() - .unwrap() - ); -} - -#[test] -fn test_complex_vertex_creation_with_properties_v2() { - use chrono::offset::TimeZone; - - let graph = graph_serializer(IoProtocol::GraphSONV2); - - let q = r#" - g.addV('person') - .property('id',UUID.randomUUID()) - .property('name',name) - .property('age',age) - .property('time',time) - .property('score',score) - .property('uuid',uuid) - .property('date',new Date(date)) - .property('dateTime',dateTime) - .propertyMap()"#; - - let uuid = uuid::Uuid::new_v4(); - let params: &[(&str, &dyn ToGValue)] = &[ - ("age", &22), - ("time", &(22 as i64)), - ("name", &"mark"), - ("score", &3.2), - ("uuid", &uuid), - ("dateTime", &chrono::Utc.timestamp(1551825863, 0)), - ("date", &(1551825863 as i64)), - ]; - let results = graph - .execute(q, params) - .expect("it should execute addV") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - let properties = &results[0]; - - assert_eq!(8, properties.len()); - - assert_eq!( - &22, - properties["age"].get::().unwrap()[0] - .get::() - .unwrap() - .get::() - .unwrap() - ); - - assert_eq!( - &22, - properties["time"].get::().unwrap()[0] - .get::() - .unwrap() - .get::() - .unwrap() - ); - - assert_eq!( - &chrono::Utc.timestamp_millis(1551825863), - properties["date"].get::().unwrap()[0] - .get::() - .unwrap() - .get::>() - .unwrap() - ); - - assert!(properties["id"].get::().unwrap()[0] - .get::() - .unwrap() - .get::() - .is_ok()); - - assert_eq!( - &uuid, - properties["uuid"].get::().unwrap()[0] - .get::() - .unwrap() - .get::() - .unwrap() - ); - - assert_eq!( - &String::from("mark"), - properties["name"].get::().unwrap()[0] - .get::() - .unwrap() - .get::() - .unwrap() - ); - - assert_eq!( - &3.2, - properties["score"].get::().unwrap()[0] - .get::() - .unwrap() - .get::() - .unwrap() - ); - - assert_eq!( - &chrono::Utc.timestamp(1551825863, 0), - properties["dateTime"].get::().unwrap()[0] - .get::() - .unwrap() - .get::>() - .unwrap() - ); -} - -#[test] -fn test_edge_creation_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - let mark = create_vertex(&graph, "mark"); - let frank = create_vertex(&graph, "frank"); - - let edge = create_edge(&graph, &mark, &frank, "knows"); - - assert_eq!("knows", edge.label()); - - assert_eq!(&mark, edge.out_v()); - assert_eq!(&frank, edge.in_v()); - - let edges = graph - .execute("g.V(identity).outE()", &[("identity", mark.id())]) - .expect("should fetch edge") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!(1, edges.len()); - - let edge = &edges[0]; - - assert_eq!("knows", edge.label()); - - assert_eq!(&mark, edge.out_v()); - assert_eq!(&frank, edge.in_v()); -} - -#[test] -fn test_profile_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - - let metrics = graph - .execute("g.V().limit(1).profile()", &[]) - .expect("should return a profile") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!(1, metrics.len()); - - let t = &metrics[0]; - - assert_eq!(true, t.duration() > &0.0); - - let steps = t.metrics(); - - assert_ne!(0, steps.len()); - - assert_eq!( - 100.0, - steps - .iter() - .map(|s| s.perc_duration()) - .fold(0.0, |acc, x| acc + x) - .round() - ); -} - -#[test] -fn test_explain_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - - let metrics = graph - .execute("g.V().limit(1).explain()", &[]) - .expect("should return a profile") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!(1, metrics.len()); - - let t = &metrics[0]; - - assert_eq!( - &vec![ - String::from("GraphStep(vertex,[])"), - String::from("RangeGlobalStep(0,1)") - ], - t.original() - ); - - assert_eq!( - &vec![ - String::from("TinkerGraphStep(vertex,[])"), - String::from("RangeGlobalStep(0,1)"), - String::from("ReferenceElementStep") - ], - t.final_t() - ); -} - -#[test] - -fn test_group_count_vertex_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - let mark = create_vertex(&graph, "mark"); - let frank = create_vertex(&graph, "frank"); - - println!("FRANK: {:#?}", frank); - - create_edge(&graph, &mark, &frank, "knows"); - - let map = graph - .execute( - "g.V(identity).out().groupCount()", - &[("identity", mark.id())], - ) - .expect("should fetch a groupCount") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - println!("MAP IS: {:#?}", map); - - assert_eq!(1, map.len()); - - let first = &map[0]; - - assert_eq!(1, first.len()); - - let count = first.get(GKey::String(match frank.id() { - GID::String(s) => s.to_string(), - GID::Int32(i) => i.to_string(), - GID::Int64(i) => i.to_string(), - })); - - assert_eq!(Some(&GValue::Int64(1)), count); -} - -#[test] - -fn test_group_count_edge_v2() { - let graph = graph_serializer(IoProtocol::GraphSONV2); - let mark = create_vertex(&graph, "mark"); - let frank = create_vertex(&graph, "frank"); - - let edge = create_edge(&graph, &mark, &frank, "knows"); - - let map = graph - .execute( - "g.V(identity).outE().groupCount()", - &[("identity", mark.id())], - ) - .expect("should fetch a groupCount") - .filter_map(Result::ok) - .map(|f| f.take::()) - .collect::, _>>() - .expect("It should be ok"); - - assert_eq!(1, map.len()); - - let first = &map[0]; - - assert_eq!(1, first.len()); - - let count = first.get(GKey::String(match edge.id() { - GID::String(s) => s.to_string(), - GID::Int32(i) => i.to_string(), - GID::Int64(i) => i.to_string(), - })); - - assert_eq!(Some(&GValue::Int64(1)), count); -}