#[cfg(test)]
mod graph_tests {
use crate::query_planner::{
graph::{
edge::{Edge, EdgeReference},
node::Node,
Graph,
},
state::supergraph_state::SupergraphState,
utils::parsing::parse_schema,
};
use petgraph::{
graph::NodeIndex,
visit::{EdgeRef, NodeRef},
};
use std::path::PathBuf;
fn init_test(supergraph_sdl: &str) -> Graph {
let schema = parse_schema(supergraph_sdl);
let metadata = SupergraphState::new(&schema);
Graph::graph_from_supergraph_state(&metadata).expect("failed to create graph")
}
#[derive(Debug)]
struct FoundEdges<'a> {
pub edges: Vec<(EdgeReference<'a>, NodeIndex)>,
pub graph: &'a Graph,
}
impl FoundEdges<'_> {
pub fn assert_key_edge(&self, key: &str, other_side: &str) -> &Self {
let edge = self.edge(key, other_side);
assert!(
edge.is_some(),
"🔑 Key edge {} <-> {} not found",
key,
other_side
);
self
}
pub fn no_field_edge(&self, key: &str) -> &Self {
let edge = self
.edges
.iter()
.find(|(edge_ref, _)| edge_ref.weight().display_name() == key);
assert!(edge.is_none(), "Field edge {} found", key);
self
}
pub fn assert_field_edge_does_not_exist(&self, key: &str, other_side: &str) -> &Self {
let edge = self.edge(key, other_side);
assert!(
edge.is_none(),
"Field edge {} <-> {} found",
key,
other_side
);
self
}
pub fn edge_field<'a>(&'a self, key: &str) -> Option<&'a (EdgeReference<'a>, NodeIndex)> {
let mut r = self
.edges
.iter()
.filter(|(edge_ref, _to)| edge_ref.weight().display_name() == key);
assert_eq!(
r.clone().count(),
1,
"expected to find exactly one edge field named '{}', found {}, available fields: {:?}",
key,
r.clone().count(),
self.edges.iter().map(|(e, _)| e.weight().display_name()).collect::<Vec<_>>()
);
r.nth(0)
}
pub fn edges_field<'a>(&'a self, key: &str) -> Vec<&'a (EdgeReference<'a>, NodeIndex)> {
self.edges
.iter()
.filter(|(edge_ref, _to)| match edge_ref.weight() {
Edge::FieldMove(fm) => fm.name == key,
_ => false,
})
.collect()
}
pub fn edge<'a>(
&'a self,
key: &str,
other_side: &str,
) -> Option<&'a (EdgeReference<'a>, NodeIndex)> {
self.edges.iter().find(|(edge_ref, node_id)| {
let edge = edge_ref.weight();
let node = self.graph.node(*node_id).unwrap();
let formatted_node = format!("{}", node);
if node.is_using_provides() {
return edge.display_name() == key
&& formatted_node.contains(&format!("{}/", other_side));
}
edge.display_name() == key && formatted_node == other_side
})
}
pub fn assert_field_edge(&self, key: &str, other_side: &str) -> &Self {
let edge = self.edge(key, other_side);
assert!(
edge.is_some(),
"Field edge {} <-> {} not found",
key,
other_side
);
self
}
pub fn assert_interface_edge(&self, key: &str, other_side: &str) -> &Self {
let edge = self.edge(key, other_side);
assert!(
edge.is_some(),
"Interface edge {} <-> {} not found",
key,
other_side
);
self
}
}
fn find_node_doesnt_exists(graph: &Graph, node_id: &str) {
let node_res = graph.node_display_name_to_index.get(node_id);
assert!(
node_res.is_none(),
"found node {} that should not exists",
node_id
);
}
fn find_node<'a>(graph: &'a Graph, node_id: &str) -> (FoundEdges<'a>, FoundEdges<'a>) {
let node_res = graph.node_display_name_to_index.get(node_id);
assert!(node_res.is_some(), "failed to find node {}", node_id);
let node_index = node_res.unwrap();
let incoming_edges = FoundEdges {
edges: graph
.edges_to(*node_index)
.map(|edge_ref| (edge_ref, edge_ref.source().id()))
.collect(),
graph,
};
let outgoing_edges = FoundEdges {
edges: graph
.edges_from(*node_index)
.map(|edge_ref| (edge_ref, edge_ref.target().id()))
.collect(),
graph,
};
(incoming_edges, outgoing_edges)
}
#[test]
fn nested_provides() -> Result<(), Box<dyn std::error::Error>> {
let supergraph_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("fixture/tests/nested-provides.supergraph.graphql");
let graph = init_test(
&std::fs::read_to_string(supergraph_path).expect("Unable to read input file"),
);
let (_, outgoing) = find_node(&graph, "Query/category");
let field_edges = outgoing.edges_field("products");
assert_eq!(field_edges.len(), 2);
let (_, to) = field_edges
.iter()
.find(|(edge_ref, _to)| format!("{:?}", edge_ref.weight()) == "products @provides")
.unwrap();
let node = graph.node(*to)?;
assert!(node.is_using_provides());
assert_eq!(node.display_name(), "Product/category/1");
let (_, viewed_outgoing) = find_node(&graph, &node.display_name());
let (_, to) = viewed_outgoing
.edge_field("categories")
.expect("failed to find edge for field categories");
let node1 = graph.node(*to)?;
assert!(node1.is_using_provides());
assert_eq!(node1.display_name(), "Category/category/1");
let (_, to_index) = field_edges
.iter()
.find(|(edge_ref, _to)| format!("{:?}", edge_ref.weight()) == "products")
.unwrap();
let node = graph.node(*to_index)?;
assert_eq!(node.display_name(), "Product/category");
assert!(!node.is_using_provides());
find_node(&graph, "Product/category")
.1
.assert_field_edge("id", "ID/category");
Ok(())
}
#[test]
fn star_stuff() -> Result<(), Box<dyn std::error::Error>> {
let supergraph_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixture/supergraph.graphql");
let graph = init_test(
&std::fs::read_to_string(supergraph_path).expect("Unable to read input file"),
);
find_node(&graph, "Product/reviews")
.1
.assert_field_edge("reviews", "Review/reviews");
find_node(&graph, "Product/products")
.1
.no_field_edge("reviews");
let (_, outgoing) = find_node(&graph, "Product/products");
outgoing
.assert_field_edge("dimensions", "ProductDimension/products")
.assert_field_edge_does_not_exist("dimensions", "ProductDimension/reviews")
.assert_field_edge_does_not_exist("dimensions", "ProductDimension/users")
.assert_field_edge_does_not_exist("dimensions", "ProductDimension/inventory");
find_node(&graph, "User/products")
.1
.assert_field_edge("totalProductsCreated", "Int/products");
find_node(&graph, "User/users")
.1
.assert_field_edge("totalProductsCreated", "Int/users");
find_node_doesnt_exists(&graph, "User/reviews");
find_node_doesnt_exists(&graph, "User/inventory");
find_node_doesnt_exists(&graph, "User/PANDAS");
find_node(&graph, "Product/products")
.1
.no_field_edge("reviewsScore");
find_node(&graph, "Product/reviews")
.1
.assert_field_edge("reviewsScore", "Float/reviews");
let (incoming, outgoing) = find_node(&graph, "ProductItf/products");
incoming
.assert_field_edge("product", "Query/products")
.assert_field_edge("allProducts", "Query/products");
outgoing
.assert_field_edge("id", "ID/products")
.assert_field_edge("variation", "ProductVariation/products")
.assert_field_edge("dimensions", "ProductDimension/products")
.assert_field_edge("hidden", "String/products")
.assert_field_edge("name", "String/products")
.assert_field_edge("oldField", "String/products")
.assert_field_edge("package", "String/products")
.assert_field_edge("sku", "String/products")
.assert_field_edge("createdBy", "User/products")
.assert_field_edge("__typename", "String/products")
.no_field_edge("reviews");
assert_eq!(incoming.edges.len(), 3);
assert_eq!(outgoing.edges.len(), 12);
let outgoing = find_node(&graph, "Product/inventory").1;
outgoing
.assert_field_edge("delivery", "DeliveryEstimates/inventory")
.edge("delivery", "DeliveryEstimates/inventory")
.expect("cant find edge");
Ok(())
}
#[test]
fn star_stuff_snapshot() {
let supergraph_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixture/supergraph.graphql");
let graph = init_test(
&std::fs::read_to_string(supergraph_path).expect("Unable to read input file"),
);
assert_eq!(graph.root_query_node(), &Node::QueryRoot("Query".into()));
assert_eq!(graph.root_mutation_node(), None);
assert_eq!(graph.root_subscription_node(), None);
let (incoming, outgoing) = find_node(&graph, "Product/products");
assert_eq!(incoming.edges.len(), 14);
assert_eq!(outgoing.edges.len(), 18);
incoming
.assert_key_edge("id", "Product/inventory")
.assert_key_edge("sku package", "Product/inventory")
.assert_key_edge("sku variation { id }", "Product/inventory")
.assert_key_edge("id", "Product/products")
.assert_key_edge("sku package", "Product/products")
.assert_key_edge("sku variation { id }", "Product/products")
.assert_key_edge("id", "Product/reviews")
.assert_key_edge("sku package", "Product/reviews")
.assert_key_edge("sku variation { id }", "Product/reviews")
.assert_interface_edge("Product", "ProductItf/products")
.assert_interface_edge("Product", "SkuItf/products");
outgoing
.assert_key_edge("id", "Product/inventory")
.assert_key_edge("id", "Product/products")
.assert_key_edge("id", "Product/reviews")
.assert_key_edge("sku package", "Product/products")
.assert_key_edge("sku variation { id }", "Product/products")
.assert_field_edge("id", "ID/products")
.assert_field_edge("variation", "ProductVariation/products")
.assert_field_edge("dimensions", "ProductDimension/products")
.assert_field_edge("hidden", "String/products")
.assert_field_edge("name", "String/products")
.assert_field_edge("oldField", "String/products")
.assert_field_edge("package", "String/products")
.assert_field_edge("sku", "String/products")
.assert_field_edge("createdBy", "User/products")
.assert_field_edge("__typename", "String/products");
}
#[test]
fn multiple_provides() -> Result<(), Box<dyn std::error::Error>> {
let supergraph_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixture/supergraph2.graphql");
let graph = init_test(
&std::fs::read_to_string(supergraph_path).expect("Unable to read input file"),
);
let (_, outgoing) = find_node(&graph, "Group/foo");
let (_, to) = outgoing
.edges_field("users")
.iter()
.find(|(edge_ref, _to)| format!("{:?}", edge_ref.weight()) == "users @provides")
.expect("failed to find edge for field users");
let node1 = graph.node(*to)?;
assert!(node1.is_using_provides());
let (viewed_incoming, viewed_outgoing) = find_node(&graph, &node1.display_name());
viewed_outgoing.assert_field_edge("id", "String/foo");
assert_eq!(viewed_incoming.edges.len(), 2); assert_eq!(viewed_outgoing.edges.len(), 3);
let (_, to) = outgoing
.edges_field("user")
.iter()
.find(|(edge_ref, _to)| format!("{:?}", edge_ref.weight()) == "user @provides")
.expect("failed to find edge for field user");
let node2 = graph.node(*to)?;
assert!(node2.is_using_provides());
let (viewed_incoming, viewed_outgoing) = find_node(&graph, &node2.display_name());
viewed_outgoing.assert_field_edge("name", "String/foo");
assert_eq!(viewed_incoming.edges.len(), 2); assert_eq!(viewed_outgoing.edges.len(), 4);
let (nested_provides_id, nested_provides_node) = viewed_outgoing
.edge_field("profile")
.map(|(_, node_index)| (*node_index, graph.node(*node_index).unwrap()))
.expect("failed to located viewed node from profile field");
assert!(nested_provides_node.is_using_provides());
assert!(nested_provides_node
.display_name()
.starts_with("Profile/foo/"));
let mut nested_edges = graph.edges_from(nested_provides_id);
assert_eq!(nested_edges.clone().count(), 2); assert_eq!(
nested_edges.next().unwrap().weight().display_name(),
String::from("age")
);
assert_ne!(node1, node2);
Ok(())
}
}