commit 39c4075119815382837832ac6aa5a980d2d78599
parent 6683d409eedb11cd97f8a11f8f9aa50847b49bd1
Author: triesap <tyson@radroots.org>
Date: Sun, 21 Jun 2026 21:34:02 +0000
authority: expand coverage edge tests
- Cover authority error display/source variants, signer identity and signer failure variants, actor selector/getter paths, and draft-validation fallback mappings.
- Validate radroots_authority tests, crate check, diff check, refreshed coverage run, and radroots_authority policy gate.
Diffstat:
4 files changed, 231 insertions(+), 44 deletions(-)
diff --git a/crates/authority/src/actor.rs b/crates/authority/src/actor.rs
@@ -327,6 +327,11 @@ mod tests {
.expect("actor");
assert_eq!(actor.source(), RadrootsActorSource::LocalAccount);
+ assert_eq!(actor.pubkey().as_str(), hex_64('a'));
+ assert_eq!(
+ actor.roles().iter().copied().collect::<Vec<_>>(),
+ vec![RadrootsActorRole::Farmer]
+ );
let account_id = actor.account_id().expect("account id");
assert_eq!(account_id.as_str(), "acct-field-01");
assert_eq!(account_id.to_string(), "acct-field-01");
@@ -406,11 +411,14 @@ mod tests {
let from_str = "acct-field-01"
.parse::<RadrootsActorAccountId>()
.expect("from str");
+ let from_borrowed =
+ RadrootsActorAccountId::try_from("acct-field-01").expect("from borrowed");
let from_owned =
RadrootsActorAccountId::try_from("acct-field-01".to_owned()).expect("from owned");
assert_eq!(parsed, "acct-field-01");
assert_eq!(from_str.as_ref(), "acct-field-01");
+ assert_eq!(from_borrowed.as_str(), "acct-field-01");
assert_eq!(from_owned.into_string(), "acct-field-01");
}
@@ -423,20 +431,22 @@ mod tests {
Some("radroots.listing.published.v1".to_owned()),
);
- assert!(matches!(
+ assert_eq!(
request.selector(),
- RadrootsActorSelector::AccountId(account_id) if account_id.as_str() == "acct-field-01"
- ));
+ &RadrootsActorSelector::account_id("acct-field-01").expect("selector")
+ );
assert_eq!(request.required_role(), RadrootsActorRole::Seller);
assert_eq!(request.contract_id(), Some("radroots.listing.published.v1"));
}
#[test]
fn selector_supports_account_and_draft_resolution() {
- assert!(matches!(
+ assert_eq!(
RadrootsActorSelector::account_id("acct-field-01").expect("selector"),
- RadrootsActorSelector::AccountId(ref account_id) if account_id.as_str() == "acct-field-01"
- ));
+ RadrootsActorSelector::AccountId(
+ RadrootsActorAccountId::parse("acct-field-01").expect("account id")
+ )
+ );
assert!(matches!(
RadrootsActorSelector::SelectedAccount,
RadrootsActorSelector::SelectedAccount
diff --git a/crates/authority/src/authorization.rs b/crates/authority/src/authorization.rs
@@ -497,6 +497,40 @@ mod tests {
}
#[test]
+ fn signed_event_pubkey_mismatch_fails() {
+ let pubkey = hex_64('a');
+ let draft = listing_draft(pubkey.as_str());
+ let signed = RadrootsSignedNostrEvent::new(RadrootsSignedNostrEventParts {
+ id: draft.expected_event_id.clone(),
+ pubkey: hex_64('b'),
+ created_at: draft.created_at,
+ kind: draft.kind,
+ tags: draft.tags.clone(),
+ content: draft.content.clone(),
+ sig: hex_128('f'),
+ raw_json: "{}".to_owned(),
+ })
+ .expect("signed event");
+
+ assert!(matches!(
+ validate_signed_event_matches_draft(&signed, &draft),
+ Err(RadrootsAuthorityError::SignedEventPubkeyMismatch { .. })
+ ));
+ }
+
+ #[test]
+ fn draft_validation_fallback_errors_map_to_computed_id_invalid() {
+ let error = authority_error_from_draft_validation(RadrootsDraftError::UnknownContract(
+ "radroots.unknown.v1".to_owned(),
+ ));
+
+ assert!(matches!(
+ error,
+ RadrootsAuthorityError::SignedEventComputedIdInvalid { .. }
+ ));
+ }
+
+ #[test]
fn signed_event_computed_id_mismatch_fails() {
let pubkey = hex_64('a');
let inconsistent_draft = RadrootsFrozenEventDraft {
@@ -540,6 +574,19 @@ mod tests {
}
#[test]
+ fn static_signer_maps_invalid_signed_event_parts() {
+ let pubkey = hex_64('a');
+ let mut draft = listing_draft(pubkey.as_str());
+ draft.expected_event_id = "bad-id".to_owned();
+ let signer = StaticSigner::new(pubkey.as_str());
+
+ assert!(matches!(
+ signer.sign_frozen_draft(&draft),
+ Err(RadrootsSignerError::SigningFailed { .. })
+ ));
+ }
+
+ #[test]
fn authorized_actor_and_signer_return_signed_event() {
let pubkey = hex_64('a');
let draft = listing_draft(pubkey.as_str());
diff --git a/crates/authority/src/error.rs b/crates/authority/src/error.rs
@@ -244,43 +244,145 @@ impl std::error::Error for RadrootsSignerError {}
#[cfg(test)]
mod tests {
use super::*;
+ use radroots_events::contract::RadrootsActorRole;
use std::error::Error as _;
#[test]
fn authority_error_display_uses_contract_messages() {
+ let cases = [
+ (
+ RadrootsAuthorityError::InvalidActorPubkey,
+ "invalid actor public key",
+ ),
+ (
+ RadrootsAuthorityError::InvalidActorAccountIdEmpty,
+ "invalid actor account id: empty",
+ ),
+ (
+ RadrootsAuthorityError::InvalidActorAccountIdUntrimmed,
+ "invalid actor account id: contains leading or trailing whitespace",
+ ),
+ (
+ RadrootsAuthorityError::InvalidActorAccountIdControlCharacter,
+ "invalid actor account id: contains a control character",
+ ),
+ (
+ RadrootsAuthorityError::InvalidActorAccountIdTooLong { max_len: 128 },
+ "invalid actor account id: longer than 128 characters",
+ ),
+ (
+ RadrootsAuthorityError::InvalidSignerPubkey,
+ "invalid signer public key",
+ ),
+ (
+ RadrootsAuthorityError::UnknownContract {
+ contract_id: "radroots.unknown.v1".to_owned(),
+ },
+ "unknown event contract `radroots.unknown.v1`",
+ ),
+ (
+ RadrootsAuthorityError::DraftKindMismatch {
+ contract_id: "radroots.social.post.v1".to_owned(),
+ expected_kind: 1,
+ actual_kind: 2,
+ },
+ "event contract `radroots.social.post.v1` expects kind 1, got 2",
+ ),
+ (
+ RadrootsAuthorityError::ActorRoleUnsatisfied {
+ contract_id: "radroots.listing.published.v1".to_owned(),
+ required_role: RadrootsActorRole::Seller,
+ },
+ "actor does not satisfy role Seller for contract `radroots.listing.published.v1`",
+ ),
+ (
+ RadrootsAuthorityError::ActorPubkeyMismatch {
+ expected_pubkey: "expected".to_owned(),
+ actor_pubkey: "actor".to_owned(),
+ },
+ "actor pubkey mismatch: expected expected, got actor",
+ ),
+ (
+ RadrootsAuthorityError::SignerPubkeyMismatch {
+ expected_pubkey: "expected".to_owned(),
+ signer_pubkey: "signer".to_owned(),
+ },
+ "signer pubkey mismatch: expected expected, got signer",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventPubkeyMismatch {
+ expected_pubkey: "expected".to_owned(),
+ actual_pubkey: "actual".to_owned(),
+ },
+ "signed event pubkey mismatch: expected expected, got actual",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventIdMismatch {
+ expected_event_id: "expected".to_owned(),
+ actual_event_id: "actual".to_owned(),
+ },
+ "signed event id mismatch: expected expected, got actual",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventCreatedAtMismatch {
+ expected_created_at: 1,
+ actual_created_at: 2,
+ },
+ "signed event created_at mismatch: expected 1, got 2",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventKindMismatch {
+ expected_kind: 1,
+ actual_kind: 2,
+ },
+ "signed event kind mismatch: expected 1, got 2",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventTagsMismatch {
+ expected_len: 2,
+ actual_len: 1,
+ },
+ "signed event tags mismatch: expected 2 tags, got 1 tags",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventContentMismatch {
+ expected_len: 17,
+ actual_len: 2,
+ },
+ "signed event content mismatch: expected 17 bytes, got 2 bytes",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventComputedIdInvalid {
+ message: "invalid event id".to_owned(),
+ },
+ "signed event computed id could not be derived: invalid event id",
+ ),
+ (
+ RadrootsAuthorityError::SignedEventComputedIdMismatch {
+ expected_event_id: "expected".to_owned(),
+ computed_event_id: "computed".to_owned(),
+ },
+ "signed event computed id mismatch: expected expected, computed computed",
+ ),
+ ];
+
+ for (error, expected) in cases {
+ assert_eq!(error.to_string(), expected);
+ assert!(error.source().is_none());
+ }
+ }
+
+ #[test]
+ fn signer_error_display_and_source_are_stable() {
assert_eq!(
- RadrootsAuthorityError::InvalidActorPubkey.to_string(),
- "invalid actor public key"
- );
- assert_eq!(
- RadrootsAuthorityError::DraftKindMismatch {
- contract_id: "radroots.social.post.v1".to_owned(),
- expected_kind: 1,
- actual_kind: 2,
- }
- .to_string(),
- "event contract `radroots.social.post.v1` expects kind 1, got 2"
- );
- assert_eq!(
- RadrootsAuthorityError::SignedEventTagsMismatch {
- expected_len: 2,
- actual_len: 1,
- }
- .to_string(),
- "signed event tags mismatch: expected 2 tags, got 1 tags"
+ RadrootsSignerError::Unavailable.to_string(),
+ "signer unavailable"
);
assert_eq!(
- RadrootsAuthorityError::SignedEventContentMismatch {
- expected_len: 17,
- actual_len: 2,
- }
- .to_string(),
- "signed event content mismatch: expected 17 bytes, got 2 bytes"
+ RadrootsSignerError::Rejected.to_string(),
+ "signer rejected draft"
);
- }
- #[test]
- fn signer_error_display_and_source_are_stable() {
let signer_error = RadrootsSignerError::SigningFailed {
message: "deterministic failure".to_owned(),
};
diff --git a/crates/authority/src/signer.rs b/crates/authority/src/signer.rs
@@ -123,6 +123,18 @@ mod tests {
}
#[test]
+ fn signer_identity_validates_public_key() {
+ let pubkey = hex_64('a');
+ let identity = RadrootsSignerIdentity::new(pubkey.as_str()).expect("identity");
+ assert_eq!(identity.pubkey().as_str(), pubkey);
+
+ assert!(matches!(
+ RadrootsSignerIdentity::new("bad-pubkey"),
+ Err(RadrootsAuthorityError::InvalidSignerPubkey)
+ ));
+ }
+
+ #[test]
fn mock_signer_returns_signed_frozen_draft() {
let pubkey = hex_64('a');
let signer = MockSigner::new(pubkey.as_str());
@@ -138,21 +150,37 @@ mod tests {
#[test]
fn mock_signer_propagates_signing_errors() {
let pubkey = hex_64('a');
- let signer = MockSigner::failing(
- pubkey.as_str(),
+ let draft = draft_for(pubkey.as_str());
+
+ for failure in [
+ RadrootsSignerError::Unavailable,
+ RadrootsSignerError::Rejected,
RadrootsSignerError::SigningFailed {
message: "deterministic failure".to_owned(),
},
- );
- let draft = draft_for(pubkey.as_str());
+ ] {
+ let signer = MockSigner::failing(pubkey.as_str(), failure);
+ let err = signer.sign_frozen_draft(&draft).expect_err("failure");
+
+ match err {
+ RadrootsSignerError::Unavailable => {}
+ RadrootsSignerError::Rejected => {}
+ RadrootsSignerError::SigningFailed { message } => {
+ assert_eq!(message, "deterministic failure");
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn mock_signer_maps_invalid_signed_event_parts() {
+ let pubkey = hex_64('a');
+ let signer = MockSigner::new(pubkey.as_str());
+ let mut draft = draft_for(pubkey.as_str());
+ draft.expected_event_id = "bad-id".to_string();
let err = signer.sign_frozen_draft(&draft).expect_err("failure");
- assert_eq!(
- err,
- RadrootsSignerError::SigningFailed {
- message: "deterministic failure".to_owned()
- }
- );
+ assert!(matches!(err, RadrootsSignerError::SigningFailed { .. }));
}
}