job_request.rs (5189B)
1 #[path = "../src/test_fixtures.rs"] 2 mod test_fixtures; 3 4 use radroots_events::job::JobInputType; 5 use radroots_events::job_request::{RadrootsJobInput, RadrootsJobParam, RadrootsJobRequest}; 6 use radroots_events::kinds::{KIND_JOB_FEEDBACK, KIND_JOB_REQUEST_MIN, KIND_JOB_RESULT_MIN}; 7 use radroots_events_codec::job::encode::JobEncodeError; 8 use radroots_events_codec::job::error::JobParseError; 9 use radroots_events_codec::job::request::decode::{job_request_from_tags, parsed_from_event}; 10 use radroots_events_codec::job::request::encode::to_wire_parts; 11 use test_fixtures::{APP_PRIMARY_HTTPS, RELAY_PRIMARY_WSS}; 12 13 fn sample_request() -> RadrootsJobRequest { 14 RadrootsJobRequest { 15 kind: (KIND_JOB_REQUEST_MIN + 1) as u16, 16 inputs: vec![RadrootsJobInput { 17 data: APP_PRIMARY_HTTPS.to_string(), 18 input_type: JobInputType::Url, 19 relay: Some(RELAY_PRIMARY_WSS.to_string()), 20 marker: Some("source".to_string()), 21 }], 22 output: Some("json".to_string()), 23 params: vec![RadrootsJobParam { 24 key: "foo".to_string(), 25 value: "bar".to_string(), 26 }], 27 bid_sat: Some(250), 28 relays: vec![RELAY_PRIMARY_WSS.to_string()], 29 providers: vec!["provider".to_string()], 30 topics: vec!["topic".to_string()], 31 encrypted: false, 32 } 33 } 34 35 #[test] 36 fn job_request_roundtrip_from_tags() { 37 let req = sample_request(); 38 let parts = to_wire_parts(&req, "payload").unwrap(); 39 40 let decoded = job_request_from_tags(parts.kind, &parts.tags).unwrap(); 41 assert_eq!(decoded, req); 42 } 43 44 #[test] 45 fn job_request_requires_valid_kind() { 46 let mut req = sample_request(); 47 req.kind = KIND_JOB_FEEDBACK as u16; 48 49 let err = to_wire_parts(&req, "payload").unwrap_err(); 50 assert!(matches!( 51 err, 52 JobEncodeError::InvalidKind(KIND_JOB_FEEDBACK) 53 )); 54 } 55 56 #[test] 57 fn job_request_requires_providers_when_encrypted() { 58 let mut req = sample_request(); 59 req.encrypted = true; 60 req.providers.clear(); 61 62 let err = to_wire_parts(&req, "payload").unwrap_err(); 63 assert!(matches!(err, JobEncodeError::MissingProvidersForEncrypted)); 64 65 let tags = vec![vec!["encrypted".to_string()]]; 66 let err = job_request_from_tags(KIND_JOB_REQUEST_MIN + 1, &tags).unwrap_err(); 67 assert!(matches!(err, JobParseError::MissingTag("p"))); 68 } 69 70 #[test] 71 fn job_request_from_tags_accepts_encrypted_with_provider() { 72 let request = job_request_from_tags( 73 KIND_JOB_REQUEST_MIN + 1, 74 &[ 75 vec!["encrypted".to_string()], 76 vec!["p".to_string(), "provider".to_string()], 77 ], 78 ) 79 .unwrap(); 80 assert!(request.encrypted); 81 assert_eq!(request.providers, vec!["provider".to_string()]); 82 } 83 84 #[test] 85 fn job_request_to_wire_parts_allows_encrypted_when_provider_present() { 86 let mut req = sample_request(); 87 req.encrypted = true; 88 let parts = to_wire_parts(&req, "payload").unwrap(); 89 assert!( 90 parts 91 .tags 92 .iter() 93 .any(|tag| tag.first().map(|v| v.as_str()) == Some("encrypted")) 94 ); 95 } 96 97 #[test] 98 fn job_request_from_tags_rejects_invalid_bid_tag() { 99 let err = job_request_from_tags( 100 KIND_JOB_REQUEST_MIN + 1, 101 &[vec!["bid".to_string(), "not-a-number".to_string()]], 102 ) 103 .unwrap_err(); 104 assert!(matches!(err, JobParseError::InvalidNumber("bid", _))); 105 } 106 107 #[test] 108 fn job_request_metadata_rejects_wrong_kind() { 109 let err = radroots_events_codec::job::request::decode::data_from_event( 110 "id".to_string(), 111 "author".to_string(), 112 1, 113 KIND_JOB_RESULT_MIN, 114 Vec::new(), 115 ) 116 .unwrap_err(); 117 118 assert!(matches!( 119 err, 120 JobParseError::InvalidTag("kind (expected 5000-5999)") 121 )); 122 } 123 124 #[test] 125 fn job_request_data_from_event_success_path() { 126 let request = sample_request(); 127 let parts = to_wire_parts(&request, "payload").expect("wire parts"); 128 let data = radroots_events_codec::job::request::decode::data_from_event( 129 "id".to_string(), 130 "author".to_string(), 131 1, 132 parts.kind, 133 parts.tags, 134 ) 135 .expect("job request data"); 136 assert_eq!(data.id, "id"); 137 assert_eq!(data.author, "author"); 138 assert_eq!(data.kind, KIND_JOB_REQUEST_MIN + 1); 139 assert_eq!(data.data.providers, vec!["provider".to_string()]); 140 } 141 142 #[test] 143 fn job_request_data_from_event_propagates_decode_errors_with_valid_kind() { 144 let err = radroots_events_codec::job::request::decode::data_from_event( 145 "id".to_string(), 146 "author".to_string(), 147 1, 148 KIND_JOB_REQUEST_MIN + 1, 149 vec![vec!["bid".to_string(), "not-a-number".to_string()]], 150 ) 151 .unwrap_err(); 152 assert!(matches!(err, JobParseError::InvalidNumber("bid", _))); 153 } 154 155 #[test] 156 fn job_request_index_from_event_propagates_parse_errors() { 157 let err = parsed_from_event( 158 "id".to_string(), 159 "author".to_string(), 160 1, 161 KIND_JOB_RESULT_MIN, 162 "payload".to_string(), 163 Vec::new(), 164 "sig".to_string(), 165 ) 166 .unwrap_err(); 167 assert!(matches!( 168 err, 169 JobParseError::InvalidTag("kind (expected 5000-5999)") 170 )); 171 }