#[cfg(test)]
mod tests {
use crate::schema::SchemaAnalyzer;
#[test]
fn test_extract_owl_class_basic() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a owl:Class ;
rdfs:label "Person" ;
rdfs:comment "A human being" .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
assert!(!owl_classes.is_empty());
let person_class = owl_classes
.iter()
.find(|c| c.base.iri.contains("Person"))
.expect("Person class not found");
assert_eq!(person_class.base.label, Some("Person".to_string()));
assert_eq!(person_class.base.comment, Some("A human being".to_string()));
}
#[test]
fn test_extract_equivalent_class() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a owl:Class ;
owl:equivalentClass ex:Human .
ex:Human a owl:Class .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let person_class = owl_classes
.iter()
.find(|c| c.base.iri.contains("Person"))
.expect("Person class not found");
assert!(!person_class.equivalent_classes.is_empty());
assert!(person_class.equivalent_classes[0].contains("Human"));
}
#[test]
fn test_extract_union_of() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:LegalEntity a owl:Class ;
owl:unionOf ( ex:Person ex:Organization ) .
ex:Person a owl:Class .
ex:Organization a owl:Class .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let legal_entity = owl_classes
.iter()
.find(|c| c.base.iri.contains("LegalEntity"))
.expect("LegalEntity class not found");
if !legal_entity.union_of.is_empty() {
assert!(!legal_entity.union_of[0].is_empty());
}
}
#[test]
fn test_extract_intersection_of() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:WorkingParent a owl:Class ;
owl:intersectionOf ( ex:Parent ex:Employee ) .
ex:Parent a owl:Class .
ex:Employee a owl:Class .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let working_parent = owl_classes
.iter()
.find(|c| c.base.iri.contains("WorkingParent"))
.expect("WorkingParent class not found");
if !working_parent.intersection_of.is_empty() {
assert!(!working_parent.intersection_of[0].is_empty());
}
}
#[test]
fn test_extract_complement_of() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:NonPerson a owl:Class ;
owl:complementOf ex:Person .
ex:Person a owl:Class .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let non_person = owl_classes
.iter()
.find(|c| c.base.iri.contains("NonPerson"))
.expect("NonPerson class not found");
assert!(!non_person.complement_of.is_empty());
assert!(non_person.complement_of[0].contains("Person"));
}
#[test]
fn test_extract_disjoint_with() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a owl:Class ;
owl:disjointWith ex:Organization .
ex:Organization a owl:Class .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let person_class = owl_classes
.iter()
.find(|c| c.base.iri.contains("Person"))
.expect("Person class not found");
assert!(!person_class.disjoint_with.is_empty());
assert!(person_class.disjoint_with[0].contains("Organization"));
}
#[test]
fn test_extract_functional_property() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:hasSSN a owl:FunctionalProperty, rdf:Property ;
rdfs:domain ex:Person ;
rdfs:range ex:SSN .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let ssn_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("hasSSN"))
.expect("hasSSN property not found");
assert!(ssn_prop.characteristics.functional);
}
#[test]
fn test_extract_inverse_functional_property() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:hasEmail a owl:InverseFunctionalProperty, rdf:Property ;
rdfs:domain ex:Person ;
rdfs:range ex:Email .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let email_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("hasEmail"))
.expect("hasEmail property not found");
assert!(email_prop.characteristics.inverse_functional);
}
#[test]
fn test_extract_transitive_property() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:ancestorOf a owl:TransitiveProperty, rdf:Property ;
rdfs:domain ex:Person ;
rdfs:range ex:Person .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let ancestor_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("ancestorOf"))
.expect("ancestorOf property not found");
assert!(ancestor_prop.characteristics.transitive);
}
#[test]
fn test_extract_symmetric_property() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:friendOf a owl:SymmetricProperty, rdf:Property ;
rdfs:domain ex:Person ;
rdfs:range ex:Person .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let friend_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("friendOf"))
.expect("friendOf property not found");
assert!(friend_prop.characteristics.symmetric);
}
#[test]
fn test_extract_inverse_of() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:hasChild a rdf:Property ;
owl:inverseOf ex:hasParent .
ex:hasParent a rdf:Property .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let has_child = owl_properties
.iter()
.find(|p| p.base.iri.contains("hasChild"))
.expect("hasChild property not found");
assert!(has_child.inverse_of.is_some());
assert!(has_child
.inverse_of
.as_ref()
.expect("unwrap")
.contains("hasParent"));
}
#[test]
fn test_extract_equivalent_property() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:hasName a rdf:Property ;
owl:equivalentProperty ex:name .
ex:name a rdf:Property .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let has_name = owl_properties
.iter()
.find(|p| p.base.iri.contains("hasName"))
.expect("hasName property not found");
assert!(!has_name.equivalent_properties.is_empty());
assert!(has_name.equivalent_properties[0].contains("name"));
}
#[test]
fn test_extract_owl_restriction_some_values_from() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Parent a owl:Class ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty ex:hasChild ;
owl:someValuesFrom ex:Person
] .
ex:Person a owl:Class .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let parent_class = owl_classes
.iter()
.find(|c| c.base.iri.contains("Parent"))
.expect("Parent class not found");
let parent_node = oxrdf::NamedNode::new(parent_class.base.iri.clone()).expect("unwrap");
let restrictions = analyzer
.extract_owl_restrictions(&parent_node)
.expect("unwrap");
if !restrictions.is_empty() {
match &restrictions[0] {
crate::schema::owl::OwlRestriction::SomeValuesFrom { on_property, class } => {
assert!(on_property.contains("hasChild"));
assert!(class.contains("Person"));
}
_ => panic!("Expected SomeValuesFrom restriction"),
}
}
}
#[test]
fn test_extract_owl_restriction_all_values_from() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:HappyPerson a owl:Class ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty ex:hasChild ;
owl:allValuesFrom ex:HappyPerson
] .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let happy_person = owl_classes
.iter()
.find(|c| c.base.iri.contains("HappyPerson"))
.expect("HappyPerson class not found");
let happy_node = oxrdf::NamedNode::new(happy_person.base.iri.clone()).expect("unwrap");
let restrictions = analyzer
.extract_owl_restrictions(&happy_node)
.expect("unwrap");
if !restrictions.is_empty() {
match &restrictions[0] {
crate::schema::owl::OwlRestriction::AllValuesFrom { on_property, class } => {
assert!(on_property.contains("hasChild"));
assert!(class.contains("HappyPerson"));
}
_ => panic!("Expected AllValuesFrom restriction"),
}
}
}
#[test]
fn test_extract_owl_restriction_min_cardinality() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Parent a owl:Class ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty ex:hasChild ;
owl:minCardinality "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>
] .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let parent_class = owl_classes
.iter()
.find(|c| c.base.iri.contains("Parent"))
.expect("Parent class not found");
let parent_node = oxrdf::NamedNode::new(parent_class.base.iri.clone()).expect("unwrap");
let restrictions = analyzer
.extract_owl_restrictions(&parent_node)
.expect("unwrap");
if !restrictions.is_empty() {
match &restrictions[0] {
crate::schema::owl::OwlRestriction::MinCardinality {
on_property,
cardinality,
} => {
assert!(on_property.contains("hasChild"));
assert_eq!(*cardinality, 1);
}
_ => panic!("Expected MinCardinality restriction"),
}
}
}
#[test]
fn test_extract_owl_restriction_max_cardinality() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Monogamist a owl:Class ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty ex:hasSpouse ;
owl:maxCardinality "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>
] .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let monogamist = owl_classes
.iter()
.find(|c| c.base.iri.contains("Monogamist"))
.expect("Monogamist class not found");
let mono_node = oxrdf::NamedNode::new(monogamist.base.iri.clone()).expect("unwrap");
let restrictions = analyzer
.extract_owl_restrictions(&mono_node)
.expect("unwrap");
if !restrictions.is_empty() {
match &restrictions[0] {
crate::schema::owl::OwlRestriction::MaxCardinality {
on_property,
cardinality,
} => {
assert!(on_property.contains("hasSpouse"));
assert_eq!(*cardinality, 1);
}
_ => panic!("Expected MaxCardinality restriction"),
}
}
}
#[test]
fn test_multiple_property_characteristics() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:knows a rdf:Property, owl:SymmetricProperty, owl:TransitiveProperty ;
rdfs:domain ex:Person ;
rdfs:range ex:Person .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
let knows_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("knows"))
.expect("knows property not found");
assert!(knows_prop.characteristics.symmetric);
assert!(knows_prop.characteristics.transitive);
}
#[test]
fn test_complex_owl_ontology() {
let owl_turtle = r#"
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:Person a owl:Class ;
rdfs:label "Person" ;
owl:disjointWith ex:Organization .
ex:Organization a owl:Class ;
rdfs:label "Organization" .
ex:Employee a owl:Class ;
rdfs:subClassOf ex:Person ;
rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty ex:worksFor ;
owl:someValuesFrom ex:Organization
] .
ex:worksFor a rdf:Property ;
rdfs:domain ex:Employee ;
rdfs:range ex:Organization .
ex:employs a rdf:Property ;
owl:inverseOf ex:worksFor .
ex:hasSSN a owl:FunctionalProperty, rdf:Property ;
rdfs:domain ex:Person .
"#;
let mut analyzer = SchemaAnalyzer::new();
analyzer.load_turtle(owl_turtle).expect("unwrap");
analyzer.analyze().expect("unwrap");
let owl_classes = analyzer.extract_owl_classes().expect("unwrap");
let owl_properties = analyzer.extract_owl_properties().expect("unwrap");
assert!(
owl_classes.len() >= 3,
"Expected at least 3 classes, got {}",
owl_classes.len()
);
let person = owl_classes
.iter()
.find(|c| c.base.iri.contains("Person"))
.expect("Person class not found");
assert!(!person.disjoint_with.is_empty());
let employee = owl_classes
.iter()
.find(|c| c.base.iri.contains("Employee"))
.expect("Employee class not found");
let emp_node = oxrdf::NamedNode::new(employee.base.iri.clone()).expect("unwrap");
let restrictions = analyzer
.extract_owl_restrictions(&emp_node)
.expect("unwrap");
assert!(
!restrictions.is_empty(),
"Expected at least one restriction for Employee"
);
let ssn_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("hasSSN"))
.expect("hasSSN property not found");
assert!(ssn_prop.characteristics.functional);
let employs_prop = owl_properties
.iter()
.find(|p| p.base.iri.contains("employs"))
.expect("employs property not found");
assert!(employs_prop.inverse_of.is_some());
}
}