lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

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 }