#[cfg(all(feature = "sparql", feature = "triple-store"))]
mod tests {
use grafeo_engine::{Config, GrafeoDB, GraphModel};
fn rdf_db() -> GrafeoDB {
GrafeoDB::with_config(Config::in_memory().with_graph_model(GraphModel::Rdf)).unwrap()
}
#[test]
fn create_graph_succeeds() {
let db = rdf_db();
let session = db.session();
let result = session.execute_sparql("CREATE GRAPH <http://ex.org/g1>");
assert!(result.is_ok());
}
#[test]
fn create_duplicate_graph_errors() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql("CREATE GRAPH <http://ex.org/g1>")
.unwrap();
let result = session.execute_sparql("CREATE GRAPH <http://ex.org/g1>");
assert!(result.is_err());
}
#[test]
fn create_duplicate_graph_silent_ok() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql("CREATE GRAPH <http://ex.org/g1>")
.unwrap();
let result = session.execute_sparql("CREATE SILENT GRAPH <http://ex.org/g1>");
assert!(result.is_ok());
}
#[test]
fn drop_graph_succeeds() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql("CREATE GRAPH <http://ex.org/g1>")
.unwrap();
let result = session.execute_sparql("DROP GRAPH <http://ex.org/g1>");
assert!(result.is_ok());
}
#[test]
fn drop_nonexistent_graph_errors() {
let db = rdf_db();
let session = db.session();
let result = session.execute_sparql("DROP GRAPH <http://ex.org/nope>");
assert!(result.is_err());
}
#[test]
fn drop_nonexistent_graph_silent_ok() {
let db = rdf_db();
let session = db.session();
let result = session.execute_sparql("DROP SILENT GRAPH <http://ex.org/nope>");
assert!(result.is_ok());
}
#[test]
fn drop_default_clears_default_graph() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
<http://ex.org/a> <http://ex.org/p> "1" .
}"#,
)
.unwrap();
session.execute_sparql("DROP DEFAULT").unwrap();
let result = session
.execute_sparql("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")
.unwrap();
assert_eq!(
result.row_count(),
0,
"DROP DEFAULT should clear the default graph"
);
}
#[test]
fn clear_default_graph() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
<http://ex.org/a> <http://ex.org/p> "1" .
<http://ex.org/b> <http://ex.org/p> "2" .
}"#,
)
.unwrap();
session.execute_sparql("CLEAR DEFAULT").unwrap();
let result = session
.execute_sparql("SELECT ?s WHERE { ?s ?p ?o }")
.unwrap();
assert_eq!(result.row_count(), 0);
}
#[test]
fn clear_named_graph_preserves_default() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
<http://ex.org/a> <http://ex.org/p> "1" .
}"#,
)
.unwrap();
let rdf = db.rdf_store();
rdf.graph_or_create("http://ex.org/g1");
if let Some(g) = rdf.graph("http://ex.org/g1") {
use grafeo_core::graph::rdf::{Literal, Term, Triple};
g.insert(Triple::new(
Term::iri("http://ex.org/b"),
Term::iri("http://ex.org/p"),
Term::Literal(Literal::simple("2")),
));
}
session
.execute_sparql("CLEAR GRAPH <http://ex.org/g1>")
.unwrap();
let result = session
.execute_sparql("SELECT ?s WHERE { ?s ?p ?o }")
.unwrap();
assert_eq!(result.row_count(), 1, "Default graph should be untouched");
let named = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/g1> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(named.row_count(), 0, "Named graph should be cleared");
}
#[test]
fn graph_clause_queries_named_graph() {
let db = rdf_db();
let rdf = db.rdf_store();
let g = rdf.graph_or_create("http://ex.org/g1");
{
use grafeo_core::graph::rdf::{Literal, Term, Triple};
g.insert(Triple::new(
Term::iri("http://ex.org/alix"),
Term::iri("http://ex.org/name"),
Term::Literal(Literal::simple("Alix")),
));
}
let session = db.session();
let result = session
.execute_sparql(
r#"SELECT ?s ?name WHERE {
GRAPH <http://ex.org/g1> { ?s <http://ex.org/name> ?name }
}"#,
)
.unwrap();
assert_eq!(result.row_count(), 1);
}
#[test]
fn graph_variable_scans_all_graphs() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
<http://ex.org/alix> <http://ex.org/name> "Alix" .
}"#,
)
.unwrap();
let rdf = db.rdf_store();
{
use grafeo_core::graph::rdf::{Literal, Term, Triple};
let g1 = rdf.graph_or_create("http://ex.org/g1");
g1.insert(Triple::new(
Term::iri("http://ex.org/gus"),
Term::iri("http://ex.org/name"),
Term::Literal(Literal::simple("Gus")),
));
let g2 = rdf.graph_or_create("http://ex.org/g2");
g2.insert(Triple::new(
Term::iri("http://ex.org/harm"),
Term::iri("http://ex.org/name"),
Term::Literal(Literal::simple("Harm")),
));
}
let result = session
.execute_sparql(
r#"SELECT ?g ?s ?name WHERE {
GRAPH ?g { ?s <http://ex.org/name> ?name }
}"#,
)
.unwrap();
assert!(
result.row_count() >= 2,
"Expected >= 2 rows from named graphs, got {}",
result.row_count()
);
}
#[test]
fn insert_data_graph_routes_to_named_graph() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
GRAPH <http://ex.org/g1> {
<http://ex.org/alix> <http://ex.org/name> "Alix" .
}
}"#,
)
.unwrap();
let default_result = session
.execute_sparql("SELECT ?s WHERE { ?s ?p ?o }")
.unwrap();
assert_eq!(
default_result.row_count(),
0,
"Default graph should be empty after INSERT DATA GRAPH"
);
let named_result = session
.execute_sparql(
r#"SELECT ?name WHERE {
GRAPH <http://ex.org/g1> {
<http://ex.org/alix> <http://ex.org/name> ?name
}
}"#,
)
.unwrap();
assert_eq!(
named_result.row_count(),
1,
"Named graph should have the inserted triple"
);
}
#[test]
fn delete_data_graph_removes_from_named_graph() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
GRAPH <http://ex.org/g1> {
<http://ex.org/alix> <http://ex.org/name> "Alix" .
<http://ex.org/gus> <http://ex.org/name> "Gus" .
}
}"#,
)
.unwrap();
session
.execute_sparql(
r#"DELETE DATA {
GRAPH <http://ex.org/g1> {
<http://ex.org/alix> <http://ex.org/name> "Alix" .
}
}"#,
)
.unwrap();
let result = session
.execute_sparql(
r#"SELECT ?name WHERE {
GRAPH <http://ex.org/g1> { ?s <http://ex.org/name> ?name }
}"#,
)
.unwrap();
assert_eq!(result.row_count(), 1, "Should have 1 triple after delete");
}
#[test]
fn insert_data_graph_does_not_leak_to_default() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
<http://ex.org/default> <http://ex.org/p> "default" .
}"#,
)
.unwrap();
session
.execute_sparql(
r#"INSERT DATA {
GRAPH <http://ex.org/g1> {
<http://ex.org/named> <http://ex.org/p> "named" .
}
}"#,
)
.unwrap();
let default_result = session
.execute_sparql("SELECT ?s WHERE { ?s ?p ?o }")
.unwrap();
assert_eq!(default_result.row_count(), 1, "Default graph: 1 triple");
let named_result = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/g1> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(named_result.row_count(), 1, "Named graph: 1 triple");
}
#[test]
fn graph_clause_isolates_from_default() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
<http://ex.org/alix> <http://ex.org/name> "Alix" .
}"#,
)
.unwrap();
let rdf = db.rdf_store();
{
use grafeo_core::graph::rdf::{Literal, Term, Triple};
let g = rdf.graph_or_create("http://ex.org/g1");
g.insert(Triple::new(
Term::iri("http://ex.org/gus"),
Term::iri("http://ex.org/name"),
Term::Literal(Literal::simple("Gus")),
));
}
let default_result = session
.execute_sparql(r#"SELECT ?s WHERE { ?s <http://ex.org/name> ?name }"#)
.unwrap();
assert_eq!(
default_result.row_count(),
1,
"Default graph should have 1 triple"
);
let named_result = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/g1> { ?s <http://ex.org/name> ?name }
}"#,
)
.unwrap();
assert_eq!(
named_result.row_count(),
1,
"Named graph should have 1 triple"
);
}
#[test]
fn copy_named_graph_preserves_source() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
GRAPH <http://ex.org/src> {
<http://ex.org/a> <http://ex.org/p> "1" .
<http://ex.org/b> <http://ex.org/q> "2" .
}
}"#,
)
.unwrap();
session
.execute_sparql("COPY <http://ex.org/src> TO <http://ex.org/dst>")
.unwrap();
let src = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/src> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(src.row_count(), 2, "Source should still have 2 triples");
let dst = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/dst> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(dst.row_count(), 2, "Dest should have 2 triples");
}
#[test]
fn move_named_graph_removes_source() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
GRAPH <http://ex.org/src> {
<http://ex.org/a> <http://ex.org/p> "val" .
}
}"#,
)
.unwrap();
session
.execute_sparql("MOVE <http://ex.org/src> TO <http://ex.org/dst>")
.unwrap();
let src = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/src> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(src.row_count(), 0, "Source should be empty after MOVE");
let dst = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/dst> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(dst.row_count(), 1, "Dest should have 1 triple");
}
#[test]
fn add_merges_into_destination() {
let db = rdf_db();
let session = db.session();
session
.execute_sparql(
r#"INSERT DATA {
GRAPH <http://ex.org/g1> {
<http://ex.org/a> <http://ex.org/p> "from-g1" .
}
GRAPH <http://ex.org/g2> {
<http://ex.org/b> <http://ex.org/q> "from-g2" .
}
}"#,
)
.unwrap();
session
.execute_sparql("ADD <http://ex.org/g1> TO <http://ex.org/g2>")
.unwrap();
let g1 = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/g1> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(g1.row_count(), 1, "g1 should still have 1 triple");
let g2 = session
.execute_sparql(
r#"SELECT ?s WHERE {
GRAPH <http://ex.org/g2> { ?s ?p ?o }
}"#,
)
.unwrap();
assert_eq!(g2.row_count(), 2, "g2 should have 2 triples after ADD");
}
#[test]
fn copy_nonexistent_source_errors() {
let db = rdf_db();
let session = db.session();
let result = session.execute_sparql("COPY <http://ex.org/nope> TO <http://ex.org/dst>");
assert!(result.is_err(), "COPY from nonexistent graph should error");
}
#[test]
fn copy_silent_nonexistent_source_ok() {
let db = rdf_db();
let session = db.session();
let result =
session.execute_sparql("COPY SILENT <http://ex.org/nope> TO <http://ex.org/dst>");
assert!(
result.is_ok(),
"COPY SILENT from nonexistent graph should succeed"
);
}
}