use edi_energy::{MessageType, Pruefidentifikator, Release};
#[test]
fn release_new_and_as_str() {
let r = Release::new("5.5.3a");
assert_eq!(r.as_str(), "5.5.3a");
}
#[test]
fn release_display() {
let r = Release::new("5.2e");
assert_eq!(r.to_string(), "5.2e");
}
#[test]
fn release_parse_via_fromstr() {
let r: Release = "S4.0".parse().expect("fromstr should never fail");
assert_eq!(r.as_str(), "S4.0");
}
#[test]
fn release_eq_and_hash() {
let a = Release::new("5.5.3a");
let b = Release::new("5.5.3a");
assert_eq!(a, b);
let mut set = std::collections::HashSet::new();
set.insert(a.clone());
set.insert(b.clone());
assert_eq!(set.len(), 1);
}
#[test]
fn release_ord() {
let older = Release::new("5.5.3a");
let newer = Release::new("5.5.4b");
assert!(older < newer, "5.5.3a < 5.5.4b (same Dotted track)");
let short_old = Release::new("2.9a");
let short_new = Release::new("2.10a");
assert!(
short_old < short_new,
"2.9a < 2.10a (numeric, not lexicographic)"
);
let strom = Release::new("S2.1");
let dotted = Release::new("5.5.3a");
assert_eq!(
strom.partial_cmp(&dotted),
None,
"S2.1 and 5.5.3a are on different tracks — incomparable"
);
}
#[test]
fn release_clone() {
let r = Release::new("5.5.3a");
let r2 = r.clone();
assert_eq!(r, r2);
}
#[test]
fn release_asref() {
let r = Release::new("5.5.3a");
let s: &str = r.as_ref();
assert_eq!(s, "5.5.3a");
}
#[test]
fn pruefidentifikator_valid_range() {
assert!(Pruefidentifikator::new(10000).is_ok());
assert!(Pruefidentifikator::new(11001).is_ok());
assert!(Pruefidentifikator::new(99999).is_ok());
}
#[test]
fn pruefidentifikator_too_small() {
assert!(Pruefidentifikator::new(9999).is_err());
assert!(Pruefidentifikator::new(0).is_err());
}
#[test]
fn pruefidentifikator_too_large() {
assert!(Pruefidentifikator::new(100_000).is_err());
}
#[test]
fn pruefidentifikator_display_zero_padded() {
let p = Pruefidentifikator::new(11001).unwrap();
assert_eq!(p.to_string(), "11001");
let min = Pruefidentifikator::new(10000).unwrap();
assert_eq!(min.to_string(), "10000");
}
#[test]
fn pruefidentifikator_as_u32() {
let p = Pruefidentifikator::new(25001).unwrap();
assert_eq!(p.as_u32(), 25001);
}
#[test]
fn pruefidentifikator_parse_via_fromstr() {
let p: Pruefidentifikator = "11001".parse().unwrap();
assert_eq!(p.as_u32(), 11001);
}
#[test]
fn pruefidentifikator_fromstr_invalid() {
assert!("99".parse::<Pruefidentifikator>().is_err());
assert!("abc".parse::<Pruefidentifikator>().is_err());
assert!("100000".parse::<Pruefidentifikator>().is_err());
}
#[test]
fn pruefidentifikator_eq() {
let a = Pruefidentifikator::new(11001).unwrap();
let b = Pruefidentifikator::new(11001).unwrap();
let c = Pruefidentifikator::new(11002).unwrap();
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn message_type_as_str_round_trip() {
for (variant, code) in [
(MessageType::Utilmd, "UTILMD"),
(MessageType::Mscons, "MSCONS"),
(MessageType::Aperak, "APERAK"),
(MessageType::Contrl, "CONTRL"),
(MessageType::Invoic, "INVOIC"),
(MessageType::Remadv, "REMADV"),
(MessageType::Orders, "ORDERS"),
(MessageType::Iftsta, "IFTSTA"),
(MessageType::Insrpt, "INSRPT"),
(MessageType::Reqote, "REQOTE"),
(MessageType::Partin, "PARTIN"),
] {
assert_eq!(variant.as_str(), code, "as_str mismatch for {variant}");
assert_eq!(
MessageType::from_unh_code(code),
Some(variant),
"from_unh_code mismatch for {code}"
);
}
}
#[test]
fn message_type_from_unh_code_unknown() {
assert_eq!(MessageType::from_unh_code("FOOBAR"), None);
assert_eq!(MessageType::from_unh_code(""), None);
}
#[test]
fn message_type_display() {
assert_eq!(MessageType::Utilmd.to_string(), "UTILMD");
assert_eq!(MessageType::Mscons.to_string(), "MSCONS");
}
#[test]
fn message_type_eq_hash() {
use std::collections::HashSet;
let set: HashSet<MessageType> = [
MessageType::Utilmd,
MessageType::Utilmd,
MessageType::Mscons,
]
.into_iter()
.collect();
assert_eq!(set.len(), 2);
}
#[cfg(feature = "mscons")]
#[test]
fn process_context_selects_active_mscons_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let early = ProcessContext::for_date(date!(2024 - 03 - 31));
assert!(
early.active_release(MessageType::Mscons).is_none(),
"no MSCONS profile should be active before 01.04.2024"
);
#[cfg(any(feature = "mscons-archive", feature = "archive"))]
{
let era_fv20240401 = ProcessContext::for_date(date!(2025 - 06 - 01));
let rel_fv20240401 = era_fv20240401
.active_release(MessageType::Mscons)
.expect("MSCONS 2.4c must be active on 2025-06-01");
assert_eq!(rel_fv20240401.as_str(), "2.4c");
let prof_fv20240401 = era_fv20240401
.active_profile(MessageType::Mscons)
.expect("fv20240401 profile must be returned");
assert_eq!(prof_fv20240401.valid_from(), Some(date!(2024 - 04 - 01)));
}
let era_fv20251001 = ProcessContext::for_date(date!(2026 - 06 - 07));
let rel_fv20251001 = era_fv20251001
.active_release(MessageType::Mscons)
.expect("MSCONS 2.4c fv20251001 must be active on 2026-06-07");
assert_eq!(rel_fv20251001.as_str(), "2.4c");
let prof_fv20251001 = era_fv20251001
.active_profile(MessageType::Mscons)
.expect("fv20251001 profile must be returned");
assert_eq!(prof_fv20251001.valid_from(), Some(date!(2025 - 10 - 01)));
let era_fv20261001 = ProcessContext::for_date(date!(2026 - 10 - 01));
let rel_fv20261001 = era_fv20261001
.active_release(MessageType::Mscons)
.expect("MSCONS 2.5 must be active on 2026-10-01");
assert_eq!(rel_fv20261001.as_str(), "2.5");
let prof_fv20261001 = era_fv20261001
.active_profile(MessageType::Mscons)
.expect("fv20261001 profile must be returned");
assert_eq!(prof_fv20261001.valid_from(), Some(date!(2026 - 10 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_release = future
.active_release(MessageType::Mscons)
.expect("some MSCONS profile must be active");
assert_eq!(
future_release.as_str(),
"2.5",
"latest registered is still 2.5"
);
}
#[test]
fn process_context_skips_undated_profiles() {
use edi_energy::ProcessContext;
use time::macros::date;
let ctx = ProcessContext::for_date(date!(2099 - 12 - 31));
let _ = ctx.active_profile(MessageType::Utilmd);
}
#[cfg(all(
feature = "mscons",
any(feature = "mscons-archive", feature = "archive")
))]
#[test]
fn profile_valid_from_matches_fv_date() {
use edi_energy::registry::ReleaseRegistry;
use time::macros::date;
let reg = ReleaseRegistry::global();
let p = reg
.profiles_for(MessageType::Mscons)
.find(|p| p.valid_from() == Some(date!(2024 - 04 - 01)))
.expect("fv20240401 must be registered with valid_from = 2024-04-01");
assert_eq!(p.release().as_str(), "2.4c");
}
#[cfg(feature = "mscons")]
#[test]
fn mscons_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Mscons)
.find(|p| p.valid_from() == Some(target_date))
.expect("MSCONS fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "2.4c");
assert_eq!(releases::mscons_fv20251001().as_str(), "2.4c");
}
#[cfg(feature = "mscons")]
#[test]
fn profile_valid_from_fv20261001() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
let reg = ReleaseRegistry::global();
let p = reg
.profile_on(
MessageType::Mscons,
&Release::new("2.5"),
time::macros::date!(2026 - 10 - 01),
)
.expect("2.5 profile must be registered");
let vf = p
.valid_from()
.expect("fv20261001 must have a valid_from date");
assert_eq!(vf, time::macros::date!(2026 - 10 - 01));
assert_eq!(releases::mscons_fv20261001().as_str(), "2.5");
}
#[cfg(feature = "utilmd")]
#[test]
fn utilmd_s21_valid_from_is_2024_10_01() {
use edi_energy::registry::ReleaseRegistry;
use time::macros::date;
let reg = ReleaseRegistry::global();
let p = reg
.profile_on(
MessageType::Utilmd,
&Release::new("S1.1a"),
date!(2024 - 10 - 01),
)
.expect("UTILMD S1.1a profile must be registered in fv20241001");
let vf = p
.valid_from()
.expect("fv20241001 must have a valid_from date");
assert_eq!(vf, date!(2024 - 10 - 01));
}
#[cfg(feature = "utilmd")]
#[test]
fn utilmd_s22_valid_from_is_2026_10_01() {
use edi_energy::registry::ReleaseRegistry;
use time::macros::date;
let reg = ReleaseRegistry::global();
let p = reg
.profile_on(
MessageType::Utilmd,
&Release::new("S2.2"),
date!(2026 - 10 - 01),
)
.expect("UTILMD S2.2 profile must be registered");
let vf = p
.valid_from()
.expect("fv20261001 must have a valid_from date");
assert_eq!(vf, date!(2026 - 10 - 01));
}
#[cfg(feature = "utilmd")]
#[test]
fn process_context_selects_active_utilmd_strom_release() {
use edi_energy::{ProcessContext, ReleaseTrack};
use time::macros::date;
let before = ProcessContext::for_date(date!(2024 - 09 - 30));
assert!(
before
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.is_none(),
"no Strom UTILMD profile active before 2024-10-01"
);
let s11a_era = ProcessContext::for_date(date!(2025 - 01 - 01));
let s11a_rel = s11a_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("S1.1a must be active on 2025-01-01");
assert_eq!(s11a_rel.as_str(), "S1.1a");
let s12_era = ProcessContext::for_date(date!(2025 - 06 - 06));
let s12_rel = s12_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("S1.2 must be active on 2025-06-06");
assert_eq!(s12_rel.as_str(), "S1.2");
let s21_era = ProcessContext::for_date(date!(2025 - 10 - 01));
let s21_rel = s21_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("S2.1 must be active on 2025-10-01");
assert_eq!(s21_rel.as_str(), "S2.1");
let last_day_s21 = ProcessContext::for_date(date!(2026 - 09 - 30));
let last_rel = last_day_s21
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("S2.1 must be active on 2026-09-30");
assert_eq!(
last_rel.as_str(),
"S2.1",
"S2.1 must still be active the day before S2.2"
);
let s22_era = ProcessContext::for_date(date!(2026 - 10 - 01));
let s22_rel = s22_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("S2.2 must be active on 2026-10-01");
assert_eq!(s22_rel.as_str(), "S2.2");
let far_future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_rel = far_future
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("some Strom UTILMD profile must be active");
assert_eq!(
future_rel.as_str(),
"S2.2",
"S2.2 must be the latest Strom UTILMD profile"
);
}
#[cfg(feature = "utilmd")]
#[test]
fn process_context_selects_active_utilmd_gas_release() {
use edi_energy::{ProcessContext, ReleaseTrack};
use time::macros::date;
let before = ProcessContext::for_date(date!(2024 - 09 - 30));
assert!(
before
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Gas)
.is_none(),
"no Gas UTILMD profile active before 2024-10-01"
);
let g10a_era = ProcessContext::for_date(date!(2025 - 06 - 01));
let g10a_rel = g10a_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Gas)
.expect("G1.0a must be active on 2025-06-01");
assert_eq!(g10a_rel.as_str(), "G1.0a");
let g11_era = ProcessContext::for_date(date!(2025 - 10 - 01));
let g11_rel = g11_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Gas)
.expect("G1.1 must be active on 2025-10-01");
assert_eq!(g11_rel.as_str(), "G1.1");
let g12_era = ProcessContext::for_date(date!(2026 - 10 - 01));
let g12_rel = g12_era
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Gas)
.expect("G1.2 must be active on 2026-10-01");
assert_eq!(g12_rel.as_str(), "G1.2");
}
#[cfg(feature = "utilmd")]
#[test]
fn utilmd_gas_and_strom_tracks_are_independent() {
use edi_energy::{ProcessContext, ReleaseTrack};
use time::macros::date;
let ctx = ProcessContext::for_date(date!(2025 - 01 - 01));
let strom = ctx
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Strom)
.expect("Strom S2.1 must be active");
let gas = ctx
.active_release_for_track(MessageType::Utilmd, &ReleaseTrack::Gas)
.expect("Gas G1.1 must be active");
assert!(
strom.as_str().starts_with('S'),
"Strom track must start with S"
);
assert!(gas.as_str().starts_with('G'), "Gas track must start with G");
assert_ne!(strom.as_str(), gas.as_str());
}
#[cfg(feature = "aperak")]
#[test]
fn aperak_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let p = reg
.profile(MessageType::Aperak, &Release::new("2.1i"))
.expect("APERAK 2.1i profile must be registered");
let vf = p
.valid_from()
.expect("fv20251001 must have a valid_from date");
assert_eq!(vf, date!(2025 - 10 - 01));
assert_eq!(releases::aperak_fv20251001().as_str(), "2.1i");
}
#[cfg(feature = "aperak")]
#[test]
fn aperak_fv20261001_valid_from_is_2026_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let p = reg
.profile_on(
MessageType::Aperak,
&Release::new("2.2"),
date!(2026 - 10 - 01),
)
.expect("APERAK 2.2 profile must be registered");
let vf = p
.valid_from()
.expect("fv20261001 must have a valid_from date");
assert_eq!(vf, date!(2026 - 10 - 01));
assert_eq!(releases::aperak_fv20261001().as_str(), "2.2");
}
#[cfg(feature = "aperak")]
#[test]
fn process_context_selects_active_aperak_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
let before_rel = before.active_release(MessageType::Aperak);
assert!(
before_rel.is_none() || before_rel.map(edi_energy::Release::as_str) == Some("2.0a"),
"no fv-dated APERAK profile active before 2025-10-01 (only legacy 2.0a if any)"
);
let era_21i = ProcessContext::for_date(date!(2026 - 05 - 01));
let rel_21i = era_21i
.active_release(MessageType::Aperak)
.expect("APERAK 2.1i must be active on 2026-05-01");
assert_eq!(rel_21i.as_str(), "2.1i");
let era_22 = ProcessContext::for_date(date!(2026 - 10 - 01));
let rel_22 = era_22
.active_release(MessageType::Aperak)
.expect("APERAK 2.2 must be active on 2026-10-01");
assert_eq!(rel_22.as_str(), "2.2");
}
#[cfg(all(
feature = "contrl",
any(feature = "contrl-archive", feature = "archive")
))]
#[test]
fn contrl_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Contrl)
.find(|p| p.valid_from() == Some(target_date))
.expect("CONTRL fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "2.0b");
assert_eq!(releases::contrl_fv20251001().as_str(), "2.0b");
}
#[cfg(feature = "contrl")]
#[test]
fn contrl_fv20260101_valid_from_is_2026_01_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 01 - 01);
let profile = reg
.profiles_for(MessageType::Contrl)
.find(|p| p.valid_from() == Some(target_date))
.expect("CONTRL fv20260101 must be registered with valid_from = 2026-01-01");
assert_eq!(profile.release().as_str(), "2.0b");
assert_eq!(releases::contrl_fv20260101().as_str(), "2.0b");
}
#[cfg(feature = "contrl")]
#[test]
fn process_context_selects_active_contrl_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Contrl).is_none(),
"no fv-dated CONTRL profile active before 2025-10-01"
);
#[cfg(any(feature = "contrl-archive", feature = "archive"))]
{
let era_fv20251001 = ProcessContext::for_date(date!(2025 - 12 - 31));
let rel_251001 = era_fv20251001
.active_release(MessageType::Contrl)
.expect("CONTRL 2.0b fv20251001 must be active on 2025-12-31");
assert_eq!(rel_251001.as_str(), "2.0b");
let profile_251001 = era_fv20251001
.active_profile(MessageType::Contrl)
.expect("profile must be returned");
assert_eq!(profile_251001.valid_from(), Some(date!(2025 - 10 - 01)));
}
let era_fv20260101 = ProcessContext::for_date(date!(2026 - 01 - 01));
let rel_260101 = era_fv20260101
.active_release(MessageType::Contrl)
.expect("CONTRL 2.0b fv20260101 must be active on 2026-01-01");
assert_eq!(rel_260101.as_str(), "2.0b");
let profile_260101 = era_fv20260101
.active_profile(MessageType::Contrl)
.expect("profile must be returned");
assert_eq!(profile_260101.valid_from(), Some(date!(2026 - 01 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Contrl)
.expect("some CONTRL profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 01 - 01)));
}
#[cfg(feature = "iftsta")]
#[test]
fn iftsta_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Iftsta)
.find(|p| p.valid_from() == Some(target_date))
.expect("IFTSTA fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "2.0g");
assert_eq!(releases::iftsta_fv20251001().as_str(), "2.0g");
}
#[cfg(feature = "iftsta")]
#[test]
fn iftsta_fv20261001_valid_from_is_2026_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Iftsta)
.find(|p| p.valid_from() == Some(target_date))
.expect("IFTSTA fv20261001 must be registered with valid_from = 2026-10-01");
assert_eq!(profile.release().as_str(), "2.1");
assert_eq!(releases::iftsta_fv20261001().as_str(), "2.1");
}
#[cfg(feature = "iftsta")]
#[test]
fn process_context_selects_active_iftsta_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Iftsta).is_none(),
"no fv-dated IFTSTA profile active before 2025-10-01"
);
let era_fv20251001 = ProcessContext::for_date(date!(2026 - 05 - 01));
let rel_fv20251001 = era_fv20251001
.active_release(MessageType::Iftsta)
.expect("IFTSTA 2.0g fv20251001 must be active on 2026-05-01");
assert_eq!(rel_fv20251001.as_str(), "2.0g");
let profile_251001 = era_fv20251001
.active_profile(MessageType::Iftsta)
.expect("profile must be returned");
assert_eq!(profile_251001.valid_from(), Some(date!(2025 - 10 - 01)));
let era_fv20261001 = ProcessContext::for_date(date!(2026 - 10 - 01));
let rel_fv20261001 = era_fv20261001
.active_release(MessageType::Iftsta)
.expect("IFTSTA 2.1 fv20261001 must be active on 2026-10-01");
assert_eq!(rel_fv20261001.as_str(), "2.1");
let profile_261001 = era_fv20261001
.active_profile(MessageType::Iftsta)
.expect("profile must be returned");
assert_eq!(profile_261001.valid_from(), Some(date!(2026 - 10 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Iftsta)
.expect("some IFTSTA profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 10 - 01)));
}
#[cfg(all(
feature = "insrpt",
any(feature = "insrpt-archive", feature = "archive")
))]
#[test]
fn insrpt_fv20211001_valid_from_is_2021_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2021 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Insrpt)
.find(|p| p.valid_from() == Some(target_date))
.expect("INSRPT fv20211001 must be registered with valid_from = 2021-10-01");
assert_eq!(profile.release().as_str(), "1.1a");
assert_eq!(releases::insrpt_fv20211001().as_str(), "1.1a");
}
#[cfg(feature = "insrpt")]
#[test]
fn insrpt_fv20260101_valid_from_is_2026_01_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 01 - 01);
let profile = reg
.profiles_for(MessageType::Insrpt)
.find(|p| p.valid_from() == Some(target_date))
.expect("INSRPT fv20260101 must be registered with valid_from = 2026-01-01");
assert_eq!(profile.release().as_str(), "1.1a");
assert_eq!(releases::insrpt_fv20260101().as_str(), "1.1a");
}
#[cfg(feature = "insrpt")]
#[test]
fn process_context_selects_active_insrpt_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2021 - 09 - 30));
assert!(
before.active_release(MessageType::Insrpt).is_none(),
"no fv-dated INSRPT profile active before 2021-10-01"
);
#[cfg(any(feature = "insrpt-archive", feature = "archive"))]
{
let era_fv20211001 = ProcessContext::for_date(date!(2025 - 06 - 01));
let rel_fv20211001 = era_fv20211001
.active_release(MessageType::Insrpt)
.expect("INSRPT 1.1a fv20211001 must be active on 2025-06-01");
assert_eq!(rel_fv20211001.as_str(), "1.1a");
let profile_211001 = era_fv20211001
.active_profile(MessageType::Insrpt)
.expect("profile must be returned");
assert_eq!(profile_211001.valid_from(), Some(date!(2021 - 10 - 01)));
}
let era_fv20260101 = ProcessContext::for_date(date!(2026 - 01 - 01));
let rel_fv20260101 = era_fv20260101
.active_release(MessageType::Insrpt)
.expect("INSRPT 1.1a fv20260101 must be active on 2026-01-01");
assert_eq!(rel_fv20260101.as_str(), "1.1a");
let profile_260101 = era_fv20260101
.active_profile(MessageType::Insrpt)
.expect("profile must be returned");
assert_eq!(profile_260101.valid_from(), Some(date!(2026 - 01 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Insrpt)
.expect("some INSRPT profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 01 - 01)));
}
#[cfg(feature = "invoic")]
#[test]
fn invoic_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Invoic)
.find(|p| p.valid_from() == Some(target_date))
.expect("INVOIC fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "2.8e");
assert_eq!(releases::invoic_fv20251001().as_str(), "2.8e");
}
#[cfg(feature = "invoic")]
#[test]
fn invoic_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Invoic)
.find(|p| p.valid_from() == Some(target_date))
.expect("INVOIC fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "2.8e");
assert_eq!(releases::invoic_fv20260401().as_str(), "2.8e");
}
#[cfg(feature = "invoic")]
#[test]
fn process_context_selects_active_invoic_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Invoic).is_none(),
"no fv-dated INVOIC profile active before 2025-10-01"
);
let era_fv20251001 = ProcessContext::for_date(date!(2026 - 01 - 01));
let rel_fv20251001 = era_fv20251001
.active_release(MessageType::Invoic)
.expect("INVOIC 2.8e fv20251001 must be active on 2026-01-01");
assert_eq!(rel_fv20251001.as_str(), "2.8e");
let profile_251001 = era_fv20251001
.active_profile(MessageType::Invoic)
.expect("profile must be returned");
assert_eq!(profile_251001.valid_from(), Some(date!(2025 - 10 - 01)));
let era_fv20260401 = ProcessContext::for_date(date!(2026 - 04 - 01));
let rel_fv20260401 = era_fv20260401
.active_release(MessageType::Invoic)
.expect("INVOIC 2.8e fv20260401 must be active on 2026-04-01");
assert_eq!(rel_fv20260401.as_str(), "2.8e");
let profile_260401 = era_fv20260401
.active_profile(MessageType::Invoic)
.expect("profile must be returned");
assert_eq!(profile_260401.valid_from(), Some(date!(2026 - 04 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Invoic)
.expect("some INVOIC profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 04 - 01)));
}
#[cfg(feature = "orders")]
#[test]
fn orders_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Orders)
.find(|p| p.valid_from() == Some(target_date))
.expect("ORDERS fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "1.4b");
assert_eq!(releases::orders_fv20251001().as_str(), "1.4b");
}
#[cfg(feature = "orders")]
#[test]
fn orders_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Orders)
.find(|p| p.valid_from() == Some(target_date))
.expect("ORDERS fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.4c");
assert_eq!(releases::orders_fv20260401().as_str(), "1.4c");
}
#[cfg(feature = "orders")]
#[test]
fn process_context_selects_active_orders_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Orders).is_none(),
"no fv-dated ORDERS profile active before 2025-10-01"
);
let era_fv20251001 = ProcessContext::for_date(date!(2026 - 01 - 01));
let rel_fv20251001 = era_fv20251001
.active_release(MessageType::Orders)
.expect("ORDERS 1.4b fv20251001 must be active on 2026-01-01");
assert_eq!(rel_fv20251001.as_str(), "1.4b");
let profile_251001 = era_fv20251001
.active_profile(MessageType::Orders)
.expect("profile must be returned");
assert_eq!(profile_251001.valid_from(), Some(date!(2025 - 10 - 01)));
let era_fv20260401 = ProcessContext::for_date(date!(2026 - 04 - 01));
let rel_fv20260401 = era_fv20260401
.active_release(MessageType::Orders)
.expect("ORDERS 1.4c fv20260401 must be active on 2026-04-01");
assert_eq!(rel_fv20260401.as_str(), "1.4c");
let profile_260401 = era_fv20260401
.active_profile(MessageType::Orders)
.expect("profile must be returned");
assert_eq!(profile_260401.valid_from(), Some(date!(2026 - 04 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Orders)
.expect("some ORDERS profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 04 - 01)));
assert_eq!(future_profile.release().as_str(), "1.4c");
}
#[cfg(feature = "partin")]
#[test]
fn partin_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Partin)
.find(|p| p.valid_from() == Some(target_date))
.expect("PARTIN fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "1.0f");
assert_eq!(releases::partin_fv20251001().as_str(), "1.0f");
}
#[cfg(feature = "partin")]
#[test]
fn partin_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Partin)
.find(|p| p.valid_from() == Some(target_date))
.expect("PARTIN fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.1");
assert_eq!(releases::partin_fv20260401().as_str(), "1.1");
}
#[cfg(feature = "partin")]
#[test]
fn process_context_selects_active_partin_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Partin).is_none(),
"no fv-dated PARTIN profile active before 2025-10-01"
);
let era_fv20251001 = ProcessContext::for_date(date!(2026 - 01 - 01));
let rel_fv20251001 = era_fv20251001
.active_release(MessageType::Partin)
.expect("PARTIN 1.0f fv20251001 must be active on 2026-01-01");
assert_eq!(rel_fv20251001.as_str(), "1.0f");
let profile_251001 = era_fv20251001
.active_profile(MessageType::Partin)
.expect("profile must be returned");
assert_eq!(profile_251001.valid_from(), Some(date!(2025 - 10 - 01)));
let era_fv20260401 = ProcessContext::for_date(date!(2026 - 04 - 01));
let rel_fv20260401 = era_fv20260401
.active_release(MessageType::Partin)
.expect("PARTIN 1.1 fv20260401 must be active on 2026-04-01");
assert_eq!(rel_fv20260401.as_str(), "1.1");
let profile_260401 = era_fv20260401
.active_profile(MessageType::Partin)
.expect("profile must be returned");
assert_eq!(profile_260401.valid_from(), Some(date!(2026 - 04 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Partin)
.expect("some PARTIN profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 04 - 01)));
assert_eq!(future_profile.release().as_str(), "1.1");
}
#[cfg(feature = "reqote")]
#[test]
fn reqote_fv20250401_valid_from_is_2025_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Reqote)
.find(|p| p.valid_from() == Some(target_date))
.expect("REQOTE fv20250401 must be registered with valid_from = 2025-04-01");
assert_eq!(profile.release().as_str(), "1.3c");
assert_eq!(releases::reqote_fv20250401().as_str(), "1.3c");
}
#[cfg(feature = "reqote")]
#[test]
fn reqote_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Reqote)
.find(|p| p.valid_from() == Some(target_date))
.expect("REQOTE fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.3c");
assert_eq!(releases::reqote_fv20260401().as_str(), "1.3c");
}
#[cfg(feature = "reqote")]
#[test]
fn process_context_selects_active_reqote_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 03 - 31));
assert!(
before.active_release(MessageType::Reqote).is_none(),
"no fv-dated REQOTE profile active before 2025-04-01"
);
let era_fv20250401 = ProcessContext::for_date(date!(2025 - 06 - 01));
let rel_fv20250401 = era_fv20250401
.active_release(MessageType::Reqote)
.expect("REQOTE 1.3c fv20250401 must be active on 2025-06-01");
assert_eq!(rel_fv20250401.as_str(), "1.3c");
let profile_250401 = era_fv20250401
.active_profile(MessageType::Reqote)
.expect("profile must be returned");
assert_eq!(profile_250401.valid_from(), Some(date!(2025 - 04 - 01)));
let era_fv20260401 = ProcessContext::for_date(date!(2026 - 04 - 01));
let rel_fv20260401 = era_fv20260401
.active_release(MessageType::Reqote)
.expect("REQOTE 1.3c fv20260401 must be active on 2026-04-01");
assert_eq!(rel_fv20260401.as_str(), "1.3c");
let profile_260401 = era_fv20260401
.active_profile(MessageType::Reqote)
.expect("profile must be returned");
assert_eq!(profile_260401.valid_from(), Some(date!(2026 - 04 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Reqote)
.expect("some REQOTE profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 04 - 01)));
assert_eq!(future_profile.release().as_str(), "1.3c");
}
#[cfg(feature = "remadv")]
#[test]
fn remadv_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Remadv)
.find(|p| p.valid_from() == Some(target_date))
.expect("REMADV fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "2.9e");
assert_eq!(releases::remadv_fv20251001().as_str(), "2.9e");
}
#[cfg(feature = "remadv")]
#[test]
fn process_context_selects_active_remadv_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Remadv).is_none(),
"no fv-dated REMADV profile active before 2025-10-01"
);
let era_fv20251001 = ProcessContext::for_date(date!(2026 - 01 - 01));
let rel = era_fv20251001
.active_release(MessageType::Remadv)
.expect("REMADV 2.9e fv20251001 must be active on 2026-01-01");
assert_eq!(rel.as_str(), "2.9e");
let profile = era_fv20251001
.active_profile(MessageType::Remadv)
.expect("profile must be returned");
assert_eq!(profile.valid_from(), Some(date!(2025 - 10 - 01)));
let future = ProcessContext::for_date(date!(2030 - 01 - 01));
let future_profile = future
.active_profile(MessageType::Remadv)
.expect("some REMADV profile must be active");
assert_eq!(future_profile.valid_from(), Some(date!(2026 - 04 - 01)));
assert_eq!(future_profile.release().as_str(), "2.9f");
}
#[cfg(feature = "ordchg")]
#[test]
fn ordchg_fv20241001_valid_from_is_2024_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2024 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Ordchg)
.find(|p| p.valid_from() == Some(target_date))
.expect("ORDCHG fv20241001 must be registered with valid_from = 2024-10-01");
assert_eq!(profile.release().as_str(), "1.1");
assert_eq!(releases::ordchg_fv20241001().as_str(), "1.1");
}
#[cfg(feature = "ordchg")]
#[test]
fn ordchg_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Ordchg)
.find(|p| p.valid_from() == Some(target_date))
.expect("ORDCHG fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.2");
assert_eq!(releases::ordchg_fv20260401().as_str(), "1.2");
}
#[cfg(feature = "ordchg")]
#[test]
fn process_context_selects_active_ordchg_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2024 - 09 - 30));
assert!(
before.active_release(MessageType::Ordchg).is_none(),
"no fv-dated ORDCHG profile before 2024-10-01"
);
for &d in &[
date!(2024 - 10 - 01),
date!(2025 - 06 - 01),
date!(2026 - 03 - 31),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Ordchg)
.unwrap_or_else(|| panic!("ORDCHG must be active on {d}"));
assert_eq!(rel.as_str(), "1.1", "expected 1.1 on {d}");
assert_eq!(
ctx.active_profile(MessageType::Ordchg)
.unwrap()
.valid_from(),
Some(date!(2024 - 10 - 01))
);
}
for &d in &[
date!(2026 - 04 - 01),
date!(2026 - 07 - 01),
date!(2030 - 01 - 01),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Ordchg)
.unwrap_or_else(|| panic!("ORDCHG must be active on {d}"));
assert_eq!(rel.as_str(), "1.2", "expected 1.2 on {d}");
assert_eq!(
ctx.active_profile(MessageType::Ordchg)
.unwrap()
.valid_from(),
Some(date!(2026 - 04 - 01))
);
}
}
#[cfg(feature = "ordrsp")]
#[test]
fn ordrsp_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Ordrsp)
.find(|p| p.valid_from() == Some(target_date))
.expect("ORDRSP fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "1.4b");
assert_eq!(releases::ordrsp_fv20251001().as_str(), "1.4b");
}
#[cfg(feature = "ordrsp")]
#[test]
fn ordrsp_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Ordrsp)
.find(|p| p.valid_from() == Some(target_date))
.expect("ORDRSP fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.4c");
assert_eq!(releases::ordrsp_fv20260401().as_str(), "1.4c");
}
#[cfg(feature = "ordrsp")]
#[test]
fn process_context_selects_active_ordrsp_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Ordrsp).is_none(),
"no fv-dated ORDRSP profile before 2025-10-01"
);
for &d in &[
date!(2025 - 10 - 01),
date!(2026 - 01 - 01),
date!(2026 - 03 - 31),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Ordrsp)
.unwrap_or_else(|| panic!("ORDRSP must be active on {d}"));
assert_eq!(rel.as_str(), "1.4b", "expected 1.4b on {d}");
assert_eq!(
ctx.active_profile(MessageType::Ordrsp)
.unwrap()
.valid_from(),
Some(date!(2025 - 10 - 01))
);
}
for &d in &[
date!(2026 - 04 - 01),
date!(2026 - 07 - 01),
date!(2030 - 01 - 01),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Ordrsp)
.unwrap_or_else(|| panic!("ORDRSP must be active on {d}"));
assert_eq!(rel.as_str(), "1.4c", "expected 1.4c on {d}");
assert_eq!(
ctx.active_profile(MessageType::Ordrsp)
.unwrap()
.valid_from(),
Some(date!(2026 - 04 - 01))
);
}
}
#[cfg(feature = "quotes")]
#[test]
fn quotes_fv20250401_valid_from_is_2025_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Quotes)
.find(|p| p.valid_from() == Some(target_date))
.expect("QUOTES fv20250401 must be registered with valid_from = 2025-04-01");
assert_eq!(profile.release().as_str(), "1.3b");
assert_eq!(releases::quotes_fv20250401().as_str(), "1.3b");
}
#[cfg(feature = "quotes")]
#[test]
fn quotes_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Quotes)
.find(|p| p.valid_from() == Some(target_date))
.expect("QUOTES fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.3c");
assert_eq!(releases::quotes_fv20260401().as_str(), "1.3c");
}
#[cfg(feature = "quotes")]
#[test]
fn process_context_selects_active_quotes_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 03 - 31));
assert!(
before.active_release(MessageType::Quotes).is_none(),
"no fv-dated QUOTES profile before 2025-04-01"
);
for &d in &[
date!(2025 - 04 - 01),
date!(2025 - 07 - 01),
date!(2026 - 03 - 31),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Quotes)
.unwrap_or_else(|| panic!("QUOTES must be active on {d}"));
assert_eq!(rel.as_str(), "1.3b", "expected 1.3b on {d}");
assert_eq!(
ctx.active_profile(MessageType::Quotes)
.unwrap()
.valid_from(),
Some(date!(2025 - 04 - 01))
);
}
for &d in &[
date!(2026 - 04 - 01),
date!(2026 - 07 - 01),
date!(2030 - 01 - 01),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Quotes)
.unwrap_or_else(|| panic!("QUOTES must be active on {d}"));
assert_eq!(rel.as_str(), "1.3c", "expected 1.3c on {d}");
assert_eq!(
ctx.active_profile(MessageType::Quotes)
.unwrap()
.valid_from(),
Some(date!(2026 - 04 - 01))
);
}
}
#[cfg(feature = "comdis")]
#[test]
fn comdis_fv20251001_valid_from_is_2025_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Comdis)
.find(|p| p.valid_from() == Some(target_date))
.expect("COMDIS fv20251001 must be registered with valid_from = 2025-10-01");
assert_eq!(profile.release().as_str(), "1.0g");
assert_eq!(releases::comdis_fv20251001().as_str(), "1.0g");
}
#[cfg(feature = "comdis")]
#[test]
fn comdis_fv20261001_valid_from_is_2026_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Comdis)
.find(|p| p.valid_from() == Some(target_date))
.expect("COMDIS fv20261001 must be registered with valid_from = 2026-10-01");
assert_eq!(profile.release().as_str(), "1.0g");
assert_eq!(releases::comdis_fv20261001().as_str(), "1.0g");
}
#[cfg(feature = "comdis")]
#[test]
fn process_context_selects_active_comdis_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 09 - 30));
assert!(
before.active_release(MessageType::Comdis).is_none(),
"no fv-dated COMDIS profile before 2025-10-01"
);
for &d in &[
date!(2025 - 10 - 01),
date!(2026 - 01 - 01),
date!(2026 - 09 - 30),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Comdis)
.unwrap_or_else(|| panic!("COMDIS must be active on {d}"));
assert_eq!(rel.as_str(), "1.0g", "expected 1.0g on {d}");
assert_eq!(
ctx.active_profile(MessageType::Comdis)
.unwrap()
.valid_from(),
Some(date!(2025 - 10 - 01))
);
}
for &d in &[
date!(2026 - 10 - 01),
date!(2027 - 01 - 01),
date!(2030 - 01 - 01),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Comdis)
.unwrap_or_else(|| panic!("COMDIS must be active on {d}"));
assert_eq!(rel.as_str(), "1.0g", "expected 1.0g on {d}");
assert_eq!(
ctx.active_profile(MessageType::Comdis)
.unwrap()
.valid_from(),
Some(date!(2026 - 10 - 01))
);
}
}
#[cfg(feature = "pricat")]
#[test]
fn pricat_fv20250401_valid_from_is_2025_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2025 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Pricat)
.find(|p| p.valid_from() == Some(target_date))
.expect("PRICAT fv20250401 must be registered with valid_from = 2025-04-01");
assert_eq!(profile.release().as_str(), "2.0e");
assert_eq!(releases::pricat_fv20250401().as_str(), "2.0e");
}
#[cfg(feature = "pricat")]
#[test]
fn pricat_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Pricat)
.find(|p| p.valid_from() == Some(target_date))
.expect("PRICAT fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "2.1");
assert_eq!(releases::pricat_fv20260401().as_str(), "2.1");
}
#[cfg(feature = "pricat")]
#[test]
fn process_context_selects_active_pricat_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2025 - 03 - 31));
assert!(
before.active_release(MessageType::Pricat).is_none(),
"no fv-dated PRICAT profile before 2025-04-01"
);
for &d in &[
date!(2025 - 04 - 01),
date!(2025 - 07 - 01),
date!(2026 - 03 - 31),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Pricat)
.unwrap_or_else(|| panic!("PRICAT must be active on {d}"));
assert_eq!(rel.as_str(), "2.0e", "expected 2.0e on {d}");
assert_eq!(
ctx.active_profile(MessageType::Pricat)
.unwrap()
.valid_from(),
Some(date!(2025 - 04 - 01))
);
}
for &d in &[
date!(2026 - 04 - 01),
date!(2027 - 01 - 01),
date!(2030 - 01 - 01),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Pricat)
.unwrap_or_else(|| panic!("PRICAT must be active on {d}"));
assert_eq!(rel.as_str(), "2.1", "expected 2.1 on {d}");
assert_eq!(
ctx.active_profile(MessageType::Pricat)
.unwrap()
.valid_from(),
Some(date!(2026 - 04 - 01))
);
}
}
#[cfg(feature = "utilts")]
#[test]
fn utilts_fv20241001_valid_from_is_2024_10_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2024 - 10 - 01);
let profile = reg
.profiles_for(MessageType::Utilts)
.find(|p| p.valid_from() == Some(target_date))
.expect("UTILTS fv20241001 must be registered with valid_from = 2024-10-01");
assert_eq!(profile.release().as_str(), "1.1e");
assert_eq!(releases::utilts_fv20241001().as_str(), "1.1e");
}
#[cfg(feature = "utilts")]
#[test]
fn utilts_fv20260401_valid_from_is_2026_04_01() {
use edi_energy::registry::ReleaseRegistry;
use edi_energy::releases;
use time::macros::date;
let reg = ReleaseRegistry::global();
let target_date = date!(2026 - 04 - 01);
let profile = reg
.profiles_for(MessageType::Utilts)
.find(|p| p.valid_from() == Some(target_date))
.expect("UTILTS fv20260401 must be registered with valid_from = 2026-04-01");
assert_eq!(profile.release().as_str(), "1.1e");
assert_eq!(releases::utilts_fv20260401().as_str(), "1.1e");
}
#[cfg(feature = "utilts")]
#[test]
fn process_context_selects_active_utilts_release() {
use edi_energy::ProcessContext;
use time::macros::date;
let before = ProcessContext::for_date(date!(2024 - 09 - 30));
assert!(
before.active_release(MessageType::Utilts).is_none(),
"no fv-dated UTILTS profile before 2024-10-01"
);
for &d in &[
date!(2024 - 10 - 01),
date!(2025 - 04 - 01),
date!(2026 - 03 - 31),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Utilts)
.unwrap_or_else(|| panic!("UTILTS must be active on {d}"));
assert_eq!(rel.as_str(), "1.1e", "expected wire 1.1e on {d}");
assert_eq!(
ctx.active_profile(MessageType::Utilts)
.unwrap()
.valid_from(),
Some(date!(2024 - 10 - 01)),
"expected fv20241001 profile on {d}"
);
}
for &d in &[
date!(2026 - 04 - 01),
date!(2027 - 01 - 01),
date!(2030 - 01 - 01),
] {
let ctx = ProcessContext::for_date(d);
let rel = ctx
.active_release(MessageType::Utilts)
.unwrap_or_else(|| panic!("UTILTS must be active on {d}"));
assert_eq!(rel.as_str(), "1.1e", "expected wire 1.1e on {d}");
assert_eq!(
ctx.active_profile(MessageType::Utilts)
.unwrap()
.valid_from(),
Some(date!(2026 - 04 - 01)),
"expected fv20260401 profile on {d}"
);
}
}
#[cfg(all(feature = "serde", feature = "mscons"))]
#[test]
fn report_serializes_to_json() {
use edi_energy::{EdiEnergyMessage, parse};
const BARE: &[u8] = b"UNA:+.? 'UNH+1+MSCONS:D:04B:UN:2.4c'UNT+1+1'";
let msg = parse(BARE).expect("bare MSCONS must parse");
let report = msg.validate().expect("validate must not return an error");
let json = serde_json::to_string(&report).expect("report must serialize to JSON");
assert!(json.contains("\"valid\""), "JSON must contain 'valid' key");
assert!(
json.contains("\"errors\""),
"JSON must contain 'errors' key"
);
assert!(
json.contains("\"warnings\""),
"JSON must contain 'warnings' key"
);
assert!(json.contains("\"infos\""), "JSON must contain 'infos' key");
assert!(
json.contains("\"totalIssues\""),
"JSON must contain 'totalIssues' key"
);
}