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