use std::path::PathBuf;
use edifact_mapper::{DataDir, Mapper};
use serde_json::Value;
fn root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..")
}
fn mapper(fv: &str) -> Option<Mapper> {
if !root().join(format!("dist/edifact-data-{fv}.bin")).exists() {
eprintln!("skipping {fv}: build the bundle first");
return None;
}
Some(Mapper::from_data_dir(DataDir::path(root().join("dist")).eager(&[fv])).expect("bundle"))
}
fn bo4e_errors(m: &Mapper, fv: &str, pid: &str, edi: &str) -> Vec<String> {
let ic = m
.from_edifact::<Value, Value>(edi, fv, "UTILMD_Strom", pid)
.expect("parse");
let n = &ic.nachrichten[0];
let mut entities = n.stammdaten.clone();
let map = entities.as_object_mut().expect("stammdaten");
map.insert(
"nachricht".into(),
Value::Object(n.nachrichtendaten.nachricht.clone()),
);
let tx = &n.transaktionen[0];
for (k, v) in tx["stammdaten"].as_object().into_iter().flatten() {
map.insert(k.clone(), v.clone());
}
map.insert("prozessdaten".into(), tx["transaktionsdaten"].clone());
m.validate_pid_with_conditions(&entities, fv, "UTILMD_Strom", pid)
.expect("validate")
.iter()
.map(|e| e.to_string())
.filter(|e| e.starts_with("ERROR") || e.starts_with("INVALID"))
.collect()
}
#[test]
fn the_transaktionsgrund_is_judged_by_its_own_code() {
for fv in ["FV2510", "FV2604", "FV2610"] {
let Some(m) = mapper(fv) else {
continue;
};
let sample = root().join(format!(
"fixtures/generated/{}/utilmd/55010__ZW3.edi",
fv.to_lowercase()
));
let edi = std::fs::read_to_string(&sample).expect("sample");
assert!(edi.contains("STS+7++E03+ZW3'"), "{fv}: {edi}");
let found = bo4e_errors(&m, fv, "55010", &edi);
assert!(found.is_empty(), "{fv} E03: {found:#?}");
let e01 = edi.replace("STS+7++E03+ZW3'", "STS+7++E01+ZW3'");
let found = bo4e_errors(&m, fv, "55010", &e01);
assert!(
found
.iter()
.any(|e| e.contains("transaktionsgrund") && e.contains("[192]")),
"{fv} E01: {found:#?}"
);
}
}
fn edifact_errors(m: &Mapper, fv: &str, pid: &str, edi: &str) -> Vec<String> {
use automapper_validation::{Severity, ValidationLevel};
m.validate_edifact_for_pid(edi, fv, "UTILMD_Strom", pid, ValidationLevel::Full)
.expect("validate")
.issues
.iter()
.filter(|i| i.severity != Severity::Info)
.filter(|i| {
i.field_path
.as_deref()
.is_some_and(|p| p.ends_with("RFF/C506/1153"))
})
.map(|i| format!("{:?} {:?} {:?}", i.kind, i.field_path, i.rule))
.collect()
}
#[test]
fn the_geraetereferenz_is_judged_by_its_own_code() {
let gateway = "SEQ+Z03+1'RFF+Z14:GATEWAY00001'";
for fv in ["FV2504", "FV2510", "FV2604", "FV2610"] {
let Some(m) = mapper(fv) else {
continue;
};
for (pid, field) in [
("55643", "rffQualifierGeraet"),
("55653", "geraetereferenzQualifier"),
] {
let sample = root().join(format!(
"fixtures/generated/{}/utilmd/{pid}.edi",
fv.to_lowercase()
));
let edi = std::fs::read_to_string(&sample).expect("sample");
assert!(edi.contains("SEQ+Z20+1'RFF+MG:"), "{fv} {pid}: {edi}");
let behind = if edi.contains("SEQ+Z03+1'RFF+Z14:") {
edi.clone()
} else {
edi.replace("SEQ+Z03+1'", gateway)
};
let z14 = behind.replace("SEQ+Z20+1'RFF+MG:", "SEQ+Z20+1'RFF+Z14:");
let ctx = format!("{fv} {pid}");
let of_field = |found: Vec<String>| -> Vec<String> {
found.into_iter().filter(|e| e.contains(field)).collect()
};
let found = of_field(bo4e_errors(&m, fv, pid, &z14));
assert!(found.is_empty(), "{ctx} Z14 behind a gateway: {found:#?}");
let found = edifact_errors(&m, fv, pid, &z14);
assert!(found.is_empty(), "{ctx} Z14 (EDIFACT): {found:#?}");
let found = of_field(bo4e_errors(&m, fv, pid, &behind));
assert!(
found.iter().any(|e| e.contains("[204]")),
"{ctx} MG behind a gateway: {found:#?}"
);
assert!(
!edifact_errors(&m, fv, pid, &behind).is_empty(),
"{ctx} MG behind a gateway (EDIFACT)"
);
let plain = behind.replace(gateway, "SEQ+Z03+1'");
let plain = match plain.find("SEQ+Z03+1'RFF+Z14:") {
Some(at) => {
let rff = at + "SEQ+Z03+1'".len();
let end = rff + plain[rff..].find('\'').expect("RFF") + 1;
format!("{}{}", &plain[..rff], &plain[end..])
}
None => plain,
};
assert!(!plain.contains("SEQ+Z03+1'RFF+Z14:"), "{ctx}: {plain}");
let found = of_field(bo4e_errors(&m, fv, pid, &plain));
assert!(found.is_empty(), "{ctx} MG without a gateway: {found:#?}");
let found = edifact_errors(&m, fv, pid, &plain);
assert!(found.is_empty(), "{ctx} MG (EDIFACT): {found:#?}");
}
}
}