use super::*;
#[test]
fn round_trip_gtin_serial() {
let uri = "https://id.odal-node.io/01/09506000134352/21/ABC123";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.gtin.as_str(), "09506000134352");
assert_eq!(dl.serial.as_deref(), Some("ABC123"));
assert_eq!(dl.build(), uri);
}
#[test]
fn non_https_scheme_is_rejected() {
assert!(matches!(
DigitalLink::parse("http://id.odal-node.io/01/09506000134352"),
Err(DigitalLinkError::InvalidScheme(_))
));
}
#[test]
fn round_trips_all_qualifiers_in_canonical_order() {
let uri = "https://id.odal-node.io/01/09506000134352/22/VAR-1/10/LOT-9/21/SN-7/235/TPX-3";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.variant.as_deref(), Some("VAR-1"));
assert_eq!(dl.batch.as_deref(), Some("LOT-9"));
assert_eq!(dl.serial.as_deref(), Some("SN-7"));
assert_eq!(dl.tpcsn.as_deref(), Some("TPX-3"));
assert_eq!(dl.build(), uri);
}
#[test]
fn parse_with_batch() {
let uri = "https://id.odal-node.io/01/09506000134352/10/BATCH01/21/SN001";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.batch.as_deref(), Some("BATCH01"));
assert_eq!(dl.serial.as_deref(), Some("SN001"));
}
#[test]
fn invalid_gtin_rejected() {
let uri = "https://id.odal-node.io/01/123/21/SN1";
assert!(matches!(
DigitalLink::parse(uri),
Err(DigitalLinkError::InvalidGtin(_))
));
}
#[test]
fn missing_gtin_rejected() {
let uri = "https://id.odal-node.io/21/SN1";
assert!(matches!(
DigitalLink::parse(uri),
Err(DigitalLinkError::MissingGtin)
));
}
#[test]
fn validate_gtin_valid() {
assert!(validate_gtin("09506000134352").is_ok());
}
#[test]
fn validate_gtin_wrong_length() {
assert!(matches!(
validate_gtin("123456"),
Err(DigitalLinkError::InvalidGtin(_))
));
}
#[test]
fn validate_gtin_non_digits() {
assert!(matches!(
validate_gtin("0950600013435X"),
Err(DigitalLinkError::InvalidGtin(_))
));
}
#[test]
fn validate_gtin_bad_check_digit() {
assert!(matches!(
validate_gtin("09506000134351"),
Err(DigitalLinkError::InvalidGtinCheckDigit { .. })
));
}
#[test]
fn build_qr_url_with_batch() {
let url = build_qr_url(
"https://id.odal-node.io",
"09506000134352",
"passport-123",
Some("BATCH-01"),
);
assert_eq!(
url,
"https://id.odal-node.io/01/09506000134352/10/BATCH-01/21/passport-123"
);
}
#[test]
fn build_qr_url_without_batch() {
let url = build_qr_url(
"https://id.odal-node.io",
"09506000134352",
"passport-456",
None,
);
assert_eq!(
url,
"https://id.odal-node.io/01/09506000134352/21/passport-456"
);
}
#[test]
fn bad_check_digit_rejected_via_parse() {
let uri = "https://id.odal-node.io/01/09506000134351/21/SN1";
assert!(matches!(
DigitalLink::parse(uri),
Err(DigitalLinkError::InvalidGtinCheckDigit { .. })
));
}
#[test]
fn query_string_does_not_corrupt_serial() {
let uri = "https://id.odal-node.io/01/09506000134352/21/SN001?linkType=gs1:pip";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.serial.as_deref(), Some("SN001"));
}
#[test]
fn query_only_uri_returns_gtin_cleanly() {
let uri = "https://id.odal-node.io/01/09506000134352?linkType=gs1:pip";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.gtin.as_str(), "09506000134352");
assert_eq!(dl.serial, None);
}
#[test]
fn gtin_13_normalised_to_14() {
let uri = "https://id.odal-node.io/01/5901234123457/21/SN1";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.gtin.as_str(), "05901234123457");
}
#[test]
fn gtin_12_normalised_to_14() {
let uri = "https://id.odal-node.io/01/012345678905/21/SN1";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.gtin.as_str(), "00012345678905");
}
#[test]
fn gtin_of_invalid_length_rejected() {
let uri = "https://id.odal-node.io/01/123456789/21/SN1";
assert!(matches!(
DigitalLink::parse(uri),
Err(DigitalLinkError::InvalidGtin(_))
));
}
#[test]
fn unknown_ai_rejected() {
let uri = "https://id.odal-node.io/01/09506000134352/99/unknown/21/SN1";
assert!(matches!(
DigitalLink::parse(uri),
Err(DigitalLinkError::UnknownApplicationIdentifier(_))
));
}
#[test]
fn misordered_qualifiers_rejected() {
let uri = "https://id.odal-node.io/01/09506000134352/21/SN1/10/BATCH1";
assert!(matches!(
DigitalLink::parse(uri),
Err(DigitalLinkError::QualifiersOutOfOrder { .. })
));
}
#[test]
fn percent_encoded_serial_decoded_on_parse() {
let uri = "https://id.odal-node.io/01/09506000134352/21/SN%2F001";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.serial.as_deref(), Some("SN/001"));
}
#[test]
fn slash_in_serial_encoded_on_build() {
let uri = "https://id.odal-node.io/01/09506000134352/21/SN1";
let mut dl = DigitalLink::parse(uri).unwrap();
dl.serial = Some("SN/001".to_owned());
assert!(dl.build().contains("/21/SN%2F001"));
}
#[test]
fn question_mark_in_serial_encoded_on_build() {
let uri = "https://id.odal-node.io/01/09506000134352/21/SN1";
let mut dl = DigitalLink::parse(uri).unwrap();
dl.serial = Some("SN?001".to_owned());
assert!(dl.build().contains("/21/SN%3F001"));
}
#[test]
fn path_prefixed_resolver_round_trip() {
let uri = "https://example.com/resolve/01/09506000134352/21/SN001";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.resolver_base, "https://example.com/resolve");
assert_eq!(dl.build(), uri);
}
#[test]
fn multi_segment_path_prefix_preserved() {
let uri = "https://example.com/api/v1/dpp/01/09506000134352/21/SN001";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.resolver_base, "https://example.com/api/v1/dpp");
assert_eq!(dl.build(), uri);
}
#[test]
fn port_bearing_resolver_round_trip() {
let uri = "https://localhost:8080/01/09506000134352/21/SN001";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.resolver_base, "https://localhost:8080");
assert_eq!(dl.build(), uri);
}
#[test]
fn path_prefixed_resolver_with_batch_round_trip() {
let uri = "https://example.com/resolve/01/09506000134352/10/LOT1/21/SN001";
let dl = DigitalLink::parse(uri).unwrap();
assert_eq!(dl.resolver_base, "https://example.com/resolve");
assert_eq!(dl.build(), uri);
}