mod.rs (14313B)
1 pub mod decode; 2 pub mod encode; 3 pub mod list_sets; 4 5 #[cfg(test)] 6 mod tests { 7 use crate::error::EventEncodeError; 8 #[cfg(feature = "serde_json")] 9 use crate::farm::decode::farm_from_event; 10 use crate::farm::encode::{farm_build_tags, farm_ref_tags}; 11 use crate::farm::list_sets::{ 12 farm_listings_list_set_from_listings, farm_members_list_set, 13 farm_plots_list_set_from_plots, member_of_farms_list_set, 14 }; 15 use radroots_core::{ 16 RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity, 17 RadrootsCoreQuantityPrice, RadrootsCoreUnit, 18 }; 19 use radroots_events::farm::{ 20 RadrootsFarm, RadrootsFarmLocation, RadrootsFarmRef, RadrootsGcsLocation, 21 RadrootsGeoJsonPoint, RadrootsGeoJsonPolygon, 22 }; 23 use radroots_events::ids::{RadrootsDTag, RadrootsInventoryBinId}; 24 #[cfg(feature = "serde_json")] 25 use radroots_events::kinds::KIND_FARM; 26 use radroots_events::listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct}; 27 use radroots_events::plot::RadrootsPlot; 28 29 fn d_tag(raw: &str) -> RadrootsDTag { 30 raw.parse().unwrap() 31 } 32 33 fn bin_id(raw: &str) -> RadrootsInventoryBinId { 34 raw.parse().unwrap() 35 } 36 37 #[test] 38 fn farm_tags_include_required_fields() { 39 let farm = RadrootsFarm { 40 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 41 name: "Test Farm".to_string(), 42 about: None, 43 website: None, 44 picture: None, 45 banner: None, 46 location: Some(RadrootsFarmLocation { 47 primary: None, 48 city: None, 49 region: None, 50 country: None, 51 gcs: Some(RadrootsGcsLocation { 52 lat: 37.0, 53 lng: -122.0, 54 geohash: "9q8yy".to_string(), 55 point: RadrootsGeoJsonPoint { 56 r#type: "Point".to_string(), 57 coordinates: [-122.0, 37.0], 58 }, 59 polygon: RadrootsGeoJsonPolygon { 60 r#type: "Polygon".to_string(), 61 coordinates: vec![vec![ 62 [-122.0, 37.0], 63 [-122.0, 37.0001], 64 [-122.0001, 37.0001], 65 [-122.0, 37.0], 66 ]], 67 }, 68 accuracy: None, 69 altitude: None, 70 tag_0: None, 71 label: None, 72 area: None, 73 elevation: None, 74 soil: None, 75 climate: None, 76 gc_id: None, 77 gc_name: None, 78 gc_admin1_id: None, 79 gc_admin1_name: None, 80 gc_country_id: None, 81 gc_country_name: None, 82 }), 83 }), 84 tags: Some(vec!["orchard".to_string()]), 85 }; 86 87 let tags = farm_build_tags(&farm).expect("tags"); 88 assert!(tags.iter().any(|tag| tag.get(0) == Some(&"d".to_string()))); 89 assert!(tags.iter().any(|tag| tag.get(0) == Some(&"t".to_string()))); 90 assert!(tags.iter().any(|tag| tag.get(0) == Some(&"g".to_string()))); 91 } 92 93 #[test] 94 fn farm_tags_allow_missing_optional_fields() { 95 let farm = RadrootsFarm { 96 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 97 name: "Test Farm".to_string(), 98 about: None, 99 website: None, 100 picture: None, 101 banner: None, 102 location: None, 103 tags: None, 104 }; 105 106 let tags = farm_build_tags(&farm).expect("tags without optional fields"); 107 assert!( 108 tags.iter() 109 .any(|tag| tag.get(0).map(|v| v.as_str()) == Some("d")) 110 ); 111 assert!( 112 !tags 113 .iter() 114 .any(|tag| tag.get(0).map(|v| v.as_str()) == Some("t")) 115 ); 116 assert!( 117 !tags 118 .iter() 119 .any(|tag| tag.get(0).map(|v| v.as_str()) == Some("g")) 120 ); 121 } 122 123 #[test] 124 fn farm_build_tags_rejects_invalid_d_tag() { 125 let farm = RadrootsFarm { 126 d_tag: "farm:invalid".to_string(), 127 name: "Test Farm".to_string(), 128 about: None, 129 website: None, 130 picture: None, 131 banner: None, 132 location: None, 133 tags: None, 134 }; 135 136 let err = farm_build_tags(&farm).expect_err("expected invalid d_tag"); 137 assert!(matches!(err, EventEncodeError::InvalidField("d_tag"))); 138 } 139 140 #[test] 141 #[cfg(feature = "serde_json")] 142 fn farm_decode_rejects_empty_d_tag_and_content() { 143 let farm = RadrootsFarm { 144 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 145 name: "Test Farm".to_string(), 146 about: None, 147 website: None, 148 picture: None, 149 banner: None, 150 location: None, 151 tags: None, 152 }; 153 let content = serde_json::to_string(&farm).expect("farm content"); 154 155 let empty_d = farm_from_event( 156 KIND_FARM, 157 &[vec!["d".to_string(), " ".to_string()]], 158 &content, 159 ) 160 .expect_err("empty d tag"); 161 assert!(matches!( 162 empty_d, 163 crate::error::EventParseError::InvalidTag("d") 164 )); 165 166 let empty_content = farm_from_event( 167 KIND_FARM, 168 &[vec!["d".to_string(), "AAAAAAAAAAAAAAAAAAAAAA".to_string()]], 169 " ", 170 ) 171 .expect_err("empty content"); 172 assert!(matches!( 173 empty_content, 174 crate::error::EventParseError::InvalidJson("content") 175 )); 176 } 177 178 #[test] 179 fn farm_ref_tags_include_p_and_a() { 180 let farm = RadrootsFarmRef { 181 pubkey: "farm_pubkey".to_string(), 182 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 183 }; 184 185 let tags = farm_ref_tags(&farm).expect("farm ref tags"); 186 let has_a = tags 187 .iter() 188 .any(|tag| tag.get(0).map(|v| v.as_str()) == Some("a")); 189 let has_p = tags 190 .iter() 191 .any(|tag| tag.get(0).map(|v| v.as_str()) == Some("p")); 192 assert!(has_a); 193 assert!(has_p); 194 195 let err = farm_ref_tags(&RadrootsFarmRef { 196 pubkey: "farm_pubkey".to_string(), 197 d_tag: "invalid".to_string(), 198 }) 199 .expect_err("expected invalid farm.d_tag"); 200 assert!(matches!(err, EventEncodeError::InvalidField("farm.d_tag"))); 201 } 202 203 #[test] 204 fn farm_encode_rejects_empty_required_fields() { 205 let mut farm = RadrootsFarm { 206 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 207 name: "Test Farm".to_string(), 208 about: None, 209 website: None, 210 picture: None, 211 banner: None, 212 location: Some(RadrootsFarmLocation { 213 primary: None, 214 city: None, 215 region: None, 216 country: None, 217 gcs: Some(RadrootsGcsLocation { 218 lat: 37.0, 219 lng: -122.0, 220 geohash: "9q8yy".to_string(), 221 point: RadrootsGeoJsonPoint { 222 r#type: "Point".to_string(), 223 coordinates: [-122.0, 37.0], 224 }, 225 polygon: RadrootsGeoJsonPolygon { 226 r#type: "Polygon".to_string(), 227 coordinates: vec![vec![ 228 [-122.0, 37.0], 229 [-122.0, 37.0001], 230 [-122.0001, 37.0001], 231 [-122.0, 37.0], 232 ]], 233 }, 234 accuracy: None, 235 altitude: None, 236 tag_0: None, 237 label: None, 238 area: None, 239 elevation: None, 240 soil: None, 241 climate: None, 242 gc_id: None, 243 gc_name: None, 244 gc_admin1_id: None, 245 gc_admin1_name: None, 246 gc_country_id: None, 247 gc_country_name: None, 248 }), 249 }), 250 tags: None, 251 }; 252 253 farm.d_tag = " ".to_string(); 254 let err = farm_build_tags(&farm).expect_err("expected empty d_tag"); 255 assert!(matches!(err, EventEncodeError::EmptyRequiredField("d_tag"))); 256 257 farm.d_tag = "AAAAAAAAAAAAAAAAAAAAAA".to_string(); 258 farm.name = " ".to_string(); 259 let err = farm_build_tags(&farm).expect_err("expected empty name"); 260 assert!(matches!(err, EventEncodeError::EmptyRequiredField("name"))); 261 262 farm.name = "Test Farm".to_string(); 263 farm.location 264 .as_mut() 265 .expect("location") 266 .gcs 267 .as_mut() 268 .expect("gcs") 269 .geohash = " ".to_string(); 270 let err = farm_build_tags(&farm).expect_err("expected empty geohash"); 271 assert!(matches!( 272 err, 273 EventEncodeError::EmptyRequiredField("location.gcs.geohash") 274 )); 275 276 farm.location.as_mut().expect("location").gcs = None; 277 let tags = farm_build_tags(&farm).expect("string-only farm location should be allowed"); 278 assert!( 279 !tags 280 .iter() 281 .any(|tag| tag.get(0).map(|v| v.as_str()) == Some("g")) 282 ); 283 284 let err = farm_ref_tags(&RadrootsFarmRef { 285 pubkey: " ".to_string(), 286 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 287 }) 288 .expect_err("expected empty farm.pubkey"); 289 assert!(matches!( 290 err, 291 EventEncodeError::EmptyRequiredField("farm.pubkey") 292 )); 293 294 let err = farm_ref_tags(&RadrootsFarmRef { 295 pubkey: "farm_pubkey".to_string(), 296 d_tag: " ".to_string(), 297 }) 298 .expect_err("expected empty farm.d_tag"); 299 assert!(matches!( 300 err, 301 EventEncodeError::EmptyRequiredField("farm.d_tag") 302 )); 303 } 304 305 #[test] 306 fn farm_list_sets_include_expected_tags() { 307 let members = farm_members_list_set("AAAAAAAAAAAAAAAAAAAAAA", ["owner_pubkey"]) 308 .expect("members list"); 309 assert_eq!(members.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:members"); 310 assert_eq!(members.entries.len(), 1); 311 assert_eq!(members.entries[0].tag, "p"); 312 313 let claims = member_of_farms_list_set(["farm_pubkey"]).expect("claims list"); 314 assert_eq!(claims.d_tag, "member_of.farms"); 315 assert_eq!(claims.entries.len(), 1); 316 assert_eq!(claims.entries[0].tag, "p"); 317 } 318 319 #[test] 320 fn farm_plots_list_set_uses_plot_addresses() { 321 let plots = vec![RadrootsPlot { 322 d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(), 323 farm: RadrootsFarmRef { 324 pubkey: "farm_pubkey".to_string(), 325 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 326 }, 327 name: "Plot 1".to_string(), 328 about: None, 329 location: None, 330 tags: None, 331 }]; 332 333 let plots_list = 334 farm_plots_list_set_from_plots("AAAAAAAAAAAAAAAAAAAAAA", "farm_pubkey", &plots) 335 .expect("plots list"); 336 assert_eq!(plots_list.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:plots"); 337 assert_eq!(plots_list.entries.len(), 1); 338 assert_eq!(plots_list.entries[0].tag, "a"); 339 assert_eq!( 340 plots_list.entries[0].values[0], 341 "30350:farm_pubkey:AAAAAAAAAAAAAAAAAAAAAQ" 342 ); 343 } 344 345 #[test] 346 fn farm_listings_list_set_uses_listing_addresses() { 347 let listings = vec![RadrootsListing { 348 d_tag: d_tag("AAAAAAAAAAAAAAAAAAAAAg"), 349 published_at: None, 350 farm: RadrootsFarmRef { 351 pubkey: "farm_pubkey".to_string(), 352 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 353 }, 354 product: RadrootsListingProduct { 355 key: "coffee".to_string(), 356 title: "Coffee".to_string(), 357 category: "coffee".to_string(), 358 summary: None, 359 process: None, 360 lot: None, 361 location: None, 362 profile: None, 363 year: None, 364 }, 365 primary_bin_id: bin_id("bin-1"), 366 bins: vec![RadrootsListingBin { 367 bin_id: bin_id("bin-1"), 368 quantity: RadrootsCoreQuantity::new( 369 RadrootsCoreDecimal::from(1u32), 370 RadrootsCoreUnit::Each, 371 ), 372 price_per_canonical_unit: RadrootsCoreQuantityPrice::new( 373 RadrootsCoreMoney::new( 374 RadrootsCoreDecimal::from(10u32), 375 RadrootsCoreCurrency::USD, 376 ), 377 RadrootsCoreQuantity::new( 378 RadrootsCoreDecimal::from(1u32), 379 RadrootsCoreUnit::Each, 380 ), 381 ), 382 display_amount: None, 383 display_unit: None, 384 display_label: None, 385 display_price: None, 386 display_price_unit: None, 387 }], 388 resource_area: None, 389 plot: None, 390 discounts: None, 391 inventory_available: None, 392 availability: None, 393 delivery_method: None, 394 location: None, 395 images: None, 396 }]; 397 398 let listings_list = farm_listings_list_set_from_listings( 399 "AAAAAAAAAAAAAAAAAAAAAA", 400 "farm_pubkey", 401 &listings, 402 ) 403 .expect("listings list"); 404 assert_eq!(listings_list.d_tag, "farm:AAAAAAAAAAAAAAAAAAAAAA:listings"); 405 assert_eq!(listings_list.entries.len(), 1); 406 assert_eq!(listings_list.entries[0].tag, "a"); 407 assert_eq!( 408 listings_list.entries[0].values[0], 409 "30402:farm_pubkey:AAAAAAAAAAAAAAAAAAAAAg" 410 ); 411 } 412 }