use std::borrow::Cow;
use super::tests::make_passport;
use super::{CarrierQualifier, Passport};
use crate::Granularity;
use crate::error::dpp::DppError;
fn at(level: Option<Granularity>) -> Passport {
let mut p = make_passport();
p.granularity = level;
p.batch_id = None;
p.serial_number = None;
p
}
fn fields_refused(p: &Passport) -> Vec<(String, String)> {
match p.validate() {
Ok(()) => Vec::new(),
Err(DppError::Validation(errors)) => errors
.errors
.into_iter()
.map(|e| (e.field, e.message))
.collect(),
Err(other) => panic!("expected a validation error, got {other:?}"),
}
}
#[test]
fn a_model_level_carrier_prints_no_serial() {
let p = at(Some(Granularity::Model));
assert_eq!(p.carrier_qualifier(), Some(CarrierQualifier::Model));
assert_eq!(CarrierQualifier::Model.ai_element(), None);
}
#[test]
fn a_batch_level_carrier_prints_its_lot() {
let mut p = at(Some(Granularity::Batch));
p.batch_id = Some("LOT-2026-A".into());
let qualifier = p.carrier_qualifier().expect("a batch to print");
assert_eq!(
qualifier,
CarrierQualifier::Batch(Cow::Borrowed("LOT-2026-A"))
);
assert_eq!(qualifier.ai_element(), Some(("10", "LOT-2026-A")));
}
#[test]
fn an_item_or_unstated_carrier_prints_the_carrier_serial() {
for level in [Some(Granularity::Item), None] {
let mut p = at(level);
p.batch_id = Some("LOT-2026-A".into());
p.serial_number = Some("SN-0001".into());
let qualifier = p.carrier_qualifier().expect("a serial to print");
assert_eq!(
qualifier.ai_element(),
Some(("21", &*p.effective_carrier_serial())),
"{level:?}"
);
}
}
#[test]
fn a_batch_level_passport_without_a_lot_has_no_carrier() {
assert_eq!(at(Some(Granularity::Batch)).carrier_qualifier(), None);
}
#[test]
fn validate_refuses_a_model_level_passport_naming_a_batch_or_unit() {
let mut p = at(Some(Granularity::Model));
assert!(p.validate().is_ok());
p.batch_id = Some("LOT-A".into());
p.serial_number = Some("SN-0001".into());
let refused: Vec<_> = fields_refused(&p).into_iter().map(|(f, _)| f).collect();
assert_eq!(refused, ["/batchId", "/serialNumber"]);
}
#[test]
fn validate_requires_a_printable_lot_at_batch_level() {
let mut p = at(Some(Granularity::Batch));
let refused = fields_refused(&p);
assert_eq!(refused.len(), 1);
assert_eq!(refused[0].0, "/batchId");
assert!(refused[0].1.contains("GS1 AI 10"), "{}", refused[0].1);
p.batch_id = Some("LOT-2026-A".into());
assert!(p.validate().is_ok());
for (bad, expected) in [
("", "must not be empty"),
("123456789012345678901", "GS1 AI 10 allows at most 20"),
("LOT A", "outside GS1 CSET 82"),
("LOT#A", "outside GS1 CSET 82"),
] {
p.batch_id = Some(bad.into());
let refused = fields_refused(&p);
assert_eq!(refused.len(), 1, "{bad:?}");
assert_eq!(refused[0].0, "/batchId", "{bad:?}");
assert!(refused[0].1.contains(expected), "{bad:?}: {}", refused[0].1);
}
p.batch_id = Some("LOT-2026-A".into());
p.serial_number = Some("SN-0001".into());
let refused: Vec<_> = fields_refused(&p).into_iter().map(|(f, _)| f).collect();
assert_eq!(refused, ["/serialNumber"]);
}
#[test]
fn validate_leaves_the_lot_free_where_no_carrier_prints_it() {
for level in [Some(Granularity::Item), None] {
let mut p = at(level);
p.batch_id = Some("Lot A, line 3 #7".into());
p.serial_number = Some("SN-0001".into());
assert!(p.validate().is_ok(), "{level:?}");
}
}