use super::fixtures::{in_port, node_at, out_port};
use crate::runtime::lookups::NodeGraphLookups;
use crate::runtime::utils::{
get_connected_edges, get_connected_edges_for_nodes, get_incomers, get_outgoers,
};
use jellyflow_core::core::{
CanvasPoint, Edge, EdgeId, EdgeKind, Graph, GraphBuilder, GraphId, NodeId, Port, PortCapacity,
PortDirection, PortId, PortKey, PortKind,
};
fn data_edge(from: PortId, to: PortId) -> Edge {
Edge::new(EdgeKind::Data, from, to)
}
#[test]
fn outgoers_incomers_connected_edges_are_derived_from_connections() {
let mut g = GraphBuilder::new(GraphId::from_u128(1));
let a = NodeId::new();
let b = NodeId::new();
let c = NodeId::new();
g.insert_node(a, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None));
g.insert_node(b, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None));
g.insert_node(c, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None));
let (a_out_id, a_out) = out_port(a);
let (b_in_id, b_in) = in_port(b, "in0");
let (c_in_id, c_in) = in_port(c, "in0");
g.insert_port(a_out_id, a_out);
g.insert_port(b_in_id, b_in);
g.insert_port(c_in_id, c_in);
let e1 = EdgeId::new();
let e2 = EdgeId::new();
g.insert_edge(e1, data_edge(a_out_id, b_in_id));
g.insert_edge(e2, data_edge(a_out_id, c_in_id));
let mut lookups = NodeGraphLookups::default();
lookups.rebuild_from(&g);
let mut expected_outgoers = vec![b, c];
expected_outgoers.sort();
assert_eq!(get_outgoers(&lookups, a), expected_outgoers);
assert_eq!(get_incomers(&lookups, b), vec![a]);
assert_eq!(get_incomers(&lookups, c), vec![a]);
let connected = get_connected_edges(&lookups, a);
assert_eq!(connected.len(), 2);
assert!(connected.contains(&e1));
assert!(connected.contains(&e2));
let connected_for_nodes = get_connected_edges_for_nodes(&lookups, [b, c]);
let mut expected = vec![e1, e2];
expected.sort();
assert_eq!(connected_for_nodes, expected);
}
#[test]
fn helpers_are_deterministic_under_insertion_order_variance() {
fn build_graph(insert_a_first: bool) -> (Graph, NodeId, NodeId, NodeId, EdgeId, EdgeId) {
let mut g = GraphBuilder::new(GraphId::from_u128(1));
let a = NodeId(uuid::Uuid::from_u128(1));
let b = NodeId(uuid::Uuid::from_u128(2));
let c = NodeId(uuid::Uuid::from_u128(3));
let nodes = [
(a, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None)),
(b, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None)),
(c, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None)),
];
if insert_a_first {
for (id, node) in nodes {
g.insert_node(id, node);
}
} else {
for (id, node) in nodes.into_iter().rev() {
g.insert_node(id, node);
}
}
let a_out_id = PortId(uuid::Uuid::from_u128(10));
let b_in_id = PortId(uuid::Uuid::from_u128(11));
let c_in_id = PortId(uuid::Uuid::from_u128(12));
let a_out = Port {
node: a,
key: PortKey::new("out"),
dir: PortDirection::Out,
kind: PortKind::Data,
capacity: PortCapacity::Multi,
connectable: None,
connectable_start: None,
connectable_end: None,
ty: None,
data: serde_json::Value::Null,
};
let b_in = Port {
node: b,
key: PortKey::new("in0"),
dir: PortDirection::In,
kind: PortKind::Data,
capacity: PortCapacity::Single,
connectable: None,
connectable_start: None,
connectable_end: None,
ty: None,
data: serde_json::Value::Null,
};
let c_in = Port {
node: c,
key: PortKey::new("in0"),
dir: PortDirection::In,
kind: PortKind::Data,
capacity: PortCapacity::Single,
connectable: None,
connectable_start: None,
connectable_end: None,
ty: None,
data: serde_json::Value::Null,
};
if insert_a_first {
g.insert_port(a_out_id, a_out);
g.insert_port(b_in_id, b_in);
g.insert_port(c_in_id, c_in);
} else {
g.insert_port(c_in_id, c_in);
g.insert_port(b_in_id, b_in);
g.insert_port(a_out_id, a_out);
}
let e1 = EdgeId(uuid::Uuid::from_u128(20));
let e2 = EdgeId(uuid::Uuid::from_u128(21));
if insert_a_first {
g.insert_edge(e1, data_edge(a_out_id, b_in_id));
g.insert_edge(e2, data_edge(a_out_id, c_in_id));
} else {
g.insert_edge(e2, data_edge(a_out_id, c_in_id));
g.insert_edge(e1, data_edge(a_out_id, b_in_id));
}
(g.into(), a, b, c, e1, e2)
}
let (g1, a1, b1, c1, e11, e21) = build_graph(true);
let (g2, a2, b2, c2, e12, e22) = build_graph(false);
assert_eq!((a1, b1, c1, e11, e21), (a2, b2, c2, e12, e22));
let mut l1 = NodeGraphLookups::default();
l1.rebuild_from(&g1);
let mut l2 = NodeGraphLookups::default();
l2.rebuild_from(&g2);
assert_eq!(get_outgoers(&l1, a1), get_outgoers(&l2, a2));
assert_eq!(get_incomers(&l1, b1), get_incomers(&l2, b2));
assert_eq!(get_incomers(&l1, c1), get_incomers(&l2, c2));
let mut expected_edges = vec![e11, e21];
expected_edges.sort();
let mut actual_edges_1 = get_connected_edges(&l1, a1);
actual_edges_1.sort();
let mut actual_edges_2 = get_connected_edges(&l2, a2);
actual_edges_2.sort();
assert_eq!(actual_edges_1, expected_edges);
assert_eq!(actual_edges_2, expected_edges);
}
#[test]
fn outgoers_and_incomers_include_self_for_self_loops_and_dedup() {
let mut g = GraphBuilder::new(GraphId::from_u128(1));
let a = NodeId::new();
let (a_out_id, a_out) = out_port(a);
let (a_in_id, a_in) = in_port(a, "in0");
g.insert_node(a, node_at(CanvasPoint { x: 0.0, y: 0.0 }, None));
g.insert_port(a_out_id, a_out);
g.insert_port(a_in_id, a_in);
let e1 = EdgeId::new();
let e2 = EdgeId::new();
g.insert_edge(e1, data_edge(a_out_id, a_in_id));
g.insert_edge(e2, data_edge(a_out_id, a_in_id));
let mut lookups = NodeGraphLookups::default();
lookups.rebuild_from(&g);
assert_eq!(get_outgoers(&lookups, a), vec![a]);
assert_eq!(get_incomers(&lookups, a), vec![a]);
let connected = get_connected_edges(&lookups, a);
assert_eq!(connected.len(), 2);
assert!(connected.contains(&e1));
assert!(connected.contains(&e2));
}