automapper-validation 0.2.0

AHB condition expression parsing, evaluation, and EDIFACT validation
Documentation
//! EBD-aware cluster conditions: [359] / [360] / [366] / [368].
//!
//! These conditions classify STS+E01 response codes by the cluster
//! assigned in the referenced EBD (C556/1131). The cluster comes from
//! the `EbdClusterLookup` data extracted from `mako_prozesse`.

use automapper_validation::eval::{
    ConditionEvaluator, ConditionResult, EvaluationContext, MapExternalProvider,
};
use automapper_validation::generated::fv2504::UtilmdStromConditionEvaluatorFV2504;
use automapper_validation::generated::fv2510::UtilmdStromConditionEvaluatorFV2510;
use mig_types::segment::OwnedSegment;

/// Build an STS segment of the form `STS+<cat>+<status>+<code>:<ebd>`.
/// Empty string on status becomes the empty second element; pass
/// `None` to the ebd to produce `STS+<cat>+<status>+<code>`.
fn sts(category: &str, status: &str, code: &str, ebd: Option<&str>) -> OwnedSegment {
    let c556 = match ebd {
        Some(e) => vec![code.to_string(), e.to_string()],
        None => vec![code.to_string()],
    };
    OwnedSegment {
        id: "STS".to_string(),
        segment_number: 0,
        elements: vec![vec![category.to_string()], vec![status.to_string()], c556],
    }
}

#[test]
fn evaluate_360_true_when_a36_in_e0624_is_zustimmung_fv2504() {
    // User's real message: STS+E01++A36:E_0624 — A36 is Zustimmung per
    // EBD E_0624. The old hardcoded allowlist (A01-A17, A25) wrongly
    // rejected this. The EBD-data-driven evaluator must accept it.
    let segments = vec![sts("E01", "", "A36", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2504::default();
    assert_eq!(eval.evaluate(360, &ctx), ConditionResult::True);
}

#[test]
fn evaluate_360_true_when_a36_in_e0624_is_zustimmung_fv2510() {
    let segments = vec![sts("E01", "", "A36", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(360, &ctx), ConditionResult::True);
}

#[test]
fn evaluate_360_false_when_a35_in_e0624_is_ablehnung() {
    // A35 in E_0624 is Cluster: Ablehnung (Vertragsbindung besteht).
    let segments = vec![sts("E01", "", "A35", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(360, &ctx), ConditionResult::False);
}

#[test]
fn evaluate_360_unknown_when_no_sts_e01() {
    let segments = vec![];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(360, &ctx), ConditionResult::Unknown);
}

#[test]
fn evaluate_359_true_when_a35_in_e0624_is_ablehnung() {
    // Inverse of 360: [359] requires Ablehnung cluster only.
    let segments = vec![sts("E01", "", "A35", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(359, &ctx), ConditionResult::True);
}

#[test]
fn evaluate_359_false_when_a36_in_e0624_is_zustimmung() {
    let segments = vec![sts("E01", "", "A36", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(359, &ctx), ConditionResult::False);
}

#[test]
fn evaluate_366_true_for_ablehnung_codes_except_a30() {
    // [366]: "Bis auf den Code A30 sind alle Codes aus EBD E_0624 im
    // Cluster Ablehnung erlaubt." A35 is Ablehnung and ≠ A30 → True.
    let segments = vec![sts("E01", "", "A35", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(366, &ctx), ConditionResult::True);
}

#[test]
fn evaluate_366_false_when_code_is_a30() {
    // A30 is Ablehnung in E_0624 but explicitly excluded by [366].
    let segments = vec![sts("E01", "", "A30", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(366, &ctx), ConditionResult::False);
}

#[test]
fn evaluate_368_false_when_code_is_a41() {
    // A41 is Ablehnung in E_0624 but explicitly excluded by [368].
    let segments = vec![sts("E01", "", "A41", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(368, &ctx), ConditionResult::False);
}

#[test]
fn evaluate_368_true_for_ablehnung_codes_except_a41() {
    let segments = vec![sts("E01", "", "A35", Some("E_0624"))];
    let external = MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new("55011", &external, &segments);
    let eval = UtilmdStromConditionEvaluatorFV2510::default();
    assert_eq!(eval.evaluate(368, &ctx), ConditionResult::True);
}