#![cfg(test)]
use super::*;
#[test]
fn every_invalid_kind_role_pair_is_refused_and_only_the_table_is_accepted() {
let mut accepted = 0;
let mut refused = 0;
for kind in [DelegationSubjectKind::Domain, DelegationSubjectKind::Seat] {
for role in [DelegationRole::Operational, DelegationRole::SpeaksFor] {
let expected = matches!(
(kind, role),
(DelegationSubjectKind::Domain, DelegationRole::Operational)
| (DelegationSubjectKind::Seat, DelegationRole::SpeaksFor)
);
assert_eq!(
kind.permits(role),
expected,
"the pair table disagrees about ({kind:?}, {role:?})"
);
if expected {
accepted += 1;
} else {
refused += 1;
}
}
}
assert_eq!(accepted, 2, "exactly two pairs are valid in v1");
assert_eq!(refused, 2, "exactly two pairs are invalid in v1");
}
#[test]
fn decoding_a_canonical_artifact_recovers_every_field() {
let fields = decode_fields(&artifact_bytes(&ROOT_KEY, &claim(), &SIGNATURE)).unwrap();
assert_eq!(fields.root_public_key, ROOT_KEY);
assert_eq!(fields.claim, claim());
assert_eq!(fields.claim.sequence, SEQUENCE);
assert_eq!(fields.signature, SIGNATURE);
}
#[test]
fn a_ten_thousand_byte_subject_decodes_whole() {
let mut long = claim();
long.subject_value = "o".repeat(10_000);
let bytes = artifact_bytes(&ROOT_KEY, &long, &SIGNATURE);
assert!(bytes.len() > 10_000);
let fields = decode_fields(&bytes).unwrap();
assert_eq!(fields.claim, long);
assert_eq!(fields.claim.subject_value.len(), 10_000);
}
#[test]
fn check_encodable_agrees_with_the_decoder_on_every_subject_length() {
let mut checked = 0;
for len in (3800..3900usize).chain([10_000, 70_000]) {
let mut c = claim();
c.subject_value = "o".repeat(len);
let accepted = check_encodable(&ROOT_KEY, &c).is_ok();
let decoded = decode_fields(&artifact_bytes(&ROOT_KEY, &c, &SIGNATURE));
assert!(accepted, "the issuing side refused subject length {len}");
assert_eq!(
decoded.map(|fields| fields.claim).ok(),
Some(c),
"encode and decode disagree at subject length {len} — this is the \
defect class the encode-side check exists to close"
);
checked += 1;
}
assert_eq!(checked, 102, "every length must have been exercised");
}
#[test]
fn a_sequence_of_u64_max_is_refused_at_both_ends_so_a_successor_always_exists() {
let mut at_max = claim();
at_max.sequence = u64::MAX;
let err = check_encodable(&ROOT_KEY, &at_max).unwrap_err();
let TrustError::DelegationEncoding { reason } = &err else {
panic!("expected an encoding error naming the constraint, got {err:?}");
};
assert!(
reason.contains("successor"),
"the reason must name the property being preserved, got: {reason}"
);
let mutant = artifact_bytes(&ROOT_KEY, &at_max, &SIGNATURE);
assert!(matches!(
decode_fields(&mutant),
Err(TrustError::DelegationVerification)
));
assert_eq!(MAX_SEQUENCE, u64::MAX - 1);
let mut at_limit = claim();
at_limit.sequence = MAX_SEQUENCE;
check_encodable(&ROOT_KEY, &at_limit).unwrap();
assert_eq!(
decode_fields(&artifact_bytes(&ROOT_KEY, &at_limit, &SIGNATURE))
.unwrap()
.claim
.sequence,
MAX_SEQUENCE
);
}
#[test]
fn check_encodable_refuses_an_empty_subject_value_and_an_unusable_delegated_key() {
let mut empty_subject = claim();
empty_subject.subject_value = String::new();
assert!(matches!(
check_encodable(&ROOT_KEY, &empty_subject),
Err(TrustError::DelegationEncoding { .. })
));
let mut refused = 0;
for bad_key in unusable_keys() {
let mut c = claim();
c.delegated_public_key = bad_key;
assert!(
matches!(
check_encodable(&ROOT_KEY, &c),
Err(TrustError::DelegationEncoding { .. })
),
"an unusable delegated key was accepted at encode"
);
refused += 1;
}
assert_eq!(refused, 3, "every unusable key shape must have been tried");
check_encodable(&ROOT_KEY, &claim()).unwrap();
}
#[test]
fn check_encodable_refuses_an_invalid_kind_role_pair_with_an_actionable_reason() {
let mut refused = 0;
for (kind, role) in [
(DelegationSubjectKind::Domain, DelegationRole::SpeaksFor),
(DelegationSubjectKind::Seat, DelegationRole::Operational),
] {
let mut c = claim();
c.subject_kind = kind;
c.role = role;
let err = check_encodable(&ROOT_KEY, &c).unwrap_err();
let TrustError::DelegationEncoding { reason } = &err else {
panic!("expected an encoding error naming the constraint, got {err:?}");
};
assert!(
reason.contains("pair"),
"the reason must name the rule being enforced, got: {reason}"
);
refused += 1;
}
assert_eq!(refused, 2, "both invalid pairs must have been tried");
check_encodable(&ROOT_KEY, &claim()).unwrap();
check_encodable(&ROOT_KEY, &seat_claim()).unwrap();
}
#[test]
fn check_encodable_refuses_an_unusable_root_key_with_an_actionable_reason() {
let mut refused = 0;
for bad_key in unusable_keys() {
let err = check_encodable(&bad_key, &claim()).unwrap_err();
let TrustError::DelegationEncoding { reason } = &err else {
panic!("expected an encoding error naming the constraint, got {err:?}");
};
assert!(
reason.contains("root public key"),
"the reason must name the slot at fault, got: {reason}"
);
refused += 1;
}
assert_eq!(refused, 3, "every unusable key shape must have been tried");
check_encodable(&ROOT_KEY, &claim()).unwrap();
}