use super::*;
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn a_fully_populated_battery_serialises_into_the_current_schema() {
use crate::identifier::Gtin;
use crate::product_group::{
BatteryChemistry, BatteryData, BatteryStatus, BatteryType, CarbonFootprintClass,
CriticalRawMaterial, DynamicPerformance, EnvironmentalReading, ExpectedLifetime,
HarmfulEvents, HazardSymbol, HazardousSubstance, MaterialComposition, StateOfChargeReading,
StateOfHealth, TemperatureRange, UsageHistory,
};
use chrono::{NaiveDate, TimeZone as _, Utc};
let range = TemperatureRange {
min_c: -20.0,
max_c: 60.0,
};
let moment = Utc.with_ymd_and_hms(2026, 3, 1, 12, 0, 0).unwrap();
let day = NaiveDate::from_ymd_opt(2026, 3, 1).unwrap();
let data = BatteryData {
gtin: Gtin::parse("09506000134352").expect("valid GTIN literal"),
battery_chemistry: BatteryChemistry::Lfp,
nominal_voltage_v: 3.2,
nominal_capacity_ah: 100.0,
expected_lifetime_cycles: Some(3_000),
co2e_per_unit_kg: 85.4,
recycled_content_cobalt_pct: Some(16.0),
recycled_content_lithium_pct: Some(6.0),
recycled_content_nickel_pct: Some(6.0),
state_of_health_pct: Some(97.0),
rated_capacity_kwh: Some(64.0),
carbon_footprint_class: Some(CarbonFootprintClass::new("B").expect("valid label")),
carbon_footprint_class_ruleset_id: Some("eu-battery-cfb".into()),
carbon_footprint_class_ruleset_version: Some("2026.1".into()),
due_diligence_url: Some("https://example.invalid/due-diligence".into()),
cathode_material: Some(vec![MaterialComposition {
name: "Lithium iron phosphate".into(),
weight_pct: 32.0,
cas_number: Some("15365-14-7".into()),
}]),
anode_material: Some(vec![MaterialComposition {
name: "Graphite".into(),
weight_pct: 18.0,
cas_number: Some("7782-42-5".into()),
}]),
electrolyte_material: Some(vec![MaterialComposition {
name: "Lithium hexafluorophosphate".into(),
weight_pct: 11.0,
cas_number: Some("21324-40-3".into()),
}]),
critical_raw_materials: Some(vec![CriticalRawMaterial {
name: "Natural graphite".into(),
cas_number: Some("7782-42-5".into()),
weight_grams: Some(8_500.0),
country_of_origin: Some("MZ".into()),
}]),
disassembly_instructions_url: Some("https://example.invalid/disassembly".into()),
soh_methodology: Some("IEC 62660-1:2018".into()),
operating_temp_min_c: Some(-20.0),
operating_temp_max_c: Some(60.0),
rated_energy_wh: Some(64_000.0),
recycled_content_lead_pct: Some(0.0),
battery_weight_kg: Some(384.0),
battery_type: BatteryType::Ev,
initial_round_trip_efficiency_pct: Some(96.0),
round_trip_efficiency_at_half_cycle_life_pct: Some(91.0),
round_trip_efficiency_pct: Some(96.0),
internal_resistance_mohm: Some(1.4),
internal_cell_resistance_mohm: Some(1.4),
internal_pack_resistance_mohm: Some(38.0),
placed_on_market_date: Some(day),
manufacturing_date: Some(moment),
manufacturing_place: Some("PL:Wrocław".into()),
battery_model_id: Some("LFP-64-A".into()),
battery_passport_number: Some("URN:UUID:6F1C9D2E-0000-4000-8000-000000000000".into()),
expected_lifetime: Some(Box::new(ExpectedLifetime {
put_into_service_date: Some(day),
energy_throughput_kwh: 128_000.0,
capacity_throughput_ah: 400_000.0,
harmful_events: HarmfulEvents {
deep_discharge_events: Some(2),
hours_in_extreme_temperature: Some(14.5),
hours_charging_in_extreme_temperature: Some(1.5),
},
full_equivalent_cycles: 2_000.0,
})),
recycled_content_reporting_year: Some(2026),
state_of_health: Some(Box::new(StateOfHealth::ElectricVehicle { soce_pct: 97.0 })),
hazardous_substances: Some(vec![HazardousSubstance {
name: "Nickel sulfate".into(),
cas_number: Some("7786-81-4".into()),
concentration_pct: Some(0.4),
}]),
usable_extinguishing_agent: Some("Class D dry powder".into()),
renewable_content_pct: Some(12.5),
minimal_voltage_v: Some(2.5),
maximum_voltage_v: Some(4.2),
voltage_temperature_range: Some(range),
original_power_capability_w: Some(150_000.0),
power_limit_min_w: Some(1_000.0),
power_limit_max_w: Some(180_000.0),
power_temperature_range: Some(range),
expected_lifetime_reference_test: Some("IEC 62660-1:2018".into()),
capacity_threshold_for_exhaustion_pct: Some(80.0),
not_in_use_temperature_range: Some(range),
not_in_use_temperature_reference_test: Some("IEC 62660-1:2018 clause 7".into()),
commercial_warranty_period_months: Some(96),
cycle_life_test_c_rate: Some(1.0),
marking_information: Some("Separate collection symbol applied".into()),
hazard_symbol: Some(HazardSymbol::Cadmium),
eu_declaration_of_conformity: Some("DoC-2027-0001".into()),
waste_battery_information: Some("https://example.invalid/waste".into()),
component_part_numbers: Some(vec!["MOD-A1".into(), "BMS-C7".into()]),
spare_parts_contacts: Some("spares@example.invalid".into()),
safety_measures: Some("Isolate at the service disconnect before handling.".into()),
test_report_results: Some("https://example.invalid/test-report".into()),
dynamic_performance: Some(Box::new(DynamicPerformance {
rated_capacity_ah: Some(98.0),
capacity_fade_pct: Some(2.0),
power_w: Some(148_000.0),
power_fade_pct: Some(1.3),
internal_resistance_mohm: Some(1.5),
internal_resistance_increase_pct: Some(7.1),
round_trip_efficiency_pct: Some(94.0),
round_trip_efficiency_fade_pct: Some(2.1),
expected_lifetime_cycles: Some(2_900),
expected_lifetime_years: Some(11.5),
})),
battery_status: Some(BatteryStatus::Original),
usage_history: Some(Box::new(UsageHistory {
charge_discharge_cycles: Some(412),
negative_events: Some(vec!["over-temperature 2026-02-11".into()]),
operating_conditions: Some(vec![EnvironmentalReading {
recorded_at: moment,
temperature_c: Some(31.5),
note: Some("fast charge".into()),
}]),
state_of_charge: Some(vec![StateOfChargeReading {
recorded_at: moment,
state_of_charge_pct: 78.0,
}]),
})),
};
let registry = VersionedSchemaRegistry::new();
let (version, _) = registry.latest("battery").expect("battery schema exists");
let json = serde_json::to_value(&data).expect("serialises");
registry
.validate("battery", version, &json)
.expect("every emitted field must be admitted by the current schema");
}
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn a_fully_populated_textile_serialises_into_the_current_schema() {
use crate::identifier::Gtin;
use crate::product_group::{FibreEntry, SvhcSubstance, TextileData};
let data = TextileData {
gtin: Gtin::parse("09506000134352").expect("valid GTIN literal"),
fibre_composition: vec![
FibreEntry {
fibre: "cotton".into(),
pct: 70.0,
country_of_origin: Some("PT".into()),
},
FibreEntry {
fibre: "polyester".into(),
pct: 30.0,
country_of_origin: Some("TR".into()),
},
],
country_of_origin: "PT".into(),
care_instructions: "Machine wash 30°C".into(),
chemical_compliance_standard: "OEKO-TEX Standard 100".into(),
recycled_content_pct: Some(24.0),
carbon_footprint_kg_co2e: Some(8.5),
water_use_litres: Some(2_400.0),
microplastic_shedding_mg_per_wash: Some(1.7),
repair_score: Some(6.5),
durability_score: Some(7.5),
expected_wash_cycles: Some(50),
country_of_raw_material_origin: Some("IN".into()),
svhc_substances: Some(vec![SvhcSubstance {
cas_number: "80-05-7".into(),
substance_name: "Bisphenol A".into(),
concentration_pct: 0.15,
location_in_product: Some("coating".into()),
scip_notification_id: Some("SCIP-0001".into()),
}]),
allergens: Some(vec!["disperse blue 1".into()]),
substances_of_concern: Some(vec!["PFAS".into()]),
recyclability_class: Some("B".into()),
end_of_life_instructions: Some("Separate trims before fibre recovery".into()),
reuse_condition: Some("good".into()),
prior_use_cycles: Some(1),
disassembly_instructions: Some("Remove buttons, separate layers by colour".into()),
spare_parts_available: Some(true),
product_weight_grams: Some(420.0),
repair_history_url: Some("https://example.invalid/repairs".into()),
repair_count: Some(2),
pef_score: Some(0.42),
};
let registry = VersionedSchemaRegistry::new();
let (version, _) = registry.latest("textile").expect("textile schema exists");
let json = serde_json::to_value(&data).expect("serialises");
registry
.validate("textile", version, &json)
.expect("every emitted field must be admitted by the current schema");
}