Skip to content

Commit

Permalink
Consolidated integration_client tests
Browse files Browse the repository at this point in the history
  • Loading branch information
criminosis committed Nov 13, 2024
1 parent 1bd393d commit 0bd9f9a
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 588 deletions.
87 changes: 83 additions & 4 deletions gremlin-client/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<chrono::Utc>, GValue::Date);
impl_from_gvalue!(Traverser, GValue::Traverser);

Expand All @@ -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<Self> {
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 = <List as FromGValue>::from_gvalue(original)?;
let intermediate = <List as FromGValue>::from_gvalue(intermediate)?;
let final_ = <List as FromGValue>::from_gvalue(final_)?;

let original: GremlinResult<Vec<String>> = original
.into_iter()
.map(|val| <String as FromGValue>::from_gvalue(val))
.collect();
let final_: GremlinResult<Vec<String>> = final_
.into_iter()
.map(|val| <String as FromGValue>::from_gvalue(val))
.collect();
let intermediate: GremlinResult<Vec<IntermediateRepr>> = 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<Self> {
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<Vec<String>> =
<List as FromGValue>::from_gvalue(traversal)?
.into_iter()
.map(|val| <String as FromGValue>::from_gvalue(val))
.collect();
Ok(IntermediateRepr::new(
traversal?,
<String as FromGValue>::from_gvalue(strategy)?,
<String as FromGValue>::from_gvalue(category)?,
))
}
_ => Err(GremlinError::Cast(format!(
"Cannot convert {:?} to IntermediateRepr",
v
))),
}
}
Expand Down
87 changes: 85 additions & 2 deletions gremlin-client/src/io/graph_binary_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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() {
Expand All @@ -707,6 +721,75 @@ impl GraphBinaryV1Deser for GValue {
}
}

impl GraphBinaryV1Deser for TraversalMetrics {
fn from_be_bytes<'a, S: Iterator<Item = &'a u8>>(bytes: &mut S) -> GremlinResult<Self> {
//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<GValue> = GraphBinaryV1Deser::from_be_bytes(bytes)?;

let metrics: Result<Vec<Metric>, 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<Item = &'a u8>>(bytes: &mut S) -> GremlinResult<Self> {
//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<GKey, GValue> = GraphBinaryV1Deser::from_be_bytes(bytes)?;

//{annotations} is a Map composed by String keys and a value of any type
let mut annotations: HashMap<GKey, GValue> = 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<Item = &'a u8>>(bytes: &mut S) -> GremlinResult<Self> {
let literal = GValue::from_be_bytes(bytes)?;
Expand Down
2 changes: 1 addition & 1 deletion gremlin-client/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 8 additions & 6 deletions gremlin-client/tests/common.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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::{
Expand Down Expand Up @@ -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);

Expand Down
Loading

0 comments on commit 0bd9f9a

Please sign in to comment.