oxirs-arq 0.2.4

Jena-style SPARQL algebra with extension points and query optimization
Documentation
//! Update Operation Conformance Tests
//!
//! Tests SPARQL 1.1 Update operations. Since update operations modify the
//! store and our test framework uses read-only datasets, we test the
//! algebra construction and that updates don't error.

use super::framework::*;
use super::helpers::*;

fn runner() -> ConformanceTestRunner {
    ConformanceTestRunner::new()
}

// ===== UPDATE-LIKE ALGEBRA TESTS =====
// These tests verify the engine can handle update-related algebra structures
// The actual store modification is tested via the oxirs-tdb crate

#[test]
fn test_update_01_select_for_insert() {
    // Verify we can select data that would be inserted
    // SELECT ?s ?name WHERE { ?s foaf:name ?name }
    let ds = person_dataset();
    let algebra = project(
        bgp(vec![triple(var("s"), foaf("name"), var("name"))]),
        vec![variable("s"), variable("name")],
    );
    let test = ConformanceTest::new(
        "update-01",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(3),
    );
    runner().run_test(&test).expect("update-01 failed");
}

#[test]
fn test_update_02_select_for_delete() {
    // SELECT ?s WHERE { ?s foaf:name "Alice" } (pattern for DELETE)
    let ds = person_dataset();
    let algebra = project(
        filter(
            bgp(vec![triple(var("s"), foaf("name"), var("name"))]),
            expr_eq(expr_var("name"), expr_lit(lit_str("Alice"))),
        ),
        vec![variable("s")],
    );
    let test = ConformanceTest::new(
        "update-02",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(1),
    );
    runner().run_test(&test).expect("update-02 failed");
}

#[test]
fn test_update_03_where_clause_for_update() {
    // Selecting data for WHERE clause of INSERT/DELETE
    let ds = numeric_dataset();
    let algebra = project(
        bgp(vec![triple(var("s"), ex("value"), var("v"))]),
        vec![variable("s"), variable("v")],
    );
    let test = ConformanceTest::new(
        "update-03",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(5),
    );
    runner().run_test(&test).expect("update-03 failed");
}

#[test]
fn test_update_04_clear_all_equivalent() {
    // Equivalent to CLEAR ALL — but as a query verifying all data
    let ds = person_dataset();
    let algebra = bgp(vec![triple(var("s"), var("p"), var("o"))]);
    let test = ConformanceTest::new(
        "update-04",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(9),
    );
    runner().run_test(&test).expect("update-04 failed");
}

#[test]
fn test_update_05_insert_data_verification() {
    // After INSERT DATA, verify the data is present
    // We simulate by starting with the data already there
    let mut ds = InMemoryDataset::new();
    ds.add_triple(ex("newPerson"), foaf("name"), str_lit("NewPerson"));
    ds.add_triple(ex("newPerson"), rdf_type(), iri(&format!("{FOAF}Person")));

    let algebra = project(
        bgp(vec![triple(
            var("s"),
            rdf_type(),
            iri(&format!("{FOAF}Person")),
        )]),
        vec![variable("s")],
    );
    let test = ConformanceTest::new(
        "update-05",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(1),
    );
    runner().run_test(&test).expect("update-05 failed");
}

#[test]
fn test_update_06_delete_data_verification() {
    // After DELETE DATA, data should be absent
    // Simulate by having empty dataset (data was deleted)
    let ds = InMemoryDataset::new();
    let algebra = project(
        bgp(vec![triple(
            var("s"),
            foaf("name"),
            str_lit("DeletedPerson"),
        )]),
        vec![variable("s")],
    );
    let test = ConformanceTest::new(
        "update-06",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(0),
    );
    runner().run_test(&test).expect("update-06 failed");
}

#[test]
fn test_update_07_insert_where_pattern() {
    // Simulate INSERT WHERE by computing what would be inserted
    // INSERT { ?s :category :Adult } WHERE { ?s foaf:age ?age . FILTER(?age >= 18) }
    let ds = person_dataset();
    let where_algebra = project(
        filter(
            bgp(vec![triple(
                var("s"),
                iri(&format!("{FOAF}age")),
                var("age"),
            )]),
            Expression::Binary {
                op: crate::algebra::BinaryOperator::GreaterEqual,
                left: Box::new(expr_var("age")),
                right: Box::new(expr_lit(lit_int(18))),
            },
        ),
        vec![variable("s"), variable("age")],
    );
    let test = ConformanceTest::new(
        "update-07",
        ConformanceGroup::Update,
        where_algebra,
        ds,
        ConformanceResult::ResultCount(3), // all 3 persons >= 18
    );
    runner().run_test(&test).expect("update-07 failed");
}

#[test]
fn test_update_08_delete_where_pattern() {
    // DELETE WHERE { ?s foaf:name ?name . FILTER(?name = "Bob") }
    // Computes what would be deleted
    let ds = person_dataset();
    let where_algebra = project(
        filter(
            bgp(vec![triple(var("s"), foaf("name"), var("name"))]),
            expr_eq(expr_var("name"), expr_lit(lit_str("Bob"))),
        ),
        vec![variable("s"), variable("name")],
    );
    let test = ConformanceTest::new(
        "update-08",
        ConformanceGroup::Update,
        where_algebra,
        ds,
        ConformanceResult::ResultCount(1), // only Bob would be deleted
    );
    runner().run_test(&test).expect("update-08 failed");
}

#[test]
fn test_update_09_load_equivalent() {
    // LOAD is equivalent to having data in the store
    let mut ds = InMemoryDataset::new();
    // Simulate loaded data
    for i in 0..10 {
        ds.add_triple(ex(&format!("item{i}")), ex("loaded"), bool_lit(true));
    }
    let algebra = bgp(vec![triple(var("s"), ex("loaded"), var("v"))]);
    let test = ConformanceTest::new(
        "update-09",
        ConformanceGroup::Update,
        algebra,
        ds,
        ConformanceResult::ResultCount(10),
    );
    runner().run_test(&test).expect("update-09 failed");
}

#[test]
fn test_update_10_move_equivalent() {
    // MOVE is equivalent to source empty, destination populated
    let mut dest_ds = InMemoryDataset::new();
    dest_ds.add_triple(ex("moved"), ex("from"), ex("source"));
    dest_ds.add_triple(ex("moved2"), ex("from"), ex("source"));

    let algebra = bgp(vec![triple(var("s"), ex("from"), ex("source"))]);
    let test = ConformanceTest::new(
        "update-10",
        ConformanceGroup::Update,
        algebra,
        dest_ds,
        ConformanceResult::ResultCount(2),
    );
    runner().run_test(&test).expect("update-10 failed");
}