rete-cli 0.3.2

CLI to build and inspect Rete graph files.
use std::process::Command;

fn temp_path(name: &str, ext: &str) -> std::path::PathBuf {
    std::env::temp_dir().join(format!(
        "rete_progressive_cli_{}_{}.{}",
        std::process::id(),
        name,
        ext
    ))
}

#[test]
fn progressive_cli_answers_exact_count_from_summary_without_index() {
    let bin = env!("CARGO_BIN_EXE_rete");
    let data = temp_path("data", "nt");
    let file = temp_path("data", "rete");

    std::fs::write(
        &data,
        r#"<http://ex/Alice> <http://ex/knows> <http://ex/Bob> .
<http://ex/Bob> <http://ex/knows> <http://ex/Alice> .
<http://ex/Alice> <http://ex/age> "30" .
"#,
    )
    .unwrap();

    let build = Command::new(bin)
        .args([
            "build",
            data.to_str().unwrap(),
            "-o",
            file.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        build.status.success(),
        "{}",
        String::from_utf8_lossy(&build.stderr)
    );

    let query = "PREFIX e: <http://ex/> SELECT (COUNT(*) AS ?total) WHERE { ?s e:knows ?o }";
    let output = Command::new(bin)
        .args(["progressive", file.to_str().unwrap(), query, "--json"])
        .output()
        .unwrap();
    assert!(
        output.status.success(),
        "{}",
        String::from_utf8_lossy(&output.stderr)
    );

    let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
    assert_eq!(json["progressive"]["stage"], "summary");
    assert_eq!(json["progressive"]["exact"], true);
    assert_eq!(json["progressive"]["reads_index"], false);
    assert_eq!(json["progressive"]["query_shape"], "predicate_count");
    assert_eq!(json["progressive"]["predicate"], "<http://ex/knows>");
    assert!(json["progressive"]["requests"].as_u64().unwrap() >= 1);
    assert!(json["progressive"]["bytes"].as_u64().unwrap() >= 128);
    assert_eq!(json["head"]["vars"], serde_json::json!(["total"]));
    assert_eq!(
        json["results"]["bindings"][0]["total"],
        serde_json::json!({
            "type": "literal",
            "value": "2",
            "datatype": "http://www.w3.org/2001/XMLSchema#integer",
        })
    );

    let total_query = "SELECT (COUNT(*) AS ?total) WHERE { ?s ?p ?o }";
    let total_output = Command::new(bin)
        .args(["progressive", file.to_str().unwrap(), total_query, "--json"])
        .output()
        .unwrap();
    assert!(
        total_output.status.success(),
        "{}",
        String::from_utf8_lossy(&total_output.stderr)
    );

    let total_json: serde_json::Value = serde_json::from_slice(&total_output.stdout).unwrap();
    assert_eq!(total_json["progressive"]["query_shape"], "triple_count");
    assert_eq!(total_json["progressive"]["reads_index"], false);
    assert_eq!(total_json["head"]["vars"], serde_json::json!(["total"]));
    assert_eq!(total_json["results"]["bindings"][0]["total"]["value"], "3");

    let totals_query = "SELECT ?p (COUNT(*) AS ?n) WHERE { ?s ?p ?o } GROUP BY ?p";
    let totals_output = Command::new(bin)
        .args([
            "progressive",
            file.to_str().unwrap(),
            totals_query,
            "--json",
        ])
        .output()
        .unwrap();
    assert!(
        totals_output.status.success(),
        "{}",
        String::from_utf8_lossy(&totals_output.stderr)
    );

    let totals_json: serde_json::Value = serde_json::from_slice(&totals_output.stdout).unwrap();
    assert_eq!(
        totals_json["progressive"]["query_shape"],
        "predicate_totals"
    );
    assert_eq!(totals_json["progressive"]["reads_index"], false);
    assert_eq!(totals_json["head"]["vars"], serde_json::json!(["p", "n"]));
    assert_eq!(
        totals_json["results"]["bindings"][0]["p"]["value"],
        "http://ex/knows"
    );
    assert_eq!(totals_json["results"]["bindings"][0]["n"]["value"], "2");
    assert_eq!(
        totals_json["results"]["bindings"][1]["p"]["value"],
        "http://ex/age"
    );
    assert_eq!(totals_json["results"]["bindings"][1]["n"]["value"], "1");

    let predicates_query = "SELECT DISTINCT ?p WHERE { ?s ?p ?o }";
    let predicates_output = Command::new(bin)
        .args([
            "progressive",
            file.to_str().unwrap(),
            predicates_query,
            "--json",
        ])
        .output()
        .unwrap();
    assert!(
        predicates_output.status.success(),
        "{}",
        String::from_utf8_lossy(&predicates_output.stderr)
    );

    let predicates_json: serde_json::Value =
        serde_json::from_slice(&predicates_output.stdout).unwrap();
    assert_eq!(
        predicates_json["progressive"]["query_shape"],
        "predicate_list"
    );
    assert_eq!(predicates_json["progressive"]["reads_index"], false);
    assert_eq!(predicates_json["head"]["vars"], serde_json::json!(["p"]));
    assert_eq!(
        predicates_json["results"]["bindings"][0]["p"]["value"],
        "http://ex/knows"
    );
    assert_eq!(
        predicates_json["results"]["bindings"][1]["p"]["value"],
        "http://ex/age"
    );

    let predicate_count_query = "SELECT (COUNT(DISTINCT ?p) AS ?predicateCount) WHERE { ?s ?p ?o }";
    let predicate_count_output = Command::new(bin)
        .args([
            "progressive",
            file.to_str().unwrap(),
            predicate_count_query,
            "--json",
        ])
        .output()
        .unwrap();
    assert!(
        predicate_count_output.status.success(),
        "{}",
        String::from_utf8_lossy(&predicate_count_output.stderr)
    );

    let predicate_count_json: serde_json::Value =
        serde_json::from_slice(&predicate_count_output.stdout).unwrap();
    assert_eq!(
        predicate_count_json["progressive"]["query_shape"],
        "predicate_distinct_count"
    );
    assert_eq!(predicate_count_json["progressive"]["reads_index"], false);
    assert_eq!(
        predicate_count_json["head"]["vars"],
        serde_json::json!(["predicateCount"])
    );
    assert_eq!(
        predicate_count_json["results"]["bindings"][0]["predicateCount"]["value"],
        "2"
    );

    let any_ask_output = Command::new(bin)
        .args([
            "progressive",
            file.to_str().unwrap(),
            "ASK { ?s ?p ?o }",
            "--json",
        ])
        .output()
        .unwrap();
    assert!(
        any_ask_output.status.success(),
        "{}",
        String::from_utf8_lossy(&any_ask_output.stderr)
    );

    let any_ask_json: serde_json::Value = serde_json::from_slice(&any_ask_output.stdout).unwrap();
    assert_eq!(any_ask_json["boolean"], true);
    assert_eq!(any_ask_json["progressive"]["query_shape"], "triple_exists");
    assert_eq!(any_ask_json["progressive"]["reads_index"], false);

    std::fs::remove_file(data).ok();
    std::fs::remove_file(file).ok();
}