use pattern_core::{
canonical_classifier, classify_by_shape, from_patterns, from_patterns_with_policy, GraphClass,
GraphClassifier, Pattern, PatternGraph, ReconciliationPolicy, Subject, Symbol,
};
use std::collections::{HashMap, HashSet};
fn node(s: &str) -> Pattern<Subject> {
Pattern {
value: Subject {
identity: Symbol(s.to_string()),
labels: HashSet::new(),
properties: HashMap::new(),
},
elements: vec![],
}
}
fn rel(r: &str, a: &str, b: &str) -> Pattern<Subject> {
Pattern {
value: Subject {
identity: Symbol(r.to_string()),
labels: HashSet::new(),
properties: HashMap::new(),
},
elements: vec![node(a), node(b)],
}
}
#[test]
fn empty_graph_has_zero_counts() {
let g: PatternGraph<(), Subject> = PatternGraph::empty();
assert_eq!(g.pg_nodes.len(), 0);
assert_eq!(g.pg_relationships.len(), 0);
assert_eq!(g.pg_walks.len(), 0);
assert_eq!(g.pg_annotations.len(), 0);
assert_eq!(g.pg_other.len(), 0);
assert_eq!(g.pg_conflicts.len(), 0);
}
#[test]
fn merge_node_appears_in_pg_nodes() {
let classifier = canonical_classifier::<Subject>();
let g = pattern_core::pg_merge(&classifier, node("a"), PatternGraph::empty());
assert_eq!(g.pg_nodes.len(), 1);
assert_eq!(g.pg_relationships.len(), 0);
}
#[test]
fn merge_relationship_inserts_endpoints_and_rel() {
let classifier = canonical_classifier::<Subject>();
let g0 = PatternGraph::empty();
let g1 = pattern_core::pg_merge(&classifier, node("a"), g0);
let g2 = pattern_core::pg_merge(&classifier, node("b"), g1);
let g3 = pattern_core::pg_merge(&classifier, rel("r", "a", "b"), g2);
assert_eq!(g3.pg_nodes.len(), 2);
assert_eq!(g3.pg_relationships.len(), 1);
}
#[test]
fn from_patterns_builds_correct_counts() {
let classifier = canonical_classifier::<Subject>();
let patterns = vec![node("a"), node("b"), rel("r1", "a", "b")];
let g = from_patterns(&classifier, patterns);
assert_eq!(g.pg_nodes.len(), 2);
assert_eq!(g.pg_relationships.len(), 1);
}
#[test]
fn unrecognized_pattern_goes_to_pg_other() {
let classifier = canonical_classifier::<Subject>();
let weird = Pattern {
value: Subject {
identity: Symbol("w".to_string()),
labels: HashSet::new(),
properties: HashMap::new(),
},
elements: vec![node("a"), node("b"), node("c")],
};
let g = from_patterns(&classifier, vec![node("a"), weird]);
assert_eq!(g.pg_nodes.len(), 1);
assert_eq!(g.pg_other.len(), 1);
}
#[test]
fn duplicate_identity_last_write_wins() {
let classifier = canonical_classifier::<Subject>();
let g = from_patterns_with_policy(
&classifier,
&ReconciliationPolicy::LastWriteWins,
vec![node("a"), node("a"), node("a")],
);
assert_eq!(g.pg_nodes.len(), 1);
}
#[test]
fn from_patterns_first_write_wins() {
let classifier = canonical_classifier::<Subject>();
let g = from_patterns_with_policy(
&classifier,
&ReconciliationPolicy::FirstWriteWins,
vec![node("a"), node("a")],
);
assert_eq!(g.pg_nodes.len(), 1);
}
#[test]
fn walk_decomposition_stores_walk_rels_and_nodes() {
let classifier = canonical_classifier::<Subject>();
let r1 = rel("r1", "a", "b");
let r2 = rel("r2", "b", "c");
let walk_pat = Pattern {
value: Subject {
identity: Symbol("path".to_string()),
labels: HashSet::new(),
properties: HashMap::new(),
},
elements: vec![r1, r2],
};
let g = pattern_core::pg_merge(&classifier, walk_pat, PatternGraph::empty());
assert_eq!(g.pg_walks.len(), 1);
assert_eq!(g.pg_relationships.len(), 2);
assert_eq!(g.pg_nodes.len(), 3);
assert!(g.pg_walks.contains_key(&Symbol("path".to_string())));
assert!(g.pg_relationships.contains_key(&Symbol("r1".to_string())));
assert!(g.pg_relationships.contains_key(&Symbol("r2".to_string())));
assert!(g.pg_nodes.contains_key(&Symbol("a".to_string())));
assert!(g.pg_nodes.contains_key(&Symbol("b".to_string())));
assert!(g.pg_nodes.contains_key(&Symbol("c".to_string())));
}
#[derive(Debug, PartialEq)]
enum MyDomain {
DomainHyperedge,
DomainOther,
}
#[test]
fn custom_classifier_routes_to_pg_other_with_typed_tag() {
let my_classifier = GraphClassifier::new(|p: &Pattern<Subject>| {
if p.elements.len() > 2 && p.elements.iter().all(|e| e.elements.is_empty()) {
GraphClass::GOther(MyDomain::DomainHyperedge)
} else {
match classify_by_shape(p) {
GraphClass::GNode => GraphClass::GNode,
GraphClass::GRelationship => GraphClass::GRelationship,
GraphClass::GWalk => GraphClass::GWalk,
GraphClass::GAnnotation => GraphClass::GAnnotation,
GraphClass::GOther(()) => GraphClass::GOther(MyDomain::DomainOther),
}
}
});
let n1 = node("n1");
let n2 = node("n2");
let n3 = node("n3");
let hyperedge = Pattern {
value: Subject {
identity: Symbol("hyper".to_string()),
labels: HashSet::new(),
properties: HashMap::new(),
},
elements: vec![node("n1"), node("n2"), node("n3")],
};
let g = from_patterns(&my_classifier, vec![n1, n2, n3, hyperedge]);
assert_eq!(g.pg_nodes.len(), 3);
assert_eq!(g.pg_other.len(), 1);
let (tag, _pat) = g
.pg_other
.get(&Symbol("hyper".to_string()))
.expect("hyperedge should be in pg_other");
assert_eq!(*tag, MyDomain::DomainHyperedge);
}