codec_error_job.rs (6285B)
1 #[path = "../src/test_fixtures.rs"] 2 mod test_fixtures; 3 4 use std::error::Error as _; 5 6 use radroots_event_codec::decode::EventParseError; 7 use radroots_event_codec::decode::job::JobParseError; 8 use radroots_event_codec::encode::EventEncodeError; 9 use radroots_event_codec::encode::job::{ 10 JobEncodeError, assert_no_inputs_when_encrypted, push_provider_tag, push_relay_tag, 11 push_status_tag, 12 }; 13 #[cfg(feature = "json")] 14 use serde::Serialize; 15 #[cfg(feature = "json")] 16 use serde::ser::{Error as _, Serializer}; 17 use test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS}; 18 19 #[test] 20 fn parse_error_display_and_source_cover_variants() { 21 let missing = EventParseError::MissingTag("d"); 22 assert_eq!(missing.to_string(), "missing tag: d"); 23 assert_eq!(missing.code(), "missing_tag"); 24 assert!(missing.source().is_none()); 25 26 let invalid = EventParseError::InvalidTag("a"); 27 assert_eq!(invalid.to_string(), "invalid tag structure for 'a'"); 28 assert_eq!(invalid.code(), "invalid_tag"); 29 assert!(invalid.source().is_none()); 30 31 let invalid_kind = EventParseError::InvalidKind { 32 expected: "30340", 33 got: 1, 34 }; 35 assert_eq!(invalid_kind.to_string(), "invalid kind 1 (expected 30340)"); 36 assert_eq!(invalid_kind.code(), "invalid_kind"); 37 assert!(invalid_kind.source().is_none()); 38 39 let parse_int = "x".parse::<u32>().expect_err("parse int error"); 40 let invalid_number = EventParseError::InvalidNumber("count", parse_int); 41 assert!( 42 invalid_number 43 .to_string() 44 .contains("invalid number in 'count'") 45 ); 46 assert_eq!(invalid_number.code(), "invalid_number"); 47 assert!(invalid_number.source().is_some()); 48 49 let invalid_json = EventParseError::InvalidJson("content"); 50 assert_eq!(invalid_json.to_string(), "invalid JSON in 'content'"); 51 assert_eq!(invalid_json.code(), "invalid_json"); 52 assert!(invalid_json.source().is_none()); 53 } 54 55 #[test] 56 fn encode_error_display_covers_variants() { 57 let invalid_kind = EventEncodeError::InvalidKind(30402); 58 assert_eq!(invalid_kind.to_string(), "invalid event kind: 30402"); 59 assert_eq!(invalid_kind.code(), "invalid_kind"); 60 61 let empty_required = EventEncodeError::EmptyRequiredField("content"); 62 assert_eq!(empty_required.to_string(), "empty required field: content"); 63 assert_eq!(empty_required.code(), "empty_required_field"); 64 65 let invalid_field = EventEncodeError::InvalidField("d"); 66 assert_eq!(invalid_field.to_string(), "invalid field: d"); 67 assert_eq!(invalid_field.code(), "invalid_field"); 68 69 let json = EventEncodeError::Json; 70 assert_eq!(json.to_string(), "failed to serialize JSON"); 71 assert_eq!(json.code(), "json"); 72 } 73 74 #[test] 75 fn job_encode_helpers_cover_status_provider_relay_and_inputs() { 76 let mut tags: Vec<Vec<String>> = Vec::new(); 77 push_status_tag(&mut tags, "ok", None); 78 push_status_tag(&mut tags, "warning", Some("detail")); 79 push_provider_tag(&mut tags, FIXTURE_ALICE_PUBLIC_KEY_HEX); 80 push_relay_tag(&mut tags, RELAY_PRIMARY_WSS); 81 82 assert_eq!(tags[0], vec!["status".to_string(), "ok".to_string()]); 83 assert_eq!( 84 tags[1], 85 vec![ 86 "status".to_string(), 87 "warning".to_string(), 88 "detail".to_string(), 89 ] 90 ); 91 assert_eq!( 92 tags[2], 93 vec!["p".to_string(), FIXTURE_ALICE_PUBLIC_KEY_HEX.to_string(),] 94 ); 95 assert_eq!( 96 tags[3], 97 vec!["relays".to_string(), RELAY_PRIMARY_WSS.to_string()] 98 ); 99 100 assert!(assert_no_inputs_when_encrypted(&tags)); 101 let tags_with_input = vec![vec!["i".to_string(), "amount".to_string()]]; 102 assert!(!assert_no_inputs_when_encrypted(&tags_with_input)); 103 } 104 105 #[cfg(feature = "json")] 106 #[test] 107 fn job_json_content_covers_success_and_error_paths() { 108 #[derive(Clone)] 109 struct BrokenSerialize; 110 111 impl Serialize for BrokenSerialize { 112 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> 113 where 114 S: Serializer, 115 { 116 Err(S::Error::custom("forced serialization error")) 117 } 118 } 119 120 let ok = radroots_event_codec::encode::job::json_content(&vec!["ok".to_string()]) 121 .expect("json content"); 122 assert_eq!(ok, "[\"ok\"]"); 123 124 let err = radroots_event_codec::encode::job::json_content(&BrokenSerialize) 125 .expect_err("json content error"); 126 assert!(matches!( 127 err, 128 JobEncodeError::EmptyRequiredField("content-json") 129 )); 130 } 131 132 #[test] 133 fn job_encode_error_display_covers_variants() { 134 assert_eq!( 135 JobEncodeError::MissingProvidersForEncrypted.to_string(), 136 "encrypted=true requires at least one provider ('p') tag" 137 ); 138 assert_eq!( 139 JobEncodeError::InvalidKind(7000).to_string(), 140 "invalid job event kind: 7000" 141 ); 142 assert_eq!( 143 JobEncodeError::EmptyRequiredField("content").to_string(), 144 "empty required field: content" 145 ); 146 } 147 148 #[test] 149 fn job_parse_error_display_and_source_covers_variants() { 150 let kind = JobParseError::KindOutOfRange(u32::from(u16::MAX) + 1); 151 assert!(kind.to_string().contains("Nostr event kind")); 152 assert!(kind.source().is_none()); 153 154 let missing = JobParseError::MissingTag("e"); 155 assert_eq!(missing.to_string(), "missing tag: e"); 156 assert!(missing.source().is_none()); 157 158 let invalid = JobParseError::InvalidTag("status"); 159 assert_eq!(invalid.to_string(), "invalid tag structure for 'status'"); 160 assert!(invalid.source().is_none()); 161 162 let invalid_number = JobParseError::InvalidNumber("amount", "x".parse::<u32>().unwrap_err()); 163 assert!( 164 invalid_number 165 .to_string() 166 .contains("invalid number in 'amount'") 167 ); 168 assert!(invalid_number.source().is_some()); 169 170 let non_whole = JobParseError::NonWholeSats("amount"); 171 assert!(non_whole.to_string().contains("whole number of sats")); 172 assert!(non_whole.source().is_none()); 173 174 let overflow = JobParseError::AmountOverflow("amount"); 175 assert!(overflow.to_string().contains("does not fit u32 sat")); 176 assert!(overflow.source().is_none()); 177 178 let missing_chain = JobParseError::MissingChainTag("e"); 179 assert_eq!(missing_chain.to_string(), "missing required chain tag: e"); 180 assert!(missing_chain.source().is_none()); 181 }