use std::path::PathBuf;
use edifact_mapper::{DataDir, Mapper, TransactionView};
use serde_json::{json, Value};
const TYPES: [&str; 7] = [
"orders", "ordrsp", "reqote", "quotes", "mscons", "utilmd", "iftsta",
];
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"))
}
#[test]
fn every_sample_reads_as_views_and_back() {
for fv in ["FV2504", "FV2510", "FV2604", "FV2610"] {
let Some(m) = mapper(fv) else {
continue;
};
let mut checked = 0;
for kind in TYPES {
let dir = root().join(format!("fixtures/generated/{}/{kind}", fv.to_lowercase()));
let Ok(entries) = std::fs::read_dir(&dir) else {
continue;
};
let mut files: Vec<PathBuf> = entries.flatten().map(|e| e.path()).collect();
files.sort();
for file in files {
let stem = file.file_stem().unwrap().to_string_lossy().to_string();
let pid = stem.split("__").next().unwrap().to_string();
let edi = std::fs::read_to_string(&file).unwrap();
let variants: Vec<String> = if kind == "utilmd" {
vec!["UTILMD_Strom".into(), "UTILMD_Gas".into()]
} else {
vec![kind.to_uppercase()]
};
let Some((variant, interchange)) = variants.iter().find_map(|v| {
m.from_edifact::<Value, Value>(&edi, fv, v, &pid)
.ok()
.map(|i| (v.clone(), i))
}) else {
continue;
};
for nachricht in &interchange.nachrichten {
let views = m
.transaction_views(nachricht)
.unwrap_or_else(|e| panic!("{fv} {stem}: {e}"));
assert_eq!(
views.len(),
nachricht.transaktionen.len().max(1),
"{fv} {stem}"
);
assert_eq!(
views.iter().any(|v| v.synthetic),
nachricht.transaktionen.is_empty(),
"{fv} {stem}"
);
let back = m
.from_transaction_views(
&views,
&nachricht.nachrichtendaten,
fv,
&variant,
&pid,
)
.unwrap_or_else(|e| panic!("{fv} {stem}: {e}"));
assert_eq!(
serde_json::to_value(&back).unwrap(),
serde_json::to_value(nachricht).unwrap(),
"{fv} {stem}: the views give the message back"
);
assert_eq!(
m.to_edifact_nachricht(&back, fv, &variant, &pid).unwrap(),
m.to_edifact_nachricht(nachricht, fv, &variant, &pid)
.unwrap(),
"{fv} {stem}: rendered alike"
);
checked += 1;
}
}
}
assert!(checked > 100, "{fv}: only {checked} messages");
}
}
#[test]
fn message_level_content_is_in_every_view() {
let Some(m) = mapper("FV2610") else {
return;
};
let read = |kind: &str, pid: &str| {
let edi = std::fs::read_to_string(
root().join(format!("fixtures/generated/fv2610/{kind}/{pid}.edi")),
)
.unwrap();
m.from_edifact::<Value, Value>(&edi, "FV2610", &kind.to_uppercase(), pid)
.unwrap()
.nachrichten
.remove(0)
};
let orders = read("orders", "17009");
let views = m.transaction_views(&orders).unwrap();
let view = &views[0].value;
assert!(!views[0].synthetic);
let dp = view["stammdaten"]["marktteilnehmer"]
.as_array()
.unwrap()
.iter()
.find(|p| p["meldepunktId"].is_string());
assert!(dp.is_some(), "{view:#}");
assert!(view["stammdaten"]["position"].is_object(), "{view:#}");
assert!(view["nachricht"]["auftragsdatum"].is_string(), "{view:#}");
assert!(view["nachricht"].get("unhReferenz").is_none(), "{view:#}");
let ordrsp = read("ordrsp", "19015");
let views = m.transaction_views(&ordrsp).unwrap();
assert_eq!(views.len(), 1);
assert!(views[0].synthetic);
assert!(views[0].value["transaktionsdaten"].is_null());
assert!(views[0].value["stammdaten"]["antwortkategorie"].is_object());
assert!(views[0].value["stammdaten"]["referenz"].is_array());
}
#[test]
fn inconsistent_views_are_refused() {
let Some(m) = mapper("FV2610") else {
return;
};
let edi =
std::fs::read_to_string(root().join("fixtures/generated/fv2610/ordrsp/19015.edi")).unwrap();
let nachricht = m
.from_edifact::<Value, Value>(&edi, "FV2610", "ORDRSP", "19015")
.unwrap()
.nachrichten
.remove(0);
let views = m.transaction_views(&nachricht).unwrap();
let kopf = &nachricht.nachrichtendaten;
let back = |views: &[TransactionView]| {
m.from_transaction_views(views, kopf, "FV2610", "ORDRSP", "19015")
.unwrap_err()
.to_string()
};
let mut other = views[0].clone();
other.synthetic = false;
other.value["stammdaten"]["antwortkategorie"]["antwortCode"] = json!("ANDERS");
let mut two = views.clone();
two[0].synthetic = false;
two.push(other);
assert!(back(&two).contains("antwortkategorie"), "{}", back(&two));
let mut synthetic = views.clone();
synthetic[0].value["stammdaten"]["position"] = json!({"positionsnummer": "1"});
assert!(
back(&synthetic).contains("position"),
"{}",
back(&synthetic)
);
assert!(back(&[]).contains("no view"));
}