use serde_json::json;
use super::{ProductIdentifier, ProductIdentifierError};
use crate::identifier::Gtin;
const GTIN: &str = "03801234567898";
#[test]
fn a_scheme_1_identifier_carries_a_validated_gtin() {
let id = ProductIdentifier::gs1(Gtin::parse(GTIN).unwrap());
assert_eq!(id.gtin().map(Gtin::as_str), Some(GTIN));
assert_eq!(id.as_str(), GTIN);
}
#[test]
fn the_self_issuing_schemes_have_no_gtin() {
let link = ProductIdentifier::identification_link("https://id.example.com/p/1").unwrap();
let did = ProductIdentifier::did("did:web:example.com:p:1").unwrap();
assert!(link.gtin().is_none());
assert!(did.gtin().is_none());
assert_eq!(link.as_str(), "https://id.example.com/p/1");
assert_eq!(did.as_str(), "did:web:example.com:p:1");
}
#[test]
fn an_identification_link_must_be_an_absolute_web_url() {
for bad in [
"example.com/p/1",
"/p/1",
"ftp://example.com/p/1",
"https://",
"",
"https:///p/1",
"https://?product=1",
"https://#fragment",
"https://exam ple.com/p/1",
] {
assert!(
matches!(
ProductIdentifier::identification_link(bad),
Err(ProductIdentifierError::NotAWebUrl(_))
),
"{bad:?} was accepted as an identification link"
);
}
assert!(ProductIdentifier::identification_link("http://example.com/p/1").is_ok());
}
#[test]
fn only_the_did_methods_the_standard_names_are_accepted() {
for method in ["web", "ethr", "ebsi"] {
let did = format!("did:{method}:example.com:p:1");
assert!(
ProductIdentifier::did(&did).is_ok(),
"{did} was refused but clause 5 names it"
);
}
assert!(matches!(
ProductIdentifier::did("did:key:z6Mk"),
Err(ProductIdentifierError::UnsupportedDidMethod(m)) if m == "key"
));
}
#[test]
fn a_malformed_did_is_refused() {
assert!(matches!(
ProductIdentifier::did("web:example.com"),
Err(ProductIdentifierError::NotADid(_))
));
assert!(matches!(
ProductIdentifier::did("did:web"),
Err(ProductIdentifierError::NotADid(_))
));
assert!(matches!(
ProductIdentifier::did("did:web:"),
Err(ProductIdentifierError::EmptyDidMethodId(_))
));
}
#[test]
fn a_did_must_satisfy_the_did_core_grammar() {
for bad in [
"did:web: ",
"did:web:exam ple.com",
"did:web:example.com:",
"did:web:exa%mple",
"did:web:example%",
"did:web:example%zz",
"did:web:example.com/p/1",
"did:web:example.com?q=1",
] {
assert!(
matches!(
ProductIdentifier::did(bad),
Err(ProductIdentifierError::NotADid(_))
),
"{bad:?} was accepted as a DID"
);
}
for good in [
"did:web:example.com%3A3000:p:1",
"did:web:example.com::p",
"did:web:a_b-c.d",
] {
assert!(
ProductIdentifier::did(good).is_ok(),
"{good:?} was refused but the grammar admits it"
);
}
}
#[test]
fn each_scheme_serialises_to_its_documented_wire_form() {
let cases = [
(
ProductIdentifier::gs1(Gtin::parse(GTIN).unwrap()),
json!({"scheme": "gs1", "gtin": GTIN}),
),
(
ProductIdentifier::identification_link("https://id.example.com/p/1").unwrap(),
json!({"scheme": "identificationLink", "url": "https://id.example.com/p/1"}),
),
(
ProductIdentifier::did("did:web:example.com:p:1").unwrap(),
json!({"scheme": "did", "did": "did:web:example.com:p:1"}),
),
];
for (value, wire) in cases {
assert_eq!(serde_json::to_value(&value).unwrap(), wire);
let back: ProductIdentifier = serde_json::from_value(wire.clone()).unwrap();
assert_eq!(back, value);
}
}
#[test]
fn deserialisation_still_rejects_an_invalid_gtin() {
let bad = json!({"scheme": "gs1", "gtin": "03801234567891"});
assert!(
serde_json::from_value::<ProductIdentifier>(bad).is_err(),
"a bad check digit was accepted through serde"
);
}
#[test]
fn deserialisation_refuses_what_the_constructors_refuse() {
let unconstructable = [
json!({"scheme": "did", "did": "did:key:z6Mk"}),
json!({"scheme": "did", "did": "did:web: "}),
json!({"scheme": "did", "did": "did:web:"}),
json!({"scheme": "identificationLink", "url": "https:///p/1"}),
json!({"scheme": "identificationLink", "url": "ftp://example.com/p/1"}),
json!({"scheme": "identificationLink", "url": "example.com/p/1"}),
];
for wire in unconstructable {
assert!(
serde_json::from_value::<ProductIdentifier>(wire.clone()).is_err(),
"{wire} was accepted through serde but the constructor refuses it"
);
}
}