edifact-mapper 0.8.0

EDIFACT to BO4E bidirectional conversion for the German energy market
Documentation
//! Integration test for PID validation via Mapper API.

use edifact_mapper::{DataDir, Mapper};
use serde_json::json;

fn skip_if_no_bundles() -> Option<Mapper> {
    let bundle_dir = std::path::Path::new("/tmp/edifact-bundles");
    if !bundle_dir.join("edifact-data-FV2504.bin").exists() {
        eprintln!(
            "Skipping: run `cargo run -p automapper-generator -- bundle-data \
             --output-dir /tmp/edifact-bundles` first"
        );
        return None;
    }
    match Mapper::from_data_dir(DataDir::path(bundle_dir).eager(&["FV2504"])) {
        Ok(m) => Some(m),
        Err(e) => {
            eprintln!("Skipping: bundle load failed (possibly incompatible version): {e}");
            None
        }
    }
}

#[test]
fn test_validate_empty_returns_errors() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let json = json!({});
    let errors = mapper
        .validate_pid(&json, "FV2504", "UTILMD_Strom", "55001")
        .unwrap();
    assert!(
        !errors.is_empty(),
        "Empty object should have validation errors"
    );
    assert!(errors
        .iter()
        .any(|e| matches!(e, mig_bo4e::PidValidationError::MissingEntity { .. })));
}

#[test]
fn test_validate_provided_entities_not_missing() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let json = json!({
        "prozessdaten": {
            "pruefidentifikator": "55001",
            "transaktionsnummer": "55001",
            "vorgangId": "V001",
            "transaktionsgrund": "E01",
            "transaktionsgrundErgaenzung": "E01",
            "transaktionsgrundErgaenzungBefristeteAnmeldung": "E01",
            "gueltigAb": "20260101"
        },
        "marktlokation": {
            "marktlokationsId": "51238696781"
        }
    });
    let errors = mapper
        .validate_pid(&json, "FV2504", "UTILMD_Strom", "55001")
        .unwrap();

    // Provided entities should not be missing
    assert!(
        !errors.iter().any(|e| matches!(
            e,
            mig_bo4e::PidValidationError::MissingEntity { entity, .. }
            if entity == "Prozessdaten" || entity == "Marktlokation"
        )),
        "Provided entities should not be missing. Errors: {:?}",
        errors
    );
}

#[test]
fn test_validate_missing_required_field() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    // Provide Ansprechpartner entity but omit required "nachname" field (ahb_status=X)
    let json = json!({
        "ansprechpartner": {
            "vorname": "Max"
        }
    });
    let errors = mapper
        .validate_pid(&json, "FV2504", "UTILMD_Strom", "55001")
        .unwrap();
    assert!(
        errors.iter().any(|e| matches!(
            e,
            mig_bo4e::PidValidationError::MissingField { entity, field, .. }
            if entity == "Ansprechpartner" && field == "nachname"
        )),
        "Should report missing nachname field. Errors: {:?}",
        errors
    );
}

#[test]
fn test_validate_invalid_code() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let json = json!({
        "ansprechpartner": {
            "anredeCode": "INVALID"
        }
    });
    let errors = mapper
        .validate_pid(&json, "FV2504", "UTILMD_Strom", "55001")
        .unwrap();
    assert!(
        errors.iter().any(|e| matches!(
            e,
            mig_bo4e::PidValidationError::InvalidCode { entity, field, .. }
            if entity == "Ansprechpartner" && field == "anredeCode"
        )),
        "Should report invalid anredeCode. Errors: {:?}",
        errors
    );
}

// ── Condition-aware validation tests ────────────────────────────────────

#[test]
fn test_validate_with_conditions_produces_results() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };

    let json = json!({
        "prozessdaten": {
            "pruefidentifikator": "55001",
            "transaktionsnummer": "55001",
            "vorgangId": "V001",
            "transaktionsgrund": "E01",
            "transaktionsgrundErgaenzung": "ZW4",
            "transaktionsgrundErgaenzungBefristeteAnmeldung": "E01"
        }
    });

    // Basic validation (no conditions) reports errors
    let basic_errors = mapper
        .validate_pid(&json, "FV2504", "UTILMD_Strom", "55001")
        .unwrap();

    // Condition-aware validation should also produce results (not crash)
    let cond_errors = mapper
        .validate_pid_with_conditions(&json, "FV2504", "UTILMD_Strom", "55001")
        .unwrap();

    // Both should have errors (incomplete data)
    assert!(
        !basic_errors.is_empty(),
        "Basic validation should report errors for incomplete data"
    );
    assert!(
        !cond_errors.is_empty(),
        "Condition-aware validation should report errors for incomplete data"
    );

    // Condition-aware may have fewer errors (some conditional fields evaluate to False)
    // or more (some Unknown conditions become warnings). The key test is it doesn't crash.
    eprintln!("Basic errors: {}", basic_errors.len());
    eprintln!("Condition-aware errors: {}", cond_errors.len());
}

#[test]
fn test_validate_with_conditions_empty_json() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let json = json!({});
    let result = mapper.validate_pid_with_conditions(&json, "FV2504", "UTILMD_Strom", "55001");
    assert!(result.is_ok());
    let errors = result.unwrap();
    assert!(
        !errors.is_empty(),
        "Empty JSON should produce validation errors"
    );
}

#[test]
fn test_validate_with_conditions_wrong_variant() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let result = mapper.validate_pid_with_conditions(&json!({}), "FV2504", "NONEXISTENT", "55001");
    assert!(result.is_err());
}

#[test]
fn test_validate_with_conditions_wrong_pid() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let result = mapper.validate_pid_with_conditions(&json!({}), "FV2504", "UTILMD_Strom", "99999");
    assert!(result.is_err());
}

#[test]
fn test_validate_wrong_variant_returns_error() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let result = mapper.validate_pid(&json!({}), "FV2504", "NONEXISTENT", "55001");
    assert!(result.is_err());
}

#[test]
fn test_validate_wrong_pid_returns_error() {
    let Some(mapper) = skip_if_no_bundles() else {
        return;
    };
    let result = mapper.validate_pid(&json!({}), "FV2504", "UTILMD_Strom", "99999");
    assert!(result.is_err());
}