use super::*;
use chrono::{TimeZone as _, Utc};
fn at(y: i32, m: u32, d: u32) -> chrono::DateTime<Utc> {
Utc.with_ymd_and_hms(y, m, d, 0, 0, 0).unwrap()
}
#[test]
fn the_qualified_axis_round_trips_through_its_uris() {
assert_eq!(
TrustServiceStatus::from_uri(GRANTED_URI),
TrustServiceStatus::Granted
);
assert_eq!(
TrustServiceStatus::from_uri(WITHDRAWN_URI),
TrustServiceStatus::Withdrawn
);
assert_eq!(TrustServiceStatus::Granted.as_uri(), GRANTED_URI);
assert_eq!(TrustServiceStatus::Withdrawn.as_uri(), WITHDRAWN_URI);
}
#[test]
fn no_other_status_is_a_lesser_granted() {
for uri in [
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/undersupervision",
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/accredited",
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/recognisedatnationallevel",
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/setbynationallaw",
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/supervisionceased",
] {
let status = TrustServiceStatus::from_uri(uri);
assert!(
!status.is_granted(),
"{uri} is not qualified status under 910/2014"
);
assert_eq!(status.as_uri(), uri, "and it round-trips unchanged");
}
}
#[test]
fn an_unknown_status_is_readable_and_not_granted() {
let future = TrustServiceStatus::from_uri(
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/somethingaddedin2030",
);
assert!(!future.is_granted());
assert!(matches!(future, TrustServiceStatus::Other(_)));
}
#[test]
fn the_remote_seal_device_service_types_are_not_interchangeable() {
let qualified = TrustServiceType::new(TrustServiceType::REMOTE_QSEAL_CD_MANAGEMENT);
let plain = TrustServiceType::new(TrustServiceType::REMOTE_SEAL_CD_MANAGEMENT);
assert_ne!(qualified, plain);
assert!(qualified.looks_qualified());
assert!(
!plain.looks_qualified(),
"managing a non-qualified creation device is not the Art. 39a service"
);
}
#[test]
fn a_withdrawal_does_not_reach_back_to_seals_made_while_granted() {
let history = TrustServiceHistory::new(vec![
TrustServiceStatusPeriod {
status: TrustServiceStatus::Granted,
starting_at: at(2026, 1, 1),
},
TrustServiceStatusPeriod {
status: TrustServiceStatus::Withdrawn,
starting_at: at(2029, 6, 1),
},
]);
assert!(
history.was_granted_at(at(2027, 5, 1)),
"the seal was made while the provider held qualified status"
);
assert!(
!history.was_granted_at(at(2030, 1, 1)),
"and the provider does not hold it now"
);
}
#[test]
fn a_later_grant_does_not_qualify_an_earlier_seal() {
let history = TrustServiceHistory::new(vec![
TrustServiceStatusPeriod {
status: TrustServiceStatus::Withdrawn,
starting_at: at(2026, 1, 1),
},
TrustServiceStatusPeriod {
status: TrustServiceStatus::Granted,
starting_at: at(2029, 6, 1),
},
]);
assert!(
!history.was_granted_at(at(2027, 5, 1)),
"the provider was not qualified when this seal was made"
);
assert!(history.was_granted_at(at(2030, 1, 1)));
}
#[test]
fn unordered_periods_give_the_same_answer() {
let ordered = vec![
TrustServiceStatusPeriod {
status: TrustServiceStatus::Granted,
starting_at: at(2026, 1, 1),
},
TrustServiceStatusPeriod {
status: TrustServiceStatus::Withdrawn,
starting_at: at(2029, 6, 1),
},
];
let mut shuffled = ordered.clone();
shuffled.reverse();
for when in [at(2027, 5, 1), at(2030, 1, 1)] {
assert_eq!(
TrustServiceHistory::new(ordered.clone()).was_granted_at(when),
TrustServiceHistory::new(shuffled.clone()).was_granted_at(when),
);
}
}
#[test]
fn a_moment_before_the_history_is_unknown_not_withdrawn() {
let history = TrustServiceHistory::new(vec![TrustServiceStatusPeriod {
status: TrustServiceStatus::Granted,
starting_at: at(2026, 1, 1),
}]);
assert_eq!(history.status_at(at(2020, 1, 1)), None);
assert!(!history.was_granted_at(at(2020, 1, 1)));
assert_eq!(
history.status_at(at(2026, 1, 1)),
Some(&TrustServiceStatus::Granted),
"the boundary is inclusive: a status applies from the instant it starts"
);
}
#[test]
fn an_empty_history_is_unknown() {
let history = TrustServiceHistory::new(vec![]);
assert_eq!(history.status_at(at(2027, 1, 1)), None);
assert!(!history.was_granted_at(at(2027, 1, 1)));
}
#[test]
fn the_qualified_naming_convention_holds_for_every_listed_service_type() {
const BASE: &str = "http://uri.etsi.org/TrstSvc/Svctype/";
const QUALIFIED: &[&str] = &[
"CA/QC",
"Certstatus/CRL/QC",
"Certstatus/OCSP/QC",
"EAA/Q",
"EDS/Q",
"EDS/REM/Q",
"ElectronicArchiving/Q",
"Ledgers/Q",
"PSES/Q",
"QESValidation/Q",
"RemoteQSealCDManagement/Q",
"RemoteQSigCDManagement/Q",
"TSA/QTST",
];
const NOT_QUALIFIED: &[&str] = &[
"AdESGeneration",
"AdESValidation",
"CA/PKC",
"CA/PKC/CertsforOtherTypesOfTS",
"Certstatus/CRL",
"Certstatus/OCSP",
"EAA",
"EAA/Pub-EAA",
"EAAValidation",
"EDS",
"EDS/REM",
"EDSValidation",
"ElectronicArchiving",
"Ledgers",
"PKCPreservation",
"PKCValidation",
"PKCValidation/CertsforOtherTypesOfTS",
"PSES",
"RemoteSealCDManagement",
"RemoteSigCDManagement",
"TSA",
"TSA/TSS-AdESQCandQES",
"TSA/TSS-QC",
"TSTValidation",
"ACA",
"Archiv",
"Archiv/nothavingPKIid",
"IdV",
"IdV/nothavingPKIid",
"KEscrow",
"KEscrow/nothavingPKIid",
"NationalRootCA-QC",
"PPwd",
"PPwd/nothavingPKIid",
"RA",
"RA/nothavingPKIid",
"SignaturePolicyAuthority",
"TLIssuer",
"unspecified",
];
for suffix in QUALIFIED {
let service = TrustServiceType::new(format!("{BASE}{suffix}"));
assert!(service.looks_qualified(), "{suffix} is a qualified type");
}
for suffix in NOT_QUALIFIED {
let service = TrustServiceType::new(format!("{BASE}{suffix}"));
assert!(
!service.looks_qualified(),
"{suffix} is not a qualified type"
);
}
}