warble 0.19.0

Warble front-end compiler: parse/resolve/validate a behavior profile into language-neutral IR (sans-IO).
Documentation
use serde_json::json;
use warble::{verify_context_preconditions, ExternalContext, PreparedContext};

fn snapshot() -> serde_json::Value {
    json!({"context_version":2,"parseable":true,
        "metrics":[{"name":"revenue","owner":"orders","declared":true,"additivity":"additive"}],
        "dimensions":[{"name":"date","owner":"orders","is_temporal":true}],
        "models":[{"name":"orders","has_timestamp":true},{"name":"plain","has_timestamp":false}],
        "source_introspectable":true,"raw_docs_readable":true})
}

#[test]
fn runtime_predicates_use_the_full_core_vocabulary_and_resolved_arguments() {
    let context = PreparedContext::from_json(&snapshot().to_string()).unwrap();
    for predicate in [
        "mdl_parseable",
        "wren_project_exists",
        "has_metric",
        "has_queryable_dimension",
        "has_groupable_dimension",
        "has_time_dimension",
        "model_has_timestamp",
        "metric_additive",
        "lineage_resolvable",
        "source_introspectable",
        "raw_docs_readable",
    ] {
        verify_context_preconditions(&json!([{"predicate":predicate}]), &context).unwrap();
    }
    verify_context_preconditions(
        &json!([
            {"predicate":"model_has_timestamp","args":{"model":"orders"}},
            {"predicate":"metric_additive","args":{"metric":"revenue"}}
        ]),
        &context,
    )
    .unwrap();
    let failed = verify_context_preconditions(
        &json!([
            {"predicate":"model_has_timestamp","args":{"model":"plain"}}
        ]),
        &context,
    )
    .unwrap_err();
    assert!(failed.to_string().contains("failed"));
    let missing = verify_context_preconditions(
        &json!([
            {"predicate":"model_has_timestamp","args":{"model":"missing"}}
        ]),
        &context,
    )
    .unwrap_err();
    assert!(missing.to_string().contains("unanswerable"));
}

#[test]
fn runtime_verification_rejects_unknown_or_unresolved_argument_shapes() {
    let context = PreparedContext::from_json(&snapshot().to_string()).unwrap();
    for condition in [
        json!(null),
        json!({"predicate":"unknown"}),
        json!({"predicate":"mdl_parseable","outcome":"pass"}),
        json!({"predicate":"model_has_timestamp","args":null}),
        json!({"predicate":"model_has_timestamp","args":{"model":4}}),
        json!({"predicate":"model_has_timestamp","args":{"model":""}}),
        json!({"predicate":"model_has_timestamp","args":{"model":"$param:model"}}),
    ] {
        assert!(verify_context_preconditions(&json!([condition]), &context).is_err());
    }
    assert!(verify_context_preconditions(&json!({}), &context).is_err());
    verify_context_preconditions(&json!([]), &context).unwrap();
}

#[test]
fn changed_snapshot_and_unanswerable_probes_never_reuse_a_pass() {
    let mut value = snapshot();
    let conditions = json!([{"predicate":"mdl_parseable"}]);
    verify_context_preconditions(
        &conditions,
        &PreparedContext::from_json(&value.to_string()).unwrap(),
    )
    .unwrap();
    value["parseable"] = json!(false);
    assert!(verify_context_preconditions(
        &conditions,
        &PreparedContext::from_json(&value.to_string()).unwrap()
    )
    .is_err());
    let minimal = PreparedContext::from_json(r#"{"context_version":2,"parseable":true}"#).unwrap();
    for predicate in [
        "source_introspectable",
        "raw_docs_readable",
        "metric_additive",
    ] {
        let error =
            verify_context_preconditions(&json!([{"predicate":predicate}]), &minimal).unwrap_err();
        assert!(error.to_string().contains("unanswerable"));
    }
    assert!(verify_context_preconditions(&conditions, &ExternalContext::new()).is_err());
}

#[test]
fn resolved_auxiliary_arguments_keep_the_compiler_representation() {
    let context = PreparedContext::from_json(&snapshot().to_string()).unwrap();
    verify_context_preconditions(
        &json!([
            {"predicate":"mdl_parseable","args":{"aux":{"tags":[true,42,null]},"name":"orders"}},
            {"predicate":"model_has_timestamp","args":{"model":"orders","aux":[1,2]}},
            {"predicate":"metric_additive","args":{"metric":"revenue","aux":false}}
        ]),
        &context,
    )
    .unwrap();
}