use std::path::PathBuf;
use edifact_mapper::{DataDir, Mapper};
use mig_bo4e::PidValidationError;
use serde_json::Value;
type Dynamic = mig_bo4e::model::Interchange<Value, Value>;
const FV: &str = "FV2604";
const VARIANT: &str = "UTILMD_Strom";
const PID: &str = "55043";
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(|p| p.parent())
.expect("crate is two levels under the workspace root")
.to_path_buf()
}
fn load() -> Option<(Mapper, Value)> {
let root = repo_root();
let fixture = root.join("fixtures/generated/fv2604/utilmd/55043.edi");
if !root.join(format!("dist/edifact-data-{FV}.bin")).exists() || !fixture.exists() {
eprintln!("skipping: regenerate the {FV} bundle + fixture first");
return None;
}
let mapper =
Mapper::from_data_dir(DataDir::path(root.join("dist")).eager(&[FV])).expect("load bundle");
let edifact = std::fs::read_to_string(&fixture).expect("read fixture");
let ic: Dynamic = mapper
.from_edifact(&edifact, FV, VARIANT, PID)
.expect("convert fixture");
let raw = ic.nachrichten[0].transaktionen[0].clone();
let mut tx = raw["stammdaten"].clone();
mig_bo4e::model::restore_entity(
&mut tx,
mig_bo4e::model::TX_METADATA_ENTITY,
raw.get("transaktionsdaten").unwrap_or(&Value::Null),
);
Some((mapper, tx))
}
fn validate(mapper: &Mapper, tx: &Value) -> Vec<PidValidationError> {
mapper
.validate_pid_with_conditions(tx, FV, VARIANT, PID)
.unwrap()
}
fn mentions(errors: &[PidValidationError], entity: &str, field: Option<&str>) -> Vec<String> {
errors
.iter()
.filter(|e| match (e, field) {
(PidValidationError::MissingEntity { entity: en, .. }, None) => en == entity,
(
PidValidationError::MissingField {
entity: en,
field: f,
..
},
Some(want),
) => en == entity && f == want,
_ => false,
})
.map(|e| e.to_string())
.collect()
}
#[test]
fn an_absent_soll_166_group_is_not_missing() {
let Some((mapper, mut tx)) = load() else {
return;
};
let obj = tx.as_object_mut().unwrap();
obj.remove("marktlokationProduktDatenZ59");
obj.remove("tranche");
let errors = validate(&mapper, &tx);
for entity in ["MarktlokationProduktDatenZ59", "Tranche"] {
let found = mentions(&errors, entity, None);
assert!(found.is_empty(), "{entity}: {found:?}");
}
}
#[test]
fn an_absent_ruhende_marktlokation_demands_none_of_its_fields() {
let Some((mapper, mut tx)) = load() else {
return;
};
let malo = tx["marktlokation"].as_object_mut().expect("Marktlokation");
assert!(malo.contains_key("marktlokationsId"), "{malo:?}");
malo.retain(|k, _| !k.starts_with("ruhendeMarktlokation"));
let errors = validate(&mapper, &tx);
let found = mentions(&errors, "Marktlokation", Some("ruhendeMarktlokationsId"));
assert!(found.is_empty(), "{found:?}");
}
#[test]
fn a_filled_ruhende_marktlokation_still_demands_its_id() {
let Some((mapper, mut tx)) = load() else {
return;
};
let malo = tx["marktlokation"].as_object_mut().expect("Marktlokation");
malo.remove("ruhendeMarktlokationsId");
malo.insert("ruhendeMarktlokationZeitraumId".into(), "1".into());
let errors = validate(&mapper, &tx);
let found = mentions(&errors, "Marktlokation", Some("ruhendeMarktlokationsId"));
assert_eq!(found.len(), 1, "{errors:#?}");
}