lib

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

emit.rs (71346B)


      1 #[cfg(not(feature = "std"))]
      2 use alloc::format;
      3 #[cfg(not(feature = "std"))]
      4 use alloc::{
      5     collections::BTreeMap,
      6     string::{String, ToString},
      7     vec::Vec,
      8 };
      9 #[cfg(feature = "std")]
     10 use std::collections::BTreeMap;
     11 
     12 use crate::canonical::canonical_json_string;
     13 use crate::error::RadrootsReplicaEventsError;
     14 use crate::geo::{geojson_point_from_lat_lng, geojson_polygon_circle_wgs84};
     15 use crate::types::{
     16     RADROOTS_REPLICA_TRANSFER_VERSION, RadrootsReplicaEventDraft, RadrootsReplicaFarmSelector,
     17     RadrootsReplicaSyncBundle, RadrootsReplicaSyncOptions, RadrootsReplicaSyncRequest,
     18 };
     19 use radroots_event::envelope::kind::{KIND_FARM, KIND_LIST_SET_GENERIC, KIND_PLOT};
     20 use radroots_event::farm::change_set::{
     21     GcsLocation as EventGcsLocation, GeoJsonPoint, GeoJsonPolygon,
     22 };
     23 use radroots_event::farm::location::{has_textual_locality, is_public_geohash5};
     24 use radroots_event::farm::plot::Plot as EventPlot;
     25 use radroots_event::farm::{Farm as EventFarm, FarmPublicLocation, FarmRef};
     26 use radroots_event::wire::Nip01EventWireParts;
     27 use radroots_event_codec::encode::farm as farm_encode;
     28 use radroots_event_codec::encode::farm as farm_list_sets;
     29 use radroots_event_codec::encode::list_set as list_set_encode;
     30 use radroots_event_codec::encode::plot as plot_encode;
     31 use radroots_replica_schema::farm::{
     32     Farm, IFarmFieldsFilter, IFarmFindMany, IFarmFindOne, IFarmFindOneArgs,
     33 };
     34 use radroots_replica_schema::farm_gcs_location::{
     35     FarmGcsLocation, IFarmGcsLocationFieldsFilter, IFarmGcsLocationFindMany,
     36 };
     37 use radroots_replica_schema::farm_member::{
     38     FarmMember, IFarmMemberFieldsFilter, IFarmMemberFindMany,
     39 };
     40 use radroots_replica_schema::farm_member_claim::{
     41     FarmMemberClaim, IFarmMemberClaimFieldsFilter, IFarmMemberClaimFindMany,
     42 };
     43 use radroots_replica_schema::farm_tag::{IFarmTagFieldsFilter, IFarmTagFindMany};
     44 use radroots_replica_schema::gcs_location::{
     45     GcsLocation, GcsLocationQueryBindValues, IGcsLocationFindOne, IGcsLocationFindOneArgs,
     46 };
     47 use radroots_replica_schema::plot::{IPlotFieldsFilter, IPlotFindMany, Plot};
     48 use radroots_replica_schema::plot_gcs_location::{
     49     IPlotGcsLocationFieldsFilter, IPlotGcsLocationFindMany, PlotGcsLocation,
     50 };
     51 use radroots_replica_schema::plot_tag::{IPlotTagFieldsFilter, IPlotTagFindMany};
     52 use radroots_replica_store::{
     53     farm, farm_gcs_location, farm_member, farm_member_claim, farm_tag, gcs_location, plot,
     54     plot_gcs_location, plot_tag,
     55 };
     56 use radroots_sql_core::SqlExecutor;
     57 
     58 const ROLE_PRIMARY: &str = "primary";
     59 const ROLE_MEMBER: &str = "member";
     60 const ROLE_OWNER: &str = "owner";
     61 const ROLE_WORKER: &str = "worker";
     62 
     63 #[cfg(test)]
     64 pub(crate) mod failpoints {
     65     use std::cell::Cell;
     66 
     67     thread_local! {
     68         static FORCE_LIST_SET_TO_WIRE_ERROR: Cell<bool> = const { Cell::new(false) };
     69         static FORCE_GCS_LOCATION_TO_EVENT_ERROR: Cell<bool> = const { Cell::new(false) };
     70     }
     71 
     72     pub(crate) fn set_list_set_to_wire_error() {
     73         FORCE_LIST_SET_TO_WIRE_ERROR.with(|flag| flag.set(true));
     74     }
     75 
     76     pub(crate) fn take_list_set_to_wire_error() -> bool {
     77         FORCE_LIST_SET_TO_WIRE_ERROR.with(|flag| {
     78             let value = flag.get();
     79             flag.set(false);
     80             value
     81         })
     82     }
     83 
     84     pub(crate) fn set_gcs_location_to_event_error() {
     85         FORCE_GCS_LOCATION_TO_EVENT_ERROR.with(|flag| flag.set(true));
     86     }
     87 
     88     pub(crate) fn take_gcs_location_to_event_error() -> bool {
     89         FORCE_GCS_LOCATION_TO_EVENT_ERROR.with(|flag| {
     90             let value = flag.get();
     91             flag.set(false);
     92             value
     93         })
     94     }
     95 }
     96 
     97 /// Builds the full replica transfer bundle.
     98 ///
     99 /// Profile events are intentionally excluded. Ingested Profile rows are lossy
    100 /// projections and cannot prove author intent for a complete kind-0 snapshot.
    101 pub fn radroots_replica_sync_all(
    102     exec: &dyn SqlExecutor,
    103     request: &RadrootsReplicaSyncRequest,
    104 ) -> Result<RadrootsReplicaSyncBundle, RadrootsReplicaEventsError> {
    105     radroots_replica_sync_all_with_options(exec, &request.farm, request.options.as_ref())
    106 }
    107 
    108 /// Builds a replica transfer bundle using explicit inclusion options.
    109 ///
    110 /// Profile events are intentionally excluded from replica emission.
    111 pub fn radroots_replica_sync_all_with_options(
    112     exec: &dyn SqlExecutor,
    113     farm_selector: &RadrootsReplicaFarmSelector,
    114     options: Option<&RadrootsReplicaSyncOptions>,
    115 ) -> Result<RadrootsReplicaSyncBundle, RadrootsReplicaEventsError> {
    116     let farm = resolve_farm(exec, farm_selector)?;
    117     let include_list_sets = options
    118         .and_then(|opt| opt.include_list_sets)
    119         .unwrap_or(true);
    120     let include_claims = options
    121         .and_then(|opt| opt.include_membership_claims)
    122         .unwrap_or(true);
    123 
    124     let mut events = Vec::new();
    125 
    126     events.push(radroots_replica_farm_event(exec, &farm)?);
    127 
    128     let plots = radroots_replica_plot_events(exec, &farm)?;
    129     events.extend(plots);
    130 
    131     if include_list_sets {
    132         let list_sets = radroots_replica_list_set_events(exec, &farm)?;
    133         events.extend(list_sets);
    134     }
    135 
    136     if include_claims {
    137         let claims = radroots_replica_membership_claim_events(exec, &farm.pubkey)?;
    138         events.extend(claims);
    139     }
    140 
    141     Ok(RadrootsReplicaSyncBundle {
    142         version: RADROOTS_REPLICA_TRANSFER_VERSION,
    143         events,
    144     })
    145 }
    146 
    147 pub fn radroots_replica_farm_event(
    148     exec: &dyn SqlExecutor,
    149     farm: &Farm,
    150 ) -> Result<RadrootsReplicaEventDraft, RadrootsReplicaEventsError> {
    151     let tags = collect_farm_tags(exec, &farm.id)?;
    152     let location = load_farm_location(exec, farm)?;
    153     let farm_event = EventFarm {
    154         d_tag: farm.d_tag.clone(),
    155         name: farm.name.clone(),
    156         about: farm.about.clone(),
    157         website: farm.website.clone(),
    158         picture: farm.picture.clone(),
    159         banner: farm.banner.clone(),
    160         location,
    161         tags: if tags.is_empty() { None } else { Some(tags) },
    162     };
    163     let tags = farm_encode::farm_build_tags(&farm_event)?;
    164     let content = canonical_json_string(&farm_event)?;
    165     let parts = Nip01EventWireParts {
    166         kind: KIND_FARM,
    167         content,
    168         tags,
    169     };
    170     Ok(parts_to_draft(&farm.pubkey, parts))
    171 }
    172 
    173 pub fn radroots_replica_plot_events(
    174     exec: &dyn SqlExecutor,
    175     farm: &Farm,
    176 ) -> Result<Vec<RadrootsReplicaEventDraft>, RadrootsReplicaEventsError> {
    177     let plots = load_plots(exec, &farm.id)?;
    178     let mut events = Vec::new();
    179     for plot_row in plots {
    180         let tags = collect_plot_tags(exec, &plot_row.id)?;
    181         let location = load_plot_location(exec, &plot_row)?;
    182         let plot_event = EventPlot {
    183             d_tag: plot_row.d_tag.clone(),
    184             farm: FarmRef {
    185                 pubkey: farm.pubkey.clone(),
    186                 d_tag: farm.d_tag.clone(),
    187             },
    188             name: plot_row.name.clone(),
    189             about: plot_row.about.clone(),
    190             location,
    191             tags: if tags.is_empty() { None } else { Some(tags) },
    192         };
    193         let tags = plot_encode::plot_build_tags(&plot_event)?;
    194         let content = canonical_json_string(&plot_event)?;
    195         let parts = Nip01EventWireParts {
    196             kind: KIND_PLOT,
    197             content,
    198             tags,
    199         };
    200         events.push(parts_to_draft(&farm.pubkey, parts));
    201     }
    202     Ok(events)
    203 }
    204 
    205 pub fn radroots_replica_list_set_events(
    206     exec: &dyn SqlExecutor,
    207     farm: &Farm,
    208 ) -> Result<Vec<RadrootsReplicaEventDraft>, RadrootsReplicaEventsError> {
    209     let members = load_farm_members(exec, &farm.id)?;
    210     let plots = load_plots(exec, &farm.id)?;
    211 
    212     let members_list =
    213         farm_list_sets::farm_members_list_set(&farm.d_tag, role_pubkeys(&members, ROLE_MEMBER))?;
    214     let owners_list =
    215         farm_list_sets::farm_owners_list_set(&farm.d_tag, role_pubkeys(&members, ROLE_OWNER))?;
    216     let workers_list =
    217         farm_list_sets::farm_workers_list_set(&farm.d_tag, role_pubkeys(&members, ROLE_WORKER))?;
    218 
    219     let plot_ids = sorted_plot_ids(&plots);
    220     let plots_list = farm_list_sets::farm_plots_list_set(&farm.d_tag, &farm.pubkey, plot_ids)?;
    221 
    222     let list_sets = [members_list, owners_list, workers_list, plots_list];
    223     let mut events = Vec::new();
    224     for list_set in list_sets {
    225         let parts = list_set_to_wire_parts(&list_set)?;
    226         events.push(parts_to_draft(&farm.pubkey, parts));
    227     }
    228     Ok(events)
    229 }
    230 
    231 pub fn radroots_replica_membership_claim_events(
    232     exec: &dyn SqlExecutor,
    233     farm_pubkey: &str,
    234 ) -> Result<Vec<RadrootsReplicaEventDraft>, RadrootsReplicaEventsError> {
    235     let claims = load_member_claims(exec, farm_pubkey)?;
    236     let mut by_member: BTreeMap<String, Vec<String>> = BTreeMap::new();
    237     for claim in claims {
    238         by_member
    239             .entry(claim.member_pubkey.clone())
    240             .or_default()
    241             .push(claim.farm_pubkey.clone());
    242     }
    243 
    244     let mut events = Vec::new();
    245     for member_pubkey in by_member.keys() {
    246         let all_claims = load_member_claims_for_member(exec, member_pubkey)?;
    247         let mut farm_pubkeys = all_claims
    248             .into_iter()
    249             .map(|claim| claim.farm_pubkey)
    250             .collect::<Vec<String>>();
    251         farm_pubkeys.sort();
    252         farm_pubkeys.dedup();
    253         let list_set = farm_list_sets::member_of_farms_list_set(farm_pubkeys)?;
    254         let parts = list_set_to_wire_parts(&list_set)?;
    255         events.push(parts_to_draft(member_pubkey, parts));
    256     }
    257 
    258     Ok(events)
    259 }
    260 
    261 fn resolve_farm(
    262     exec: &dyn SqlExecutor,
    263     selector: &RadrootsReplicaFarmSelector,
    264 ) -> Result<Farm, RadrootsReplicaEventsError> {
    265     if let Some(id) = selector.id.as_ref().filter(|v| !v.trim().is_empty()) {
    266         let result_query = farm::find_one(
    267             exec,
    268             &IFarmFindOne::On(IFarmFindOneArgs {
    269                 on: radroots_replica_schema::farm::FarmQueryBindValues::Id { id: id.clone() },
    270             }),
    271         );
    272         let result = result_query?;
    273         return result.result.ok_or_else(|| {
    274             RadrootsReplicaEventsError::InvalidSelector(format!("farm not found: {id}"))
    275         });
    276     }
    277 
    278     let d_tag = selector
    279         .d_tag
    280         .as_ref()
    281         .map(|v| v.trim())
    282         .filter(|v| !v.is_empty());
    283     let pubkey = selector
    284         .pubkey
    285         .as_ref()
    286         .map(|v| v.trim())
    287         .filter(|v| !v.is_empty());
    288 
    289     let (d_tag, pubkey) = match (d_tag, pubkey) {
    290         (Some(d_tag), Some(pubkey)) => (d_tag, pubkey),
    291         _ => {
    292             return Err(RadrootsReplicaEventsError::InvalidSelector(
    293                 "farm selector requires id or (d_tag + pubkey)".to_string(),
    294             ));
    295         }
    296     };
    297 
    298     let filter = IFarmFieldsFilter {
    299         id: None,
    300         created_at: None,
    301         updated_at: None,
    302         d_tag: Some(d_tag.to_string()),
    303         pubkey: Some(pubkey.to_string()),
    304         name: None,
    305         about: None,
    306         website: None,
    307         picture: None,
    308         banner: None,
    309         location_primary: None,
    310         location_city: None,
    311         location_region: None,
    312         location_country: None,
    313     };
    314     let result_query = farm::find_many(
    315         exec,
    316         &IFarmFindMany {
    317             filter: Some(filter),
    318         },
    319     );
    320     let result = result_query?;
    321     if result.results.len() == 1 {
    322         return Ok(result.results.into_iter().next().expect("farm result"));
    323     }
    324     Err(RadrootsReplicaEventsError::InvalidSelector(
    325         "farm selector did not resolve to a single farm".to_string(),
    326     ))
    327 }
    328 
    329 fn collect_farm_tags(
    330     exec: &dyn SqlExecutor,
    331     farm_id: &str,
    332 ) -> Result<Vec<String>, RadrootsReplicaEventsError> {
    333     let filter = IFarmTagFieldsFilter {
    334         id: None,
    335         created_at: None,
    336         updated_at: None,
    337         farm_id: Some(farm_id.to_string()),
    338         tag: None,
    339     };
    340     let result_query = farm_tag::find_many(
    341         exec,
    342         &IFarmTagFindMany {
    343             filter: Some(filter),
    344         },
    345     );
    346     let result = result_query?;
    347     let mut tags = result
    348         .results
    349         .into_iter()
    350         .map(|row| row.tag)
    351         .collect::<Vec<_>>();
    352     tags.sort();
    353     tags.dedup();
    354     Ok(tags)
    355 }
    356 
    357 fn collect_plot_tags(
    358     exec: &dyn SqlExecutor,
    359     plot_id: &str,
    360 ) -> Result<Vec<String>, RadrootsReplicaEventsError> {
    361     let filter = IPlotTagFieldsFilter {
    362         id: None,
    363         created_at: None,
    364         updated_at: None,
    365         plot_id: Some(plot_id.to_string()),
    366         tag: None,
    367     };
    368     let result_query = plot_tag::find_many(
    369         exec,
    370         &IPlotTagFindMany {
    371             filter: Some(filter),
    372         },
    373     );
    374     let result = result_query?;
    375     let mut tags = result
    376         .results
    377         .into_iter()
    378         .map(|row| row.tag)
    379         .collect::<Vec<_>>();
    380     tags.sort();
    381     tags.dedup();
    382     Ok(tags)
    383 }
    384 
    385 fn load_farm_members(
    386     exec: &dyn SqlExecutor,
    387     farm_id: &str,
    388 ) -> Result<Vec<FarmMember>, RadrootsReplicaEventsError> {
    389     let filter = IFarmMemberFieldsFilter {
    390         id: None,
    391         created_at: None,
    392         updated_at: None,
    393         farm_id: Some(farm_id.to_string()),
    394         member_pubkey: None,
    395         role: None,
    396     };
    397     let result_query = farm_member::find_many(
    398         exec,
    399         &IFarmMemberFindMany {
    400             filter: Some(filter),
    401         },
    402     );
    403     let result = result_query?;
    404     Ok(result.results)
    405 }
    406 
    407 fn role_pubkeys(members: &[FarmMember], role: &str) -> Vec<String> {
    408     let mut values = members
    409         .iter()
    410         .filter(|member| member.role == role)
    411         .map(|member| member.member_pubkey.clone())
    412         .collect::<Vec<_>>();
    413     values.sort();
    414     values.dedup();
    415     values
    416 }
    417 
    418 fn sorted_plot_ids(plots: &[Plot]) -> Vec<String> {
    419     let mut ids = plots
    420         .iter()
    421         .map(|plot| plot.d_tag.clone())
    422         .collect::<Vec<_>>();
    423     ids.sort();
    424     ids.dedup();
    425     ids
    426 }
    427 
    428 fn load_plots(
    429     exec: &dyn SqlExecutor,
    430     farm_id: &str,
    431 ) -> Result<Vec<Plot>, RadrootsReplicaEventsError> {
    432     let filter = IPlotFieldsFilter {
    433         id: None,
    434         created_at: None,
    435         updated_at: None,
    436         d_tag: None,
    437         farm_id: Some(farm_id.to_string()),
    438         name: None,
    439         about: None,
    440         location_primary: None,
    441         location_city: None,
    442         location_region: None,
    443         location_country: None,
    444     };
    445     let result_query = plot::find_many(
    446         exec,
    447         &IPlotFindMany {
    448             filter: Some(filter),
    449         },
    450     );
    451     let result = result_query?;
    452     let mut plots = result.results;
    453     plots.sort_by(|a, b| a.d_tag.cmp(&b.d_tag));
    454     Ok(plots)
    455 }
    456 
    457 fn load_farm_location(
    458     exec: &dyn SqlExecutor,
    459     farm: &Farm,
    460 ) -> Result<Option<FarmPublicLocation>, RadrootsReplicaEventsError> {
    461     let Some(gcs) = load_gcs_location_for_farm(exec, &farm.id)? else {
    462         return Ok(None);
    463     };
    464     let Some(geohash) = gcs.geohash.get(..5).map(|value| value.to_ascii_lowercase()) else {
    465         return Ok(None);
    466     };
    467     if !is_public_geohash5(&geohash) {
    468         return Ok(None);
    469     }
    470     let primary = farm
    471         .location_primary
    472         .as_ref()
    473         .filter(|value| !value.trim().is_empty())
    474         .cloned()
    475         .or_else(|| gcs.gc_name.clone())
    476         .or_else(|| gcs.label.clone())
    477         .unwrap_or_else(|| geohash.clone());
    478     let city = farm.location_city.clone().or_else(|| gcs.gc_name.clone());
    479     let region = farm
    480         .location_region
    481         .clone()
    482         .or_else(|| gcs.gc_admin1_name.clone());
    483     let country = farm
    484         .location_country
    485         .clone()
    486         .or_else(|| gcs.gc_country_name.clone())
    487         .or_else(|| gcs.gc_country_id.clone());
    488     if !has_textual_locality(
    489         &primary,
    490         city.as_deref(),
    491         region.as_deref(),
    492         country.as_deref(),
    493     ) {
    494         return Ok(None);
    495     }
    496     Ok(Some(FarmPublicLocation {
    497         primary,
    498         city,
    499         region,
    500         country,
    501         geohash,
    502     }))
    503 }
    504 
    505 fn load_plot_location(
    506     exec: &dyn SqlExecutor,
    507     plot: &Plot,
    508 ) -> Result<Option<radroots_event::farm::plot::PlotLocation>, RadrootsReplicaEventsError> {
    509     let location = load_gcs_location_for_plot(exec, &plot.id)?;
    510     Ok(
    511         location.map(|gcs| radroots_event::farm::plot::PlotLocation {
    512             primary: plot.location_primary.clone(),
    513             city: plot.location_city.clone(),
    514             region: plot.location_region.clone(),
    515             country: plot.location_country.clone(),
    516             gcs,
    517         }),
    518     )
    519 }
    520 
    521 fn load_gcs_location_for_farm(
    522     exec: &dyn SqlExecutor,
    523     farm_id: &str,
    524 ) -> Result<Option<EventGcsLocation>, RadrootsReplicaEventsError> {
    525     let primary = load_relation_by_role(exec, farm_id, ROLE_PRIMARY, RelationType::Farm)?;
    526     match primary {
    527         Some(gcs) => Ok(Some(gcs)),
    528         None => load_relation_by_role(exec, farm_id, "", RelationType::Farm),
    529     }
    530 }
    531 
    532 fn load_gcs_location_for_plot(
    533     exec: &dyn SqlExecutor,
    534     plot_id: &str,
    535 ) -> Result<Option<EventGcsLocation>, RadrootsReplicaEventsError> {
    536     let primary = load_relation_by_role(exec, plot_id, ROLE_PRIMARY, RelationType::Plot)?;
    537     match primary {
    538         Some(gcs) => Ok(Some(gcs)),
    539         None => load_relation_by_role(exec, plot_id, "", RelationType::Plot),
    540     }
    541 }
    542 
    543 enum RelationType {
    544     Farm,
    545     Plot,
    546 }
    547 
    548 fn load_relation_by_role(
    549     exec: &dyn SqlExecutor,
    550     id: &str,
    551     role: &str,
    552     relation: RelationType,
    553 ) -> Result<Option<EventGcsLocation>, RadrootsReplicaEventsError> {
    554     let mut rels = match relation {
    555         RelationType::Farm => {
    556             let filter = IFarmGcsLocationFieldsFilter {
    557                 id: None,
    558                 created_at: None,
    559                 updated_at: None,
    560                 farm_id: Some(id.to_string()),
    561                 gcs_location_id: None,
    562                 role: if role.is_empty() {
    563                     None
    564                 } else {
    565                     Some(role.to_string())
    566                 },
    567             };
    568             let result_query = farm_gcs_location::find_many(
    569                 exec,
    570                 &IFarmGcsLocationFindMany {
    571                     filter: Some(filter),
    572                 },
    573             );
    574             let result = result_query?;
    575             result
    576                 .results
    577                 .into_iter()
    578                 .map(RelationRow::Farm)
    579                 .collect::<Vec<_>>()
    580         }
    581         RelationType::Plot => {
    582             let filter = IPlotGcsLocationFieldsFilter {
    583                 id: None,
    584                 created_at: None,
    585                 updated_at: None,
    586                 plot_id: Some(id.to_string()),
    587                 gcs_location_id: None,
    588                 role: if role.is_empty() {
    589                     None
    590                 } else {
    591                     Some(role.to_string())
    592                 },
    593             };
    594             let result_query = plot_gcs_location::find_many(
    595                 exec,
    596                 &IPlotGcsLocationFindMany {
    597                     filter: Some(filter),
    598                 },
    599             );
    600             let result = result_query?;
    601             result
    602                 .results
    603                 .into_iter()
    604                 .map(RelationRow::Plot)
    605                 .collect::<Vec<_>>()
    606         }
    607     };
    608 
    609     if rels.is_empty() {
    610         return Ok(None);
    611     }
    612 
    613     rels.sort_by(compare_relation_rows);
    614     let gcs_id = rels[0].gcs_location_id().to_string();
    615     let gcs_result = gcs_location::find_one(
    616         exec,
    617         &IGcsLocationFindOne::On(IGcsLocationFindOneArgs {
    618             on: GcsLocationQueryBindValues::Id { id: gcs_id },
    619         }),
    620     );
    621     let gcs = match gcs_result?.result {
    622         Some(gcs) => gcs,
    623         None => {
    624             return Err(RadrootsReplicaEventsError::InvalidData(
    625                 "gcs_location not found".to_string(),
    626             ));
    627         }
    628     };
    629     Ok(Some(gcs_location_to_event(&gcs)?))
    630 }
    631 
    632 fn compare_relation_rows(a: &RelationRow, b: &RelationRow) -> core::cmp::Ordering {
    633     let rank = location_role_rank(a.role()).cmp(&location_role_rank(b.role()));
    634     rank.then_with(|| a.gcs_location_id().cmp(b.gcs_location_id()))
    635 }
    636 
    637 fn list_set_to_wire_parts(
    638     list_set: &radroots_event::social::list_set::ListSet,
    639 ) -> Result<Nip01EventWireParts, RadrootsReplicaEventsError> {
    640     #[cfg(test)]
    641     if failpoints::take_list_set_to_wire_error() {
    642         return Err(RadrootsReplicaEventsError::InvalidData(
    643             "list_set_to_wire".to_string(),
    644         ));
    645     }
    646     Ok(list_set_encode::to_wire_parts_with_kind(
    647         list_set,
    648         KIND_LIST_SET_GENERIC,
    649     )?)
    650 }
    651 
    652 enum RelationRow {
    653     Farm(FarmGcsLocation),
    654     Plot(PlotGcsLocation),
    655 }
    656 
    657 impl RelationRow {
    658     fn gcs_location_id(&self) -> &str {
    659         match self {
    660             Self::Farm(row) => row.gcs_location_id.as_str(),
    661             Self::Plot(row) => row.gcs_location_id.as_str(),
    662         }
    663     }
    664 
    665     fn role(&self) -> &str {
    666         match self {
    667             Self::Farm(row) => row.role.as_str(),
    668             Self::Plot(row) => row.role.as_str(),
    669         }
    670     }
    671 }
    672 
    673 fn location_role_rank(role: &str) -> u8 {
    674     u8::from(role != ROLE_PRIMARY)
    675 }
    676 
    677 fn gcs_location_to_event(
    678     gcs: &GcsLocation,
    679 ) -> Result<EventGcsLocation, RadrootsReplicaEventsError> {
    680     #[cfg(test)]
    681     if failpoints::take_gcs_location_to_event_error() {
    682         return Err(RadrootsReplicaEventsError::InvalidData(
    683             "gcs_location_to_event".to_string(),
    684         ));
    685     }
    686     let point = parse_point(&gcs.point, gcs.lat, gcs.lng);
    687     let polygon = parse_polygon(&gcs.polygon, gcs.lat, gcs.lng);
    688     Ok(EventGcsLocation {
    689         lat: gcs.lat,
    690         lng: gcs.lng,
    691         geohash: gcs.geohash.clone(),
    692         point,
    693         polygon,
    694         accuracy: gcs.accuracy,
    695         altitude: gcs.altitude,
    696         tag_0: gcs.tag_0.clone(),
    697         label: gcs.label.clone(),
    698         area: gcs.area,
    699         elevation: gcs.elevation,
    700         soil: gcs.soil.clone(),
    701         climate: gcs.climate.clone(),
    702         gc_id: gcs.gc_id.clone(),
    703         gc_name: gcs.gc_name.clone(),
    704         gc_admin1_id: gcs.gc_admin1_id.clone(),
    705         gc_admin1_name: gcs.gc_admin1_name.clone(),
    706         gc_country_id: gcs.gc_country_id.clone(),
    707         gc_country_name: gcs.gc_country_name.clone(),
    708     })
    709 }
    710 
    711 fn parse_point(value: &str, lat: f64, lng: f64) -> GeoJsonPoint {
    712     if !value.trim().is_empty()
    713         && let Ok(parsed) = serde_json::from_str::<GeoJsonPoint>(value)
    714     {
    715         return parsed;
    716     }
    717     geojson_point_from_lat_lng(lat, lng)
    718 }
    719 
    720 fn parse_polygon(value: &str, lat: f64, lng: f64) -> GeoJsonPolygon {
    721     if !value.trim().is_empty()
    722         && let Ok(parsed) = serde_json::from_str::<GeoJsonPolygon>(value)
    723         && !parsed.coordinates.is_empty()
    724         && !parsed.coordinates[0].is_empty()
    725     {
    726         return parsed;
    727     }
    728     geojson_polygon_circle_wgs84(lat, lng, 100.0, 64)
    729 }
    730 
    731 fn load_member_claims(
    732     exec: &dyn SqlExecutor,
    733     farm_pubkey: &str,
    734 ) -> Result<Vec<FarmMemberClaim>, RadrootsReplicaEventsError> {
    735     let filter = IFarmMemberClaimFieldsFilter {
    736         id: None,
    737         created_at: None,
    738         updated_at: None,
    739         member_pubkey: None,
    740         farm_pubkey: Some(farm_pubkey.to_string()),
    741     };
    742     let result_query = farm_member_claim::find_many(
    743         exec,
    744         &IFarmMemberClaimFindMany {
    745             filter: Some(filter),
    746         },
    747     );
    748     let result = result_query?;
    749     Ok(result.results)
    750 }
    751 
    752 fn load_member_claims_for_member(
    753     exec: &dyn SqlExecutor,
    754     member_pubkey: &str,
    755 ) -> Result<Vec<FarmMemberClaim>, RadrootsReplicaEventsError> {
    756     let filter = IFarmMemberClaimFieldsFilter {
    757         id: None,
    758         created_at: None,
    759         updated_at: None,
    760         member_pubkey: Some(member_pubkey.to_string()),
    761         farm_pubkey: None,
    762     };
    763     let result_query = farm_member_claim::find_many(
    764         exec,
    765         &IFarmMemberClaimFindMany {
    766             filter: Some(filter),
    767         },
    768     );
    769     let result = result_query?;
    770     Ok(result.results)
    771 }
    772 
    773 fn parts_to_draft(author: &str, parts: Nip01EventWireParts) -> RadrootsReplicaEventDraft {
    774     RadrootsReplicaEventDraft {
    775         kind: parts.kind,
    776         author: author.to_string(),
    777         content: parts.content,
    778         tags: parts.tags,
    779     }
    780 }
    781 
    782 #[cfg(test)]
    783 mod tests {
    784     use super::*;
    785     use radroots_replica_schema::farm::{IFarmFields, IFarmFieldsFilter, IFarmFindMany};
    786     use radroots_replica_schema::farm_gcs_location::{
    787         IFarmGcsLocationFields, IFarmGcsLocationFindMany,
    788     };
    789     use radroots_replica_schema::farm_member::IFarmMemberFields;
    790     use radroots_replica_schema::farm_member_claim::IFarmMemberClaimFields;
    791     use radroots_replica_schema::farm_tag::IFarmTagFields;
    792     use radroots_replica_schema::gcs_location::IGcsLocationFields;
    793     use radroots_replica_schema::nostr_profile::INostrProfileFields;
    794     use radroots_replica_schema::plot::{IPlotFields, IPlotFindMany};
    795     use radroots_replica_schema::plot_gcs_location::{
    796         IPlotGcsLocationFields, IPlotGcsLocationFindMany,
    797     };
    798     use radroots_replica_schema::plot_tag::IPlotTagFields;
    799     use radroots_replica_store::{
    800         farm, farm_gcs_location, farm_member, farm_member_claim, farm_tag, gcs_location,
    801         migrations, nostr_profile, plot, plot_gcs_location, plot_tag,
    802     };
    803     use radroots_sql_core::{ExecOutcome, SqlError, SqlExecutor, SqlxSqliteExecutor};
    804 
    805     struct ErrorExecutor;
    806 
    807     impl SqlExecutor for ErrorExecutor {
    808         fn exec(&self, _sql: &str, _params_json: &str) -> Result<ExecOutcome, SqlError> {
    809             Err(SqlError::Internal)
    810         }
    811 
    812         fn query_raw(&self, _sql: &str, _params_json: &str) -> Result<String, SqlError> {
    813             Err(SqlError::Internal)
    814         }
    815 
    816         fn begin(&self) -> Result<(), SqlError> {
    817             Ok(())
    818         }
    819 
    820         fn commit(&self) -> Result<(), SqlError> {
    821             Ok(())
    822         }
    823 
    824         fn rollback(&self) -> Result<(), SqlError> {
    825             Ok(())
    826         }
    827     }
    828 
    829     struct QueryFailExecutor<'a> {
    830         inner: &'a SqlxSqliteExecutor,
    831         needle: &'static str,
    832         err: SqlError,
    833     }
    834 
    835     impl SqlExecutor for QueryFailExecutor<'_> {
    836         fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> {
    837             self.inner.exec(sql, params_json)
    838         }
    839 
    840         fn query_raw(&self, sql: &str, params_json: &str) -> Result<String, SqlError> {
    841             if sql.to_ascii_lowercase().contains(self.needle) {
    842                 return Err(self.err.clone());
    843             }
    844             self.inner.query_raw(sql, params_json)
    845         }
    846 
    847         fn begin(&self) -> Result<(), SqlError> {
    848             self.inner.begin()
    849         }
    850 
    851         fn commit(&self) -> Result<(), SqlError> {
    852             self.inner.commit()
    853         }
    854 
    855         fn rollback(&self) -> Result<(), SqlError> {
    856             self.inner.rollback()
    857         }
    858     }
    859 
    860     struct DuplicateFarmSelectorExecutor<'a> {
    861         inner: &'a SqlxSqliteExecutor,
    862         duplicated_rows_json: String,
    863     }
    864 
    865     impl SqlExecutor for DuplicateFarmSelectorExecutor<'_> {
    866         fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> {
    867             self.inner.exec(sql, params_json)
    868         }
    869 
    870         fn query_raw(&self, sql: &str, params_json: &str) -> Result<String, SqlError> {
    871             let _ = (sql, params_json);
    872             Ok(self.duplicated_rows_json.clone())
    873         }
    874 
    875         fn begin(&self) -> Result<(), SqlError> {
    876             self.inner.begin()
    877         }
    878 
    879         fn commit(&self) -> Result<(), SqlError> {
    880             self.inner.commit()
    881         }
    882 
    883         fn rollback(&self) -> Result<(), SqlError> {
    884             self.inner.rollback()
    885         }
    886     }
    887 
    888     fn seed(exec: &SqlxSqliteExecutor) -> (Farm, Plot, Plot) {
    889         migrations::run_all_up(exec).expect("migrations");
    890         let farm = farm::create(
    891             exec,
    892             &IFarmFields {
    893                 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
    894                 pubkey: "f".repeat(64),
    895                 name: "farm".to_string(),
    896                 about: Some("about".to_string()),
    897                 website: Some("https://farm.example.com".to_string()),
    898                 picture: Some("https://farm.example.com/p.png".to_string()),
    899                 banner: Some("https://farm.example.com/b.png".to_string()),
    900                 location_primary: Some("primary".to_string()),
    901                 location_city: Some("city".to_string()),
    902                 location_region: Some("region".to_string()),
    903                 location_country: Some("country".to_string()),
    904             },
    905         )
    906         .expect("farm")
    907         .result;
    908 
    909         let gcs_primary = gcs_location::create(
    910             exec,
    911             &IGcsLocationFields {
    912                 d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(),
    913                 lat: 10.0,
    914                 lng: 20.0,
    915                 geohash: "9q8yy".to_string(),
    916                 point: "{\"type\":\"Point\",\"coordinates\":[20.0,10.0]}".to_string(),
    917                 polygon:
    918                     "{\"type\":\"Polygon\",\"coordinates\":[[[20.0,10.0],[20.1,10.1],[19.9,10.1],[20.0,10.0]]]}".to_string(),
    919                 accuracy: None,
    920                 altitude: None,
    921                 tag_0: None,
    922                 label: None,
    923                 area: None,
    924                 elevation: None,
    925                 soil: None,
    926                 climate: None,
    927                 gc_id: None,
    928                 gc_name: None,
    929                 gc_admin1_id: None,
    930                 gc_admin1_name: None,
    931                 gc_country_id: None,
    932                 gc_country_name: None,
    933             },
    934         )
    935         .expect("gcs primary")
    936         .result;
    937         let gcs_secondary = gcs_location::create(
    938             exec,
    939             &IGcsLocationFields {
    940                 d_tag: "AAAAAAAAAAAAAAAAAAAAAw".to_string(),
    941                 lat: 11.0,
    942                 lng: 21.0,
    943                 geohash: "9q8yv".to_string(),
    944                 point: "{".to_string(),
    945                 polygon: "{\"type\":\"Polygon\",\"coordinates\":[[]]}".to_string(),
    946                 accuracy: None,
    947                 altitude: None,
    948                 tag_0: None,
    949                 label: None,
    950                 area: None,
    951                 elevation: None,
    952                 soil: None,
    953                 climate: None,
    954                 gc_id: None,
    955                 gc_name: None,
    956                 gc_admin1_id: None,
    957                 gc_admin1_name: None,
    958                 gc_country_id: None,
    959                 gc_country_name: None,
    960             },
    961         )
    962         .expect("gcs secondary")
    963         .result;
    964 
    965         let _ = farm_gcs_location::create(
    966             exec,
    967             &IFarmGcsLocationFields {
    968                 farm_id: farm.id.clone(),
    969                 gcs_location_id: gcs_secondary.id.clone(),
    970                 role: "".to_string(),
    971             },
    972         )
    973         .expect("farm gcs secondary");
    974         let _ = farm_gcs_location::create(
    975             exec,
    976             &IFarmGcsLocationFields {
    977                 farm_id: farm.id.clone(),
    978                 gcs_location_id: gcs_primary.id.clone(),
    979                 role: "primary".to_string(),
    980             },
    981         )
    982         .expect("farm gcs primary");
    983 
    984         let plot_primary = plot::create(
    985             exec,
    986             &IPlotFields {
    987                 d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(),
    988                 farm_id: farm.id.clone(),
    989                 name: "plot-primary".to_string(),
    990                 about: Some("plot about".to_string()),
    991                 location_primary: Some("plot primary".to_string()),
    992                 location_city: Some("plot city".to_string()),
    993                 location_region: Some("plot region".to_string()),
    994                 location_country: Some("plot country".to_string()),
    995             },
    996         )
    997         .expect("plot primary")
    998         .result;
    999         let plot_secondary = plot::create(
   1000             exec,
   1001             &IPlotFields {
   1002                 d_tag: "AAAAAAAAAAAAAAAAAAAAAg".to_string(),
   1003                 farm_id: farm.id.clone(),
   1004                 name: "plot-secondary".to_string(),
   1005                 about: Some("plot secondary about".to_string()),
   1006                 location_primary: Some("plot secondary primary".to_string()),
   1007                 location_city: None,
   1008                 location_region: None,
   1009                 location_country: None,
   1010             },
   1011         )
   1012         .expect("plot secondary")
   1013         .result;
   1014 
   1015         let _ = plot_gcs_location::create(
   1016             exec,
   1017             &IPlotGcsLocationFields {
   1018                 plot_id: plot_primary.id.clone(),
   1019                 gcs_location_id: gcs_secondary.id.clone(),
   1020                 role: "secondary".to_string(),
   1021             },
   1022         )
   1023         .expect("plot primary secondary relation");
   1024         let _ = plot_gcs_location::create(
   1025             exec,
   1026             &IPlotGcsLocationFields {
   1027                 plot_id: plot_primary.id.clone(),
   1028                 gcs_location_id: gcs_primary.id.clone(),
   1029                 role: "primary".to_string(),
   1030             },
   1031         )
   1032         .expect("plot primary relation");
   1033         let _ = plot_gcs_location::create(
   1034             exec,
   1035             &IPlotGcsLocationFields {
   1036                 plot_id: plot_secondary.id.clone(),
   1037                 gcs_location_id: gcs_secondary.id.clone(),
   1038                 role: "secondary".to_string(),
   1039             },
   1040         )
   1041         .expect("plot secondary relation");
   1042 
   1043         let _ = farm_tag::create(
   1044             exec,
   1045             &IFarmTagFields {
   1046                 farm_id: farm.id.clone(),
   1047                 tag: "coffee".to_string(),
   1048             },
   1049         )
   1050         .expect("farm tag");
   1051         let _ = plot_tag::create(
   1052             exec,
   1053             &IPlotTagFields {
   1054                 plot_id: plot_primary.id.clone(),
   1055                 tag: "orchard".to_string(),
   1056             },
   1057         )
   1058         .expect("plot tag");
   1059 
   1060         let _ = farm_member::create(
   1061             exec,
   1062             &IFarmMemberFields {
   1063                 farm_id: farm.id.clone(),
   1064                 member_pubkey: "6".repeat(64),
   1065                 role: "member".to_string(),
   1066             },
   1067         )
   1068         .expect("member");
   1069         let _ = farm_member::create(
   1070             exec,
   1071             &IFarmMemberFields {
   1072                 farm_id: farm.id.clone(),
   1073                 member_pubkey: "8".repeat(64),
   1074                 role: "owner".to_string(),
   1075             },
   1076         )
   1077         .expect("owner");
   1078         let _ = farm_member::create(
   1079             exec,
   1080             &IFarmMemberFields {
   1081                 farm_id: farm.id.clone(),
   1082                 member_pubkey: "e".repeat(64),
   1083                 role: "worker".to_string(),
   1084             },
   1085         )
   1086         .expect("worker");
   1087         let _ = farm_member::create(
   1088             exec,
   1089             &IFarmMemberFields {
   1090                 farm_id: farm.id.clone(),
   1091                 member_pubkey: "1".repeat(64),
   1092                 role: "member".to_string(),
   1093             },
   1094         )
   1095         .expect("member no profile");
   1096 
   1097         let _ = farm_member_claim::create(
   1098             exec,
   1099             &IFarmMemberClaimFields {
   1100                 member_pubkey: "6".repeat(64),
   1101                 farm_pubkey: farm.pubkey.clone(),
   1102             },
   1103         )
   1104         .expect("claim member");
   1105         let _ = farm_member_claim::create(
   1106             exec,
   1107             &IFarmMemberClaimFields {
   1108                 member_pubkey: "1".repeat(64),
   1109                 farm_pubkey: farm.pubkey.clone(),
   1110             },
   1111         )
   1112         .expect("claim member no profile");
   1113 
   1114         let _ = nostr_profile::create(
   1115             exec,
   1116             &INostrProfileFields {
   1117                 public_key: farm.pubkey.clone(),
   1118                 profile_type: "farm".to_string(),
   1119                 name: "farm profile".to_string(),
   1120                 display_name: None,
   1121                 about: None,
   1122                 website: None,
   1123                 picture: None,
   1124                 banner: None,
   1125                 nip05: None,
   1126                 lud06: None,
   1127                 lud16: None,
   1128             },
   1129         )
   1130         .expect("farm profile");
   1131         let _ = nostr_profile::create(
   1132             exec,
   1133             &INostrProfileFields {
   1134                 public_key: "6".repeat(64),
   1135                 profile_type: "legacy".to_string(),
   1136                 name: "member profile".to_string(),
   1137                 display_name: Some("member".to_string()),
   1138                 about: Some("about".to_string()),
   1139                 website: Some("https://member.example.com".to_string()),
   1140                 picture: Some("https://member.example.com/p.png".to_string()),
   1141                 banner: Some("https://member.example.com/b.png".to_string()),
   1142                 nip05: Some("member@example.com".to_string()),
   1143                 lud06: Some("lud06".to_string()),
   1144                 lud16: Some("lud16".to_string()),
   1145             },
   1146         )
   1147         .expect("member profile");
   1148 
   1149         (farm, plot_primary, plot_secondary)
   1150     }
   1151 
   1152     fn create_farm_record(
   1153         exec: &SqlxSqliteExecutor,
   1154         d_tag: &str,
   1155         pubkey: &str,
   1156         name: &str,
   1157     ) -> Farm {
   1158         farm::create(
   1159             exec,
   1160             &IFarmFields {
   1161                 d_tag: d_tag.to_string(),
   1162                 pubkey: pubkey.to_string(),
   1163                 name: name.to_string(),
   1164                 about: None,
   1165                 website: None,
   1166                 picture: None,
   1167                 banner: None,
   1168                 location_primary: None,
   1169                 location_city: None,
   1170                 location_region: None,
   1171                 location_country: None,
   1172             },
   1173         )
   1174         .expect("farm")
   1175         .result
   1176     }
   1177 
   1178     fn create_plot_record(exec: &SqlxSqliteExecutor, farm_id: &str, d_tag: &str, name: &str) {
   1179         let _ = plot::create(
   1180             exec,
   1181             &IPlotFields {
   1182                 d_tag: d_tag.to_string(),
   1183                 farm_id: farm_id.to_string(),
   1184                 name: name.to_string(),
   1185                 about: None,
   1186                 location_primary: None,
   1187                 location_city: None,
   1188                 location_region: None,
   1189                 location_country: None,
   1190             },
   1191         )
   1192         .expect("plot");
   1193     }
   1194 
   1195     fn add_member_record(
   1196         exec: &SqlxSqliteExecutor,
   1197         farm_id: &str,
   1198         member_pubkey: &str,
   1199         role: &str,
   1200     ) {
   1201         let _ = farm_member::create(
   1202             exec,
   1203             &IFarmMemberFields {
   1204                 farm_id: farm_id.to_string(),
   1205                 member_pubkey: member_pubkey.to_string(),
   1206                 role: role.to_string(),
   1207             },
   1208         )
   1209         .expect("member");
   1210     }
   1211 
   1212     #[test]
   1213     fn emit_paths_cover_private_and_public_helpers() {
   1214         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   1215         let (farm_row, plot_primary, plot_secondary) = seed(&exec);
   1216 
   1217         let by_id = resolve_farm(
   1218             &exec,
   1219             &RadrootsReplicaFarmSelector {
   1220                 id: Some(farm_row.id.clone()),
   1221                 d_tag: None,
   1222                 pubkey: None,
   1223             },
   1224         )
   1225         .expect("resolve by id");
   1226         assert_eq!(by_id.id, farm_row.id);
   1227 
   1228         assert!(
   1229             resolve_farm(
   1230                 &exec,
   1231                 &RadrootsReplicaFarmSelector {
   1232                     id: Some("00000000-0000-0000-0000-000000000000".to_string()),
   1233                     d_tag: None,
   1234                     pubkey: None,
   1235                 },
   1236             )
   1237             .is_err()
   1238         );
   1239         assert!(
   1240             radroots_replica_sync_all_with_options(
   1241                 &exec,
   1242                 &RadrootsReplicaFarmSelector {
   1243                     id: Some("00000000-0000-0000-0000-000000000000".to_string()),
   1244                     d_tag: None,
   1245                     pubkey: None,
   1246                 },
   1247                 None,
   1248             )
   1249             .is_err()
   1250         );
   1251         assert!(
   1252             resolve_farm(
   1253                 &exec,
   1254                 &RadrootsReplicaFarmSelector {
   1255                     id: None,
   1256                     d_tag: None,
   1257                     pubkey: None,
   1258                 },
   1259             )
   1260             .is_err()
   1261         );
   1262 
   1263         assert!(
   1264             farm::create(
   1265                 &exec,
   1266                 &IFarmFields {
   1267                     d_tag: farm_row.d_tag.clone(),
   1268                     pubkey: farm_row.pubkey.clone(),
   1269                     name: "duplicate".to_string(),
   1270                     about: None,
   1271                     website: None,
   1272                     picture: None,
   1273                     banner: None,
   1274                     location_primary: None,
   1275                     location_city: None,
   1276                     location_region: None,
   1277                     location_country: None,
   1278                 },
   1279             )
   1280             .is_err()
   1281         );
   1282         let by_identity = resolve_farm(
   1283             &exec,
   1284             &RadrootsReplicaFarmSelector {
   1285                 id: None,
   1286                 d_tag: Some(farm_row.d_tag.clone()),
   1287                 pubkey: Some(farm_row.pubkey.clone()),
   1288             },
   1289         )
   1290         .expect("resolve unique farm");
   1291         assert_eq!(by_identity.id, farm_row.id);
   1292 
   1293         let tags = collect_farm_tags(&exec, &farm_row.id).expect("farm tags");
   1294         assert_eq!(tags, vec!["coffee".to_string()]);
   1295         let plot_tags = collect_plot_tags(&exec, &plot_primary.id).expect("plot tags");
   1296         assert_eq!(plot_tags, vec!["orchard".to_string()]);
   1297 
   1298         let members = load_farm_members(&exec, &farm_row.id).expect("members");
   1299         assert_eq!(role_pubkeys(&members, ROLE_MEMBER).len(), 2);
   1300         assert_eq!(role_pubkeys(&members, ROLE_OWNER).len(), 1);
   1301         assert_eq!(role_pubkeys(&members, ROLE_WORKER).len(), 1);
   1302         let plots = load_plots(&exec, &farm_row.id).expect("plots");
   1303         assert_eq!(sorted_plot_ids(&plots).len(), 2);
   1304 
   1305         let farm_location = load_farm_location(&exec, &farm_row).expect("farm location");
   1306         assert!(farm_location.is_some());
   1307         let plot_location_primary = load_plot_location(&exec, &plot_primary).expect("plot primary");
   1308         assert!(plot_location_primary.is_some());
   1309         let plot_location_secondary =
   1310             load_plot_location(&exec, &plot_secondary).expect("plot secondary");
   1311         assert!(plot_location_secondary.is_some());
   1312 
   1313         assert!(
   1314             load_relation_by_role(&exec, &farm_row.id, "primary", RelationType::Farm)
   1315                 .expect("farm primary")
   1316                 .is_some()
   1317         );
   1318         assert!(
   1319             load_relation_by_role(&exec, &farm_row.id, "", RelationType::Farm)
   1320                 .expect("farm fallback")
   1321                 .is_some()
   1322         );
   1323         assert!(
   1324             load_relation_by_role(&exec, &plot_secondary.id, "", RelationType::Plot)
   1325                 .expect("plot fallback")
   1326                 .is_some()
   1327         );
   1328 
   1329         let mut farm_rel =
   1330             farm_gcs_location::find_many(&exec, &IFarmGcsLocationFindMany { filter: None })
   1331                 .expect("farm rels")
   1332                 .results;
   1333         let mut plot_rel =
   1334             plot_gcs_location::find_many(&exec, &IPlotGcsLocationFindMany { filter: None })
   1335                 .expect("plot rels")
   1336                 .results;
   1337         let farm_row_role = RelationRow::Farm(farm_rel.remove(0)).role().to_string();
   1338         let plot_row_role = RelationRow::Plot(plot_rel.remove(0)).role().to_string();
   1339         let _ = farm_row_role;
   1340         let _ = plot_row_role;
   1341         assert_eq!(location_role_rank(ROLE_PRIMARY), 0);
   1342         assert_eq!(location_role_rank("secondary"), 1);
   1343 
   1344         let point_valid = parse_point("{\"type\":\"Point\",\"coordinates\":[1.0,2.0]}", 3.0, 4.0);
   1345         assert_eq!(point_valid.coordinates, [1.0, 2.0]);
   1346         let point_invalid = parse_point("{", 3.0, 4.0);
   1347         assert_eq!(point_invalid.coordinates, [4.0, 3.0]);
   1348         let point_empty = parse_point("", 3.0, 4.0);
   1349         assert_eq!(point_empty.coordinates, [4.0, 3.0]);
   1350 
   1351         let polygon_valid = parse_polygon(
   1352             "{\"type\":\"Polygon\",\"coordinates\":[[[1.0,2.0],[1.1,2.1],[1.0,2.0]]]}",
   1353             3.0,
   1354             4.0,
   1355         );
   1356         assert!(!polygon_valid.coordinates[0].is_empty());
   1357         let polygon_empty_outer =
   1358             parse_polygon("{\"type\":\"Polygon\",\"coordinates\":[]}", 3.0, 4.0);
   1359         assert!(!polygon_empty_outer.coordinates[0].is_empty());
   1360         let polygon_empty_inner =
   1361             parse_polygon("{\"type\":\"Polygon\",\"coordinates\":[[]]}", 3.0, 4.0);
   1362         assert!(!polygon_empty_inner.coordinates[0].is_empty());
   1363         let polygon_invalid = parse_polygon("{", 3.0, 4.0);
   1364         assert!(!polygon_invalid.coordinates[0].is_empty());
   1365         let polygon_blank = parse_polygon("", 3.0, 4.0);
   1366         assert!(!polygon_blank.coordinates[0].is_empty());
   1367 
   1368         let claims = load_member_claims(&exec, &farm_row.pubkey).expect("claims");
   1369         assert!(!claims.is_empty());
   1370         let member_claims =
   1371             load_member_claims_for_member(&exec, &"6".repeat(64)).expect("claims by member");
   1372         assert!(!member_claims.is_empty());
   1373 
   1374         let farm_event = radroots_replica_farm_event(&exec, &farm_row).expect("farm event");
   1375         assert_eq!(farm_event.kind, KIND_FARM);
   1376         let plot_events = radroots_replica_plot_events(&exec, &farm_row).expect("plot events");
   1377         assert_eq!(plot_events.len(), 2);
   1378         let list_sets = radroots_replica_list_set_events(&exec, &farm_row).expect("list sets");
   1379         assert_eq!(list_sets.len(), 4);
   1380         let membership_claims =
   1381             radroots_replica_membership_claim_events(&exec, &farm_row.pubkey).expect("membership");
   1382         assert!(!membership_claims.is_empty());
   1383         let bundle = radroots_replica_sync_all_with_options(
   1384             &exec,
   1385             &RadrootsReplicaFarmSelector {
   1386                 id: Some(farm_row.id.clone()),
   1387                 d_tag: None,
   1388                 pubkey: None,
   1389             },
   1390             Some(&RadrootsReplicaSyncOptions {
   1391                 include_list_sets: Some(true),
   1392                 include_membership_claims: Some(true),
   1393             }),
   1394         )
   1395         .expect("sync all");
   1396         assert!(!bundle.events.is_empty());
   1397 
   1398         let _ = exec.exec("PRAGMA foreign_keys = OFF", "[]");
   1399         let _ = plot_gcs_location::create(
   1400             &exec,
   1401             &IPlotGcsLocationFields {
   1402                 plot_id: plot_secondary.id.clone(),
   1403                 gcs_location_id: "00000000-0000-0000-0000-000000000000".to_string(),
   1404                 role: "".to_string(),
   1405             },
   1406         );
   1407         assert!(load_relation_by_role(&exec, &plot_secondary.id, "", RelationType::Plot).is_err());
   1408 
   1409         let by_pair = farm::find_many(
   1410             &exec,
   1411             &IFarmFindMany {
   1412                 filter: Some(IFarmFieldsFilter {
   1413                     id: None,
   1414                     created_at: None,
   1415                     updated_at: None,
   1416                     d_tag: Some("AAAAAAAAAAAAAAAAAAAAAA".to_string()),
   1417                     pubkey: Some("f".repeat(64)),
   1418                     name: None,
   1419                     about: None,
   1420                     website: None,
   1421                     picture: None,
   1422                     banner: None,
   1423                     location_primary: None,
   1424                     location_city: None,
   1425                     location_region: None,
   1426                     location_country: None,
   1427                 }),
   1428             },
   1429         )
   1430         .expect("by pair");
   1431         assert!(!by_pair.results.is_empty());
   1432 
   1433         let plots_lookup = plot::find_many(
   1434             &exec,
   1435             &IPlotFindMany {
   1436                 filter: Some(IPlotFieldsFilter {
   1437                     id: None,
   1438                     created_at: None,
   1439                     updated_at: None,
   1440                     d_tag: None,
   1441                     farm_id: Some(farm_row.id),
   1442                     name: None,
   1443                     about: None,
   1444                     location_primary: None,
   1445                     location_city: None,
   1446                     location_region: None,
   1447                     location_country: None,
   1448                 }),
   1449             },
   1450         )
   1451         .expect("plots lookup");
   1452         assert_eq!(plots_lookup.results.len(), 2);
   1453     }
   1454 
   1455     #[test]
   1456     fn emit_option_toggles_and_empty_rows_cover_branches() {
   1457         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   1458         migrations::run_all_up(&exec).expect("migrations");
   1459 
   1460         let farm = farm::create(
   1461             &exec,
   1462             &IFarmFields {
   1463                 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
   1464                 pubkey: "b".repeat(64),
   1465                 name: "farm-empty".to_string(),
   1466                 about: None,
   1467                 website: None,
   1468                 picture: None,
   1469                 banner: None,
   1470                 location_primary: None,
   1471                 location_city: None,
   1472                 location_region: None,
   1473                 location_country: None,
   1474             },
   1475         )
   1476         .expect("farm")
   1477         .result;
   1478 
   1479         let _plot = plot::create(
   1480             &exec,
   1481             &IPlotFields {
   1482                 d_tag: "AAAAAAAAAAAAAAAAAAAAAQ".to_string(),
   1483                 farm_id: farm.id.clone(),
   1484                 name: "plot-empty".to_string(),
   1485                 about: None,
   1486                 location_primary: None,
   1487                 location_city: None,
   1488                 location_region: None,
   1489                 location_country: None,
   1490             },
   1491         )
   1492         .expect("plot");
   1493 
   1494         let selector = RadrootsReplicaFarmSelector {
   1495             id: Some(farm.id.clone()),
   1496             d_tag: None,
   1497             pubkey: None,
   1498         };
   1499         let bundle = radroots_replica_sync_all_with_options(
   1500             &exec,
   1501             &selector,
   1502             Some(&RadrootsReplicaSyncOptions {
   1503                 include_list_sets: Some(false),
   1504                 include_membership_claims: Some(false),
   1505             }),
   1506         )
   1507         .expect("sync");
   1508         assert_eq!(bundle.events.len(), 2);
   1509 
   1510         let farm_event = radroots_replica_farm_event(&exec, &farm).expect("farm event");
   1511         assert_eq!(farm_event.kind, KIND_FARM);
   1512         let plot_events = radroots_replica_plot_events(&exec, &farm).expect("plot events");
   1513         assert_eq!(plot_events.len(), 1);
   1514 
   1515         let claims =
   1516             radroots_replica_membership_claim_events(&exec, &"3".repeat(64)).expect("empty claims");
   1517         assert!(claims.is_empty());
   1518 
   1519         let by_pair = resolve_farm(
   1520             &exec,
   1521             &RadrootsReplicaFarmSelector {
   1522                 id: None,
   1523                 d_tag: Some(farm.d_tag.clone()),
   1524                 pubkey: Some(farm.pubkey.clone()),
   1525             },
   1526         )
   1527         .expect("resolve by pair");
   1528         assert_eq!(by_pair.id, farm.id);
   1529     }
   1530 
   1531     #[test]
   1532     fn emit_query_error_paths_are_reported() {
   1533         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   1534         migrations::run_all_up(&exec).expect("migrations");
   1535 
   1536         let farm = Farm {
   1537             id: "farm".to_string(),
   1538             created_at: "now".to_string(),
   1539             updated_at: "now".to_string(),
   1540             d_tag: "d".to_string(),
   1541             pubkey: "9".repeat(64),
   1542             name: "farm".to_string(),
   1543             about: None,
   1544             website: None,
   1545             picture: None,
   1546             banner: None,
   1547             location_primary: None,
   1548             location_city: None,
   1549             location_region: None,
   1550             location_country: None,
   1551         };
   1552         let plot = Plot {
   1553             id: "plot".to_string(),
   1554             created_at: "now".to_string(),
   1555             updated_at: "now".to_string(),
   1556             d_tag: "plot".to_string(),
   1557             farm_id: farm.id.clone(),
   1558             name: "plot".to_string(),
   1559             about: None,
   1560             location_primary: None,
   1561             location_city: None,
   1562             location_region: None,
   1563             location_country: None,
   1564         };
   1565 
   1566         let tags_fail = QueryFailExecutor {
   1567             inner: &exec,
   1568             needle: "farm_tag",
   1569             err: SqlError::Internal,
   1570         };
   1571         assert!(collect_farm_tags(&tags_fail, "id").is_err());
   1572 
   1573         let plot_tags_fail = QueryFailExecutor {
   1574             inner: &exec,
   1575             needle: "plot_tag",
   1576             err: SqlError::Internal,
   1577         };
   1578         assert!(collect_plot_tags(&plot_tags_fail, "id").is_err());
   1579 
   1580         let members_fail = QueryFailExecutor {
   1581             inner: &exec,
   1582             needle: "farm_member",
   1583             err: SqlError::Internal,
   1584         };
   1585         assert!(load_farm_members(&members_fail, "id").is_err());
   1586 
   1587         let plots_fail = QueryFailExecutor {
   1588             inner: &exec,
   1589             needle: "from plot",
   1590             err: SqlError::Internal,
   1591         };
   1592         assert!(load_plots(&plots_fail, "id").is_err());
   1593 
   1594         let farm_location_fail = QueryFailExecutor {
   1595             inner: &exec,
   1596             needle: "farm_gcs_location",
   1597             err: SqlError::Internal,
   1598         };
   1599         assert!(load_farm_location(&farm_location_fail, &farm).is_err());
   1600 
   1601         let plot_location_fail = QueryFailExecutor {
   1602             inner: &exec,
   1603             needle: "plot_gcs_location",
   1604             err: SqlError::Internal,
   1605         };
   1606         assert!(load_plot_location(&plot_location_fail, &plot).is_err());
   1607 
   1608         let claims_fail = QueryFailExecutor {
   1609             inner: &exec,
   1610             needle: "farm_member_claim",
   1611             err: SqlError::Internal,
   1612         };
   1613         assert!(load_member_claims(&claims_fail, "p").is_err());
   1614         assert!(load_member_claims_for_member(&claims_fail, "p").is_err());
   1615     }
   1616 
   1617     #[test]
   1618     fn load_farm_location_omits_string_only_public_locations() {
   1619         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   1620         migrations::run_all_up(&exec).expect("migrations");
   1621         let farm_row = farm::create(
   1622             &exec,
   1623             &IFarmFields {
   1624                 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(),
   1625                 pubkey: "f".repeat(64),
   1626                 name: "string-only farm".to_string(),
   1627                 about: None,
   1628                 website: None,
   1629                 picture: None,
   1630                 banner: None,
   1631                 location_primary: Some("San Francisco, CA".to_string()),
   1632                 location_city: Some("San Francisco".to_string()),
   1633                 location_region: Some("CA".to_string()),
   1634                 location_country: Some("US".to_string()),
   1635             },
   1636         )
   1637         .expect("farm")
   1638         .result;
   1639 
   1640         let location = load_farm_location(&exec, &farm_row).expect("location query");
   1641         assert!(location.is_none());
   1642     }
   1643 
   1644     #[test]
   1645     fn emit_propagates_queryfail_and_builder_errors() {
   1646         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   1647         let (farm_row, _, _) = seed(&exec);
   1648         let selector = RadrootsReplicaFarmSelector {
   1649             id: Some(farm_row.id.clone()),
   1650             d_tag: None,
   1651             pubkey: None,
   1652         };
   1653 
   1654         let farm_tag_fail = QueryFailExecutor {
   1655             inner: &exec,
   1656             needle: "farm_tag",
   1657             err: SqlError::Internal,
   1658         };
   1659         assert!(radroots_replica_farm_event(&farm_tag_fail, &farm_row).is_err());
   1660 
   1661         let plot_tag_fail = QueryFailExecutor {
   1662             inner: &exec,
   1663             needle: "plot_tag",
   1664             err: SqlError::Internal,
   1665         };
   1666         assert!(radroots_replica_plot_events(&plot_tag_fail, &farm_row).is_err());
   1667 
   1668         let list_set_fail = QueryFailExecutor {
   1669             inner: &exec,
   1670             needle: "farm_member",
   1671             err: SqlError::Internal,
   1672         };
   1673         assert!(radroots_replica_list_set_events(&list_set_fail, &farm_row).is_err());
   1674 
   1675         let claims_fail = QueryFailExecutor {
   1676             inner: &exec,
   1677             needle: "farm_member_claim",
   1678             err: SqlError::Internal,
   1679         };
   1680         assert!(radroots_replica_membership_claim_events(&claims_fail, &farm_row.pubkey).is_err());
   1681 
   1682         let sync_farm_fail = QueryFailExecutor {
   1683             inner: &exec,
   1684             needle: "farm_tag",
   1685             err: SqlError::Internal,
   1686         };
   1687         assert!(
   1688             radroots_replica_sync_all_with_options(
   1689                 &sync_farm_fail,
   1690                 &selector,
   1691                 Some(&RadrootsReplicaSyncOptions {
   1692                     include_list_sets: Some(false),
   1693                     include_membership_claims: Some(false),
   1694                 }),
   1695             )
   1696             .is_err()
   1697         );
   1698         let sync_plot_fail = QueryFailExecutor {
   1699             inner: &exec,
   1700             needle: "plot_tag",
   1701             err: SqlError::Internal,
   1702         };
   1703         assert!(
   1704             radroots_replica_sync_all_with_options(
   1705                 &sync_plot_fail,
   1706                 &selector,
   1707                 Some(&RadrootsReplicaSyncOptions {
   1708                     include_list_sets: Some(false),
   1709                     include_membership_claims: Some(false),
   1710                 }),
   1711             )
   1712             .is_err()
   1713         );
   1714         let sync_list_set_fail = QueryFailExecutor {
   1715             inner: &exec,
   1716             needle: "farm_member",
   1717             err: SqlError::Internal,
   1718         };
   1719         assert!(
   1720             radroots_replica_sync_all_with_options(
   1721                 &sync_list_set_fail,
   1722                 &selector,
   1723                 Some(&RadrootsReplicaSyncOptions {
   1724                     include_list_sets: Some(true),
   1725                     include_membership_claims: Some(false),
   1726                 }),
   1727             )
   1728             .is_err()
   1729         );
   1730         let sync_claims_fail = QueryFailExecutor {
   1731             inner: &exec,
   1732             needle: "farm_member_claim",
   1733             err: SqlError::Internal,
   1734         };
   1735         assert!(
   1736             radroots_replica_sync_all_with_options(
   1737                 &sync_claims_fail,
   1738                 &selector,
   1739                 Some(&RadrootsReplicaSyncOptions {
   1740                     include_list_sets: Some(false),
   1741                     include_membership_claims: Some(true),
   1742                 }),
   1743             )
   1744             .is_err()
   1745         );
   1746 
   1747         assert!(radroots_replica_farm_event(&exec, &farm_row).is_ok());
   1748         assert!(radroots_replica_plot_events(&exec, &farm_row).is_ok());
   1749     }
   1750 
   1751     #[test]
   1752     fn emit_additional_error_branches_are_reported() {
   1753         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   1754         let (farm_row, _, _) = seed(&exec);
   1755 
   1756         let farm_location_fail = QueryFailExecutor {
   1757             inner: &exec,
   1758             needle: "farm_gcs_location",
   1759             err: SqlError::Internal,
   1760         };
   1761         assert!(radroots_replica_farm_event(&farm_location_fail, &farm_row).is_err());
   1762         let invalid_farm = Farm {
   1763             id: farm_row.id.clone(),
   1764             created_at: farm_row.created_at.clone(),
   1765             updated_at: farm_row.updated_at.clone(),
   1766             d_tag: "invalid".to_string(),
   1767             pubkey: farm_row.pubkey.clone(),
   1768             name: farm_row.name.clone(),
   1769             about: farm_row.about.clone(),
   1770             website: farm_row.website.clone(),
   1771             picture: farm_row.picture.clone(),
   1772             banner: farm_row.banner.clone(),
   1773             location_primary: farm_row.location_primary.clone(),
   1774             location_city: farm_row.location_city.clone(),
   1775             location_region: farm_row.location_region.clone(),
   1776             location_country: farm_row.location_country.clone(),
   1777         };
   1778         assert!(radroots_replica_farm_event(&exec, &invalid_farm).is_err());
   1779         crate::canonical::failpoints::set_error();
   1780         assert!(radroots_replica_farm_event(&exec, &farm_row).is_err());
   1781 
   1782         let plots_fail = QueryFailExecutor {
   1783             inner: &exec,
   1784             needle: "from plot",
   1785             err: SqlError::Internal,
   1786         };
   1787         assert!(radroots_replica_plot_events(&plots_fail, &farm_row).is_err());
   1788         let plot_location_fail = QueryFailExecutor {
   1789             inner: &exec,
   1790             needle: "plot_gcs_location",
   1791             err: SqlError::Internal,
   1792         };
   1793         assert!(radroots_replica_plot_events(&plot_location_fail, &farm_row).is_err());
   1794         crate::canonical::failpoints::set_error();
   1795         assert!(radroots_replica_plot_events(&exec, &farm_row).is_err());
   1796         create_plot_record(&exec, &farm_row.id, "invalid", "plot-invalid");
   1797         assert!(radroots_replica_plot_events(&exec, &farm_row).is_err());
   1798 
   1799         let resolve_id_fail = QueryFailExecutor {
   1800             inner: &exec,
   1801             needle: "from farm",
   1802             err: SqlError::Internal,
   1803         };
   1804         assert!(
   1805             resolve_farm(
   1806                 &resolve_id_fail,
   1807                 &RadrootsReplicaFarmSelector {
   1808                     id: Some(farm_row.id.clone()),
   1809                     d_tag: None,
   1810                     pubkey: None,
   1811                 }
   1812             )
   1813             .is_err()
   1814         );
   1815         let resolve_pair_fail = QueryFailExecutor {
   1816             inner: &exec,
   1817             needle: "from farm",
   1818             err: SqlError::Internal,
   1819         };
   1820         assert!(
   1821             resolve_farm(
   1822                 &resolve_pair_fail,
   1823                 &RadrootsReplicaFarmSelector {
   1824                     id: None,
   1825                     d_tag: Some(farm_row.d_tag.clone()),
   1826                     pubkey: Some(farm_row.pubkey.clone()),
   1827                 }
   1828             )
   1829             .is_err()
   1830         );
   1831 
   1832         let gcs_query_fail = QueryFailExecutor {
   1833             inner: &exec,
   1834             needle: "from gcs_location",
   1835             err: SqlError::Internal,
   1836         };
   1837         assert!(
   1838             load_relation_by_role(
   1839                 &gcs_query_fail,
   1840                 &farm_row.id,
   1841                 ROLE_PRIMARY,
   1842                 RelationType::Farm
   1843             )
   1844             .is_err()
   1845         );
   1846         super::failpoints::set_gcs_location_to_event_error();
   1847         assert!(
   1848             load_relation_by_role(&exec, &farm_row.id, ROLE_PRIMARY, RelationType::Farm).is_err()
   1849         );
   1850 
   1851         let list_plot_fail = QueryFailExecutor {
   1852             inner: &exec,
   1853             needle: "from plot",
   1854             err: SqlError::Internal,
   1855         };
   1856         assert!(radroots_replica_list_set_events(&list_plot_fail, &farm_row).is_err());
   1857 
   1858         let list_member_error_farm = create_farm_record(
   1859             &exec,
   1860             "AAAAAAAAAAAAAAAAAAAAAA",
   1861             &"1".repeat(64),
   1862             "list-member-error",
   1863         );
   1864         add_member_record(
   1865             &exec,
   1866             &list_member_error_farm.id,
   1867             &" ".repeat(64),
   1868             ROLE_MEMBER,
   1869         );
   1870         create_plot_record(
   1871             &exec,
   1872             &list_member_error_farm.id,
   1873             "AAAAAAAAAAAAAAAAAAAAAQ",
   1874             "plot-member-error",
   1875         );
   1876         assert!(radroots_replica_list_set_events(&exec, &list_member_error_farm).is_err());
   1877 
   1878         let list_owner_error_farm = create_farm_record(
   1879             &exec,
   1880             "AAAAAAAAAAAAAAAAAAAAAA",
   1881             &"2".repeat(64),
   1882             "list-owner-error",
   1883         );
   1884         add_member_record(
   1885             &exec,
   1886             &list_owner_error_farm.id,
   1887             &"a".repeat(64),
   1888             ROLE_MEMBER,
   1889         );
   1890         add_member_record(
   1891             &exec,
   1892             &list_owner_error_farm.id,
   1893             &" ".repeat(64),
   1894             ROLE_OWNER,
   1895         );
   1896         add_member_record(
   1897             &exec,
   1898             &list_owner_error_farm.id,
   1899             &"b".repeat(64),
   1900             ROLE_WORKER,
   1901         );
   1902         create_plot_record(
   1903             &exec,
   1904             &list_owner_error_farm.id,
   1905             "AAAAAAAAAAAAAAAAAAAAAQ",
   1906             "plot-owner-error",
   1907         );
   1908         assert!(radroots_replica_list_set_events(&exec, &list_owner_error_farm).is_err());
   1909 
   1910         let list_worker_error_farm = create_farm_record(
   1911             &exec,
   1912             "AAAAAAAAAAAAAAAAAAAAAA",
   1913             &"3".repeat(64),
   1914             "list-worker-error",
   1915         );
   1916         add_member_record(
   1917             &exec,
   1918             &list_worker_error_farm.id,
   1919             &"c".repeat(64),
   1920             ROLE_MEMBER,
   1921         );
   1922         add_member_record(
   1923             &exec,
   1924             &list_worker_error_farm.id,
   1925             &"d".repeat(64),
   1926             ROLE_OWNER,
   1927         );
   1928         add_member_record(
   1929             &exec,
   1930             &list_worker_error_farm.id,
   1931             &" ".repeat(64),
   1932             ROLE_WORKER,
   1933         );
   1934         create_plot_record(
   1935             &exec,
   1936             &list_worker_error_farm.id,
   1937             "AAAAAAAAAAAAAAAAAAAAAQ",
   1938             "plot-worker-error",
   1939         );
   1940         assert!(radroots_replica_list_set_events(&exec, &list_worker_error_farm).is_err());
   1941 
   1942         let list_plot_error_farm = create_farm_record(
   1943             &exec,
   1944             "AAAAAAAAAAAAAAAAAAAAAA",
   1945             &"4".repeat(64),
   1946             "list-plot-error",
   1947         );
   1948         add_member_record(
   1949             &exec,
   1950             &list_plot_error_farm.id,
   1951             &"e".repeat(64),
   1952             ROLE_MEMBER,
   1953         );
   1954         add_member_record(&exec, &list_plot_error_farm.id, &"f".repeat(64), ROLE_OWNER);
   1955         add_member_record(
   1956             &exec,
   1957             &list_plot_error_farm.id,
   1958             &"7".repeat(64),
   1959             ROLE_WORKER,
   1960         );
   1961         create_plot_record(&exec, &list_plot_error_farm.id, "", "plot-list-error");
   1962         assert!(radroots_replica_list_set_events(&exec, &list_plot_error_farm).is_err());
   1963 
   1964         let clean_exec = SqlxSqliteExecutor::open_memory().expect("db clean");
   1965         let (clean_farm, _, _) = seed(&clean_exec);
   1966         super::failpoints::set_list_set_to_wire_error();
   1967         assert!(radroots_replica_list_set_events(&clean_exec, &clean_farm).is_err());
   1968 
   1969         let invalid_list_set = radroots_event::social::list_set::ListSet {
   1970             d_tag: String::new(),
   1971             content: String::new(),
   1972             entries: Vec::new(),
   1973             title: None,
   1974             description: None,
   1975             image: None,
   1976         };
   1977         assert!(list_set_to_wire_parts(&invalid_list_set).is_err());
   1978 
   1979         let claims_member_query_fail = QueryFailExecutor {
   1980             inner: &exec,
   1981             needle: "where member_pubkey",
   1982             err: SqlError::Internal,
   1983         };
   1984         assert!(
   1985             radroots_replica_membership_claim_events(&claims_member_query_fail, &farm_row.pubkey)
   1986                 .is_err()
   1987         );
   1988 
   1989         let _ = farm_member_claim::create(
   1990             &exec,
   1991             &IFarmMemberClaimFields {
   1992                 member_pubkey: "a".repeat(64),
   1993                 farm_pubkey: " ".repeat(64),
   1994             },
   1995         )
   1996         .expect("empty-farm-pubkey claim");
   1997         assert!(radroots_replica_membership_claim_events(&exec, &" ".repeat(64)).is_err());
   1998 
   1999         super::failpoints::set_list_set_to_wire_error();
   2000         assert!(radroots_replica_membership_claim_events(&clean_exec, &clean_farm.pubkey).is_err());
   2001     }
   2002 
   2003     #[test]
   2004     fn emit_list_set_wire_error_paths_are_reported() {
   2005         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   2006         let (farm_row, _, _) = seed(&exec);
   2007 
   2008         super::failpoints::set_list_set_to_wire_error();
   2009         assert!(radroots_replica_list_set_events(&exec, &farm_row).is_err());
   2010 
   2011         let invalid_list_set = radroots_event::social::list_set::ListSet {
   2012             d_tag: String::new(),
   2013             content: String::new(),
   2014             entries: Vec::new(),
   2015             title: None,
   2016             description: None,
   2017             image: None,
   2018         };
   2019         assert!(list_set_to_wire_parts(&invalid_list_set).is_err());
   2020     }
   2021 
   2022     #[test]
   2023     fn emit_pass_through_executor_instantiation_paths_are_covered() {
   2024         let exec = SqlxSqliteExecutor::open_memory().expect("db");
   2025         let (farm_row, _, plot_secondary) = seed(&exec);
   2026 
   2027         let pass = QueryFailExecutor {
   2028             inner: &exec,
   2029             needle: "__never_match__",
   2030             err: SqlError::Internal,
   2031         };
   2032 
   2033         let selector = RadrootsReplicaFarmSelector {
   2034             id: Some(farm_row.id.clone()),
   2035             d_tag: None,
   2036             pubkey: None,
   2037         };
   2038         let request = RadrootsReplicaSyncRequest {
   2039             farm: selector.clone(),
   2040             options: None,
   2041         };
   2042         let bundle = radroots_replica_sync_all(&pass, &request).expect("sync via pass executor");
   2043         assert!(!bundle.events.is_empty());
   2044 
   2045         let resolved_by_id = resolve_farm(&pass, &selector).expect("resolve by id");
   2046         assert_eq!(resolved_by_id.id, farm_row.id);
   2047         let resolved_by_pair = resolve_farm(
   2048             &pass,
   2049             &RadrootsReplicaFarmSelector {
   2050                 id: None,
   2051                 d_tag: Some(farm_row.d_tag.clone()),
   2052                 pubkey: Some(farm_row.pubkey.clone()),
   2053             },
   2054         )
   2055         .expect("resolve by pair");
   2056         assert_eq!(resolved_by_pair.id, farm_row.id);
   2057         let duplicate_pair = DuplicateFarmSelectorExecutor {
   2058             inner: &exec,
   2059             duplicated_rows_json: {
   2060                 let farm_json = serde_json::to_string(&farm_row).expect("farm json");
   2061                 format!("[{farm_json},{farm_json}]")
   2062             },
   2063         };
   2064         duplicate_pair.begin().expect("duplicate begin");
   2065         duplicate_pair.rollback().expect("duplicate rollback");
   2066         duplicate_pair.begin().expect("duplicate begin");
   2067         duplicate_pair.commit().expect("duplicate commit");
   2068         duplicate_pair
   2069             .exec("CREATE TABLE duplicate_probe (id INTEGER)", "[]")
   2070             .expect("duplicate exec");
   2071         let duplicate_err = resolve_farm(
   2072             &duplicate_pair,
   2073             &RadrootsReplicaFarmSelector {
   2074                 id: None,
   2075                 d_tag: Some(farm_row.d_tag.clone()),
   2076                 pubkey: Some(farm_row.pubkey.clone()),
   2077             },
   2078         )
   2079         .map(|_| ())
   2080         .unwrap_err();
   2081         assert!(
   2082             duplicate_err
   2083                 .to_string()
   2084                 .contains("did not resolve to a single farm")
   2085         );
   2086         assert!(
   2087             resolve_farm(
   2088                 &pass,
   2089                 &RadrootsReplicaFarmSelector {
   2090                     id: Some("00000000-0000-0000-0000-000000000000".to_string()),
   2091                     d_tag: None,
   2092                     pubkey: None,
   2093                 },
   2094             )
   2095             .is_err()
   2096         );
   2097 
   2098         assert!(
   2099             load_relation_by_role(&pass, &farm_row.id, ROLE_PRIMARY, RelationType::Farm)
   2100                 .expect("farm primary relation")
   2101                 .is_some()
   2102         );
   2103         assert!(
   2104             load_relation_by_role(&pass, &farm_row.id, "", RelationType::Farm)
   2105                 .expect("farm fallback relation")
   2106                 .is_some()
   2107         );
   2108         assert!(
   2109             load_relation_by_role(&pass, &plot_secondary.id, "", RelationType::Plot)
   2110                 .expect("plot fallback relation")
   2111                 .is_some()
   2112         );
   2113     }
   2114 
   2115     #[test]
   2116     fn emit_executor_trait_method_paths_are_covered() {
   2117         let sqlite = SqlxSqliteExecutor::open_memory().expect("db");
   2118         migrations::run_all_up(&sqlite).expect("migrations");
   2119 
   2120         let err_exec = ErrorExecutor;
   2121         assert!(err_exec.exec("SELECT 1", "[]").is_err());
   2122         assert!(err_exec.query_raw("SELECT 1", "[]").is_err());
   2123         assert!(err_exec.begin().is_ok());
   2124         assert!(err_exec.commit().is_ok());
   2125         assert!(err_exec.rollback().is_ok());
   2126 
   2127         let pass = QueryFailExecutor {
   2128             inner: &sqlite,
   2129             needle: "__never_match__",
   2130             err: SqlError::Internal,
   2131         };
   2132         assert!(pass.exec("PRAGMA foreign_keys = ON", "[]").is_ok());
   2133         assert!(pass.query_raw("SELECT 1", "[]").is_ok());
   2134         assert!(pass.begin().is_ok());
   2135         assert!(pass.commit().is_ok());
   2136 
   2137         let pass_rollback = QueryFailExecutor {
   2138             inner: &sqlite,
   2139             needle: "__never_match__",
   2140             err: SqlError::Internal,
   2141         };
   2142         assert!(pass_rollback.begin().is_ok());
   2143         assert!(pass_rollback.rollback().is_ok());
   2144 
   2145         let fail_query = QueryFailExecutor {
   2146             inner: &sqlite,
   2147             needle: "select 1",
   2148             err: SqlError::Internal,
   2149         };
   2150         assert!(fail_query.query_raw("SELECT 1", "[]").is_err());
   2151     }
   2152 }