use super::*;
use dpp_domain::Disclosure;
fn sample_descriptors() -> Vec<LinkDescriptor> {
vec![
LinkDescriptor {
href: "https://id.example.com/dpp/12345/pip".into(),
link_type: Gs1LinkType::ProductInformationPage,
media_type: DppMediaType::Html,
disclosure: Disclosure::Public,
title: Some("Product Page".into()),
language: Some("en".into()),
},
LinkDescriptor {
href: "https://id.example.com/dpp/12345/data.json".into(),
link_type: Gs1LinkType::DigitalProductPassport,
media_type: DppMediaType::Json,
disclosure: Disclosure::Public,
title: Some("DPP Data (JSON)".into()),
language: None,
},
LinkDescriptor {
href: "https://id.example.com/dpp/12345/data.jsonld".into(),
link_type: Gs1LinkType::DigitalProductPassport,
media_type: DppMediaType::JsonLd,
disclosure: Disclosure::Public,
title: Some("DPP Data (JSON-LD)".into()),
language: None,
},
LinkDescriptor {
href: "https://id.example.com/dpp/12345/safety".into(),
link_type: Gs1LinkType::SafetyInfo,
media_type: DppMediaType::Json,
disclosure: Disclosure::Restricted,
title: Some("SVHC Substances".into()),
language: None,
},
LinkDescriptor {
href: "https://id.example.com/dpp/12345/compliance".into(),
link_type: Gs1LinkType::CertificationInfo,
media_type: DppMediaType::Json,
disclosure: Disclosure::Conformity,
title: Some("Full Compliance Report".into()),
language: None,
},
]
}
#[test]
fn negotiate_default_returns_first_public() {
let descs = sample_descriptors();
let req = ResolutionRequest {
link_type: None,
media_type: None,
audience: None,
};
let result = negotiate(&descs, &req).unwrap();
assert_eq!(result.link_type, Gs1LinkType::ProductInformationPage);
}
#[test]
fn negotiate_by_link_type() {
let descs = sample_descriptors();
let req = ResolutionRequest {
link_type: Some(Gs1LinkType::DigitalProductPassport),
media_type: None,
audience: None,
};
let result = negotiate(&descs, &req).unwrap();
assert_eq!(result.link_type, Gs1LinkType::DigitalProductPassport);
}
#[test]
fn negotiate_by_link_type_and_media_type() {
let descs = sample_descriptors();
let req = ResolutionRequest {
link_type: Some(Gs1LinkType::DigitalProductPassport),
media_type: Some(DppMediaType::JsonLd),
audience: None,
};
let result = negotiate(&descs, &req).unwrap();
assert_eq!(result.media_type, DppMediaType::JsonLd);
}
#[test]
fn negotiate_filters_by_audience() {
let descs = sample_descriptors();
let req = ResolutionRequest {
link_type: Some(Gs1LinkType::SafetyInfo),
media_type: None,
audience: Some(Audience::Public),
};
let result = negotiate(&descs, &req);
assert!(result.is_none(), "public caller should not see safety info");
}
#[test]
fn negotiate_professional_sees_safety_info() {
let descs = sample_descriptors();
let req = ResolutionRequest {
link_type: Some(Gs1LinkType::SafetyInfo),
media_type: None,
audience: Some(Audience::LegitimateInterest),
};
let result = negotiate(&descs, &req).unwrap();
assert_eq!(result.link_type, Gs1LinkType::SafetyInfo);
}
#[test]
fn negotiate_confidential_sees_everything() {
let descs = sample_descriptors();
let req = ResolutionRequest {
link_type: Some(Gs1LinkType::CertificationInfo),
media_type: None,
audience: Some(Audience::Authority),
};
let result = negotiate(&descs, &req).unwrap();
assert_eq!(result.link_type, Gs1LinkType::CertificationInfo);
}
#[test]
fn link_type_parse_shorthand() {
assert_eq!(
Gs1LinkType::parse("gs1:pip"),
Gs1LinkType::ProductInformationPage
);
assert_eq!(
Gs1LinkType::parse("gs1:dpp"),
Gs1LinkType::DigitalProductPassport
);
assert_eq!(
Gs1LinkType::parse("gs1:sustainabilityInfo"),
Gs1LinkType::SustainabilityInfo
);
}
#[test]
fn link_type_parse_full_uri() {
assert_eq!(
Gs1LinkType::parse("https://ref.gs1.org/voc/pip"),
Gs1LinkType::ProductInformationPage
);
}
#[test]
fn gs1_uris_use_canonical_ref_namespace() {
for lt in [
Gs1LinkType::ProductInformationPage,
Gs1LinkType::DigitalProductPassport,
Gs1LinkType::SustainabilityInfo,
Gs1LinkType::Traceability,
] {
assert!(
lt.as_gs1_uri().starts_with("https://ref.gs1.org/voc/"),
"{lt:?} must use the canonical ref.gs1.org/voc namespace, got {}",
lt.as_gs1_uri()
);
}
}
#[test]
fn link_type_parse_unknown() {
let lt = Gs1LinkType::parse("https://example.com/custom");
assert!(matches!(lt, Gs1LinkType::Custom(_)));
}
#[test]
fn link_type_round_trip_uri() {
let lt = Gs1LinkType::DigitalProductPassport;
let uri = lt.as_gs1_uri();
let parsed = Gs1LinkType::parse(uri);
assert_eq!(lt, parsed);
}
#[test]
fn media_type_parse() {
assert_eq!(DppMediaType::parse("application/json"), DppMediaType::Json);
assert_eq!(
DppMediaType::parse("application/ld+json; charset=utf-8"),
DppMediaType::JsonLd
);
assert_eq!(DppMediaType::parse("text/html"), DppMediaType::Html);
}
#[test]
fn accept_header_html_picks_html() {
let req = ResolutionRequest::from_accept_header("text/html");
assert_eq!(req.media_type, Some(DppMediaType::Html));
}
#[test]
fn accept_header_json_ld_picks_json_ld() {
let req = ResolutionRequest::from_accept_header("application/ld+json");
assert_eq!(req.media_type, Some(DppMediaType::JsonLd));
}
#[test]
fn accept_header_browser_wildcard_picks_html() {
let req = ResolutionRequest::from_accept_header(
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
);
assert_eq!(req.media_type, Some(DppMediaType::Html));
}
#[test]
fn accept_header_q_value_respected() {
let req = ResolutionRequest::from_accept_header("text/html;q=0.1, application/ld+json;q=0.9");
assert_eq!(req.media_type, Some(DppMediaType::JsonLd));
}
#[test]
fn accept_header_wildcard_only_returns_none() {
let req = ResolutionRequest::from_accept_header("*/*");
assert_eq!(req.media_type, None);
}
#[test]
fn accept_header_empty_returns_none() {
let req = ResolutionRequest::from_accept_header("");
assert_eq!(req.media_type, None);
}
#[test]
fn empty_descriptors_returns_none() {
let req = ResolutionRequest {
link_type: None,
media_type: None,
audience: None,
};
assert!(negotiate(&[], &req).is_none());
}