lemma-engine 0.8.10

A language that means business.
Documentation
use crate::evaluation::response::{EvaluatedRule, Response, RuleResult};
use crate::planning::semantics::{
    Expression, ExpressionKind, LiteralValue, RulePath, Source, Span,
};
use crate::OperationResult;
use indexmap::IndexMap;
use rust_decimal::Decimal;
use std::str::FromStr;

fn dummy_source() -> Source {
    Source::new(
        "test.lemma",
        Span {
            start: 0,
            end: 0,
            line: 1,
            col: 1,
        },
    )
}

fn dummy_rule(name: &str) -> EvaluatedRule {
    EvaluatedRule {
        name: name.to_string(),
        path: RulePath::new(vec![], name.to_string()),
        default_expression: Expression::new(
            ExpressionKind::Literal(Box::new(LiteralValue::from_bool(true))),
            dummy_source(),
        ),
        unless_branches: vec![],
        source_location: dummy_source(),
        rule_type: crate::planning::semantics::primitive_boolean().clone(),
    }
}

#[test]
fn test_response_serialization() {
    let mut results = IndexMap::new();
    results.insert(
        "test_rule".to_string(),
        RuleResult {
            rule: dummy_rule("test_rule"),
            result: OperationResult::Value(Box::new(LiteralValue::number(
                Decimal::from_str("42").unwrap(),
            ))),
            facts: vec![],
            operations: vec![],
            explanation: None,
            rule_type: crate::planning::semantics::primitive_number().clone(),
        },
    );
    let response = Response {
        spec_name: "test_spec".to_string(),
        spec_hash: None,
        spec_effective_from: None,
        spec_effective_to: None,
        facts: vec![],
        results,
    };

    let json = serde_json::to_string(&response).unwrap();
    let deserialized: serde_json::Value = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized["spec_name"], "test_spec");
    assert!(deserialized["results"]
        .as_object()
        .unwrap()
        .contains_key("test_rule"));
    assert_eq!(
        deserialized["results"]["test_rule"]["result"]["value"]["display_value"],
        "42"
    );
}

#[test]
fn test_response_filter_rules() {
    let mut results = IndexMap::new();
    results.insert(
        "rule1".to_string(),
        RuleResult {
            rule: dummy_rule("rule1"),
            result: OperationResult::Value(Box::new(LiteralValue::from_bool(true))),
            facts: vec![],
            operations: vec![],
            explanation: None,
            rule_type: crate::planning::semantics::primitive_boolean().clone(),
        },
    );
    results.insert(
        "rule2".to_string(),
        RuleResult {
            rule: dummy_rule("rule2"),
            result: OperationResult::Value(Box::new(LiteralValue::from_bool(false))),
            facts: vec![],
            operations: vec![],
            explanation: None,
            rule_type: crate::planning::semantics::primitive_boolean().clone(),
        },
    );
    let mut response = Response {
        spec_name: "test_spec".to_string(),
        spec_hash: None,
        spec_effective_from: None,
        spec_effective_to: None,
        facts: vec![],
        results,
    };

    response.filter_rules(&["rule1".to_string()]);

    assert_eq!(response.results.len(), 1);
    assert_eq!(response.results.values().next().unwrap().rule.name, "rule1");
}

#[test]
fn test_rule_result_types() {
    let success = RuleResult {
        rule: dummy_rule("rule1"),
        result: OperationResult::Value(Box::new(LiteralValue::from_bool(true))),
        facts: vec![],
        operations: vec![],
        explanation: None,
        rule_type: crate::planning::semantics::primitive_boolean().clone(),
    };
    assert!(matches!(success.result, OperationResult::Value(_)));

    let missing = RuleResult {
        rule: dummy_rule("rule3"),
        result: OperationResult::Veto(Some("Missing fact: fact1".to_string())),
        facts: vec![crate::planning::semantics::Fact {
            path: crate::planning::semantics::FactPath::new(vec![], "fact1".to_string()),
            value: crate::planning::semantics::FactValue::Literal(
                crate::planning::semantics::LiteralValue::from_bool(false),
            ),
            source: None,
        }],
        operations: vec![],
        explanation: None,
        rule_type: crate::planning::LemmaType::veto_type(),
    };
    assert_eq!(missing.facts.len(), 1);
    assert_eq!(missing.facts[0].path.fact, "fact1");
    assert!(matches!(missing.result, OperationResult::Veto(_)));

    let veto = RuleResult {
        rule: dummy_rule("rule4"),
        result: OperationResult::Veto(Some("Vetoed".to_string())),
        facts: vec![],
        operations: vec![],
        explanation: None,
        rule_type: crate::planning::LemmaType::veto_type(),
    };
    assert_eq!(
        veto.result,
        OperationResult::Veto(Some("Vetoed".to_string()))
    );
}