lib

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

farm_workspace.rs (5485B)


      1 #![cfg(feature = "serde_json")]
      2 
      3 use radroots_events::{
      4     farm::RadrootsFarmRef,
      5     farm_crdt::KIND_FARM_CRDT_CHANGE,
      6     farm_workspace::{
      7         KIND_FARM_WORKSPACE_MANIFEST, RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION,
      8         RADROOTS_FARM_WORKSPACE_SCHEMA, RadrootsFarmWorkspaceManifest,
      9         RadrootsFarmWorkspaceMediaServer, RadrootsFarmWorkspaceRelay,
     10         RadrootsFarmWorkspaceRelayMode,
     11     },
     12     kinds::{KIND_FARM, KIND_FARM_FILE_METADATA},
     13     tags::{TAG_A, TAG_H, TAG_P},
     14 };
     15 use radroots_events_codec::{
     16     error::{EventEncodeError, EventParseError},
     17     farm_workspace::decode::farm_workspace_from_event,
     18     farm_workspace::encode::to_wire_parts,
     19 };
     20 
     21 const D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA";
     22 const FARM_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAQ";
     23 const OTHER_FARM_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAg";
     24 const GROUP_ID: &str = "field-group";
     25 
     26 #[test]
     27 fn farm_workspace_decode_handles_optional_and_mismatch_edges() {
     28     let manifest = sample_manifest();
     29     let parts = to_wire_parts(&manifest).unwrap();
     30 
     31     let mut mismatched_group = manifest.clone();
     32     mismatched_group.farm_group_id = "other-group".to_string();
     33     let content = serde_json::to_string(&mismatched_group).unwrap();
     34     assert!(matches!(
     35         farm_workspace_from_event(parts.kind, &parts.tags, &content),
     36         Err(EventParseError::InvalidTag(TAG_H))
     37     ));
     38 
     39     let mut without_owner = parts.tags.clone();
     40     without_owner.retain(|tag| tag.first().map(String::as_str) != Some(TAG_P));
     41     let decoded = farm_workspace_from_event(parts.kind, &without_owner, &parts.content).unwrap();
     42     assert_eq!(decoded.owner_pubkey, "workspace_owner_pubkey");
     43 
     44     let mut without_farm_address = parts.tags.clone();
     45     without_farm_address.retain(|tag| tag.first().map(String::as_str) != Some(TAG_A));
     46     let decoded =
     47         farm_workspace_from_event(parts.kind, &without_farm_address, &parts.content).unwrap();
     48     assert_eq!(
     49         decoded.farm.as_ref().map(|farm| farm.d_tag.as_str()),
     50         Some(FARM_D_TAG)
     51     );
     52 
     53     let mut mismatched_farm_address = parts.tags.clone();
     54     replace_first_tag(
     55         &mut mismatched_farm_address,
     56         TAG_A,
     57         vec![
     58             TAG_A.to_string(),
     59             format!("{KIND_FARM}:farm_pubkey:{OTHER_FARM_D_TAG}"),
     60         ],
     61     );
     62     assert!(matches!(
     63         farm_workspace_from_event(parts.kind, &mismatched_farm_address, &parts.content),
     64         Err(EventParseError::InvalidTag(TAG_A))
     65     ));
     66 
     67     let mut mismatched_farm_pubkey = parts.tags.clone();
     68     replace_first_tag(
     69         &mut mismatched_farm_pubkey,
     70         TAG_A,
     71         vec![
     72             TAG_A.to_string(),
     73             format!("{KIND_FARM}:other_farm:{FARM_D_TAG}"),
     74         ],
     75     );
     76     assert!(matches!(
     77         farm_workspace_from_event(parts.kind, &mismatched_farm_pubkey, &parts.content),
     78         Err(EventParseError::InvalidTag(TAG_A))
     79     ));
     80 
     81     for supported_kinds in [
     82         vec![KIND_FARM_WORKSPACE_MANIFEST],
     83         vec![KIND_FARM_CRDT_CHANGE],
     84     ] {
     85         let mut unsupported = manifest.clone();
     86         unsupported.supported_kinds = supported_kinds;
     87         let content = serde_json::to_string(&unsupported).unwrap();
     88         assert!(matches!(
     89             farm_workspace_from_event(parts.kind, &parts.tags, &content),
     90             Err(EventParseError::InvalidJson("supported_kinds"))
     91         ));
     92     }
     93 }
     94 
     95 #[test]
     96 fn farm_workspace_encode_rejects_schema_and_supported_kind_edges() {
     97     let mut bad_schema = sample_manifest();
     98     bad_schema.schema = "radroots.farm.workspace.invalid".to_string();
     99     assert!(matches!(
    100         to_wire_parts(&bad_schema),
    101         Err(EventEncodeError::InvalidField("schema"))
    102     ));
    103 
    104     for supported_kinds in [
    105         vec![KIND_FARM_WORKSPACE_MANIFEST],
    106         vec![KIND_FARM_CRDT_CHANGE],
    107     ] {
    108         let mut unsupported = sample_manifest();
    109         unsupported.supported_kinds = supported_kinds;
    110         assert!(matches!(
    111             to_wire_parts(&unsupported),
    112             Err(EventEncodeError::InvalidField("supported_kinds"))
    113         ));
    114     }
    115 }
    116 
    117 fn sample_manifest() -> RadrootsFarmWorkspaceManifest {
    118     RadrootsFarmWorkspaceManifest {
    119         d_tag: D_TAG.to_string(),
    120         schema: RADROOTS_FARM_WORKSPACE_SCHEMA.to_string(),
    121         farm_group_id: GROUP_ID.to_string(),
    122         name: "Small Regen Farm".to_string(),
    123         owner_pubkey: "workspace_owner_pubkey".to_string(),
    124         farm: Some(RadrootsFarmRef {
    125             pubkey: "farm_pubkey".to_string(),
    126             d_tag: FARM_D_TAG.to_string(),
    127         }),
    128         relays: vec![RadrootsFarmWorkspaceRelay {
    129             url: "wss://relay.example.invalid/farm/field-group".to_string(),
    130             mode: RadrootsFarmWorkspaceRelayMode::ReadWrite,
    131         }],
    132         media_servers: vec![RadrootsFarmWorkspaceMediaServer {
    133             url: "https://media.example.invalid/farm/field-group".to_string(),
    134             service: "RadrootsPrivateMedia".to_string(),
    135         }],
    136         supported_kinds: vec![
    137             KIND_FARM_CRDT_CHANGE,
    138             KIND_FARM_WORKSPACE_MANIFEST,
    139             KIND_FARM_FILE_METADATA,
    140         ],
    141         protocol_version: RADROOTS_FARM_WORKSPACE_PROTOCOL_VERSION.to_string(),
    142         created_at_ms: 1_780_000_000_000,
    143         updated_at_ms: None,
    144     }
    145 }
    146 
    147 fn replace_first_tag(tags: &mut [Vec<String>], name: &str, replacement: Vec<String>) {
    148     let tag = tags
    149         .iter_mut()
    150         .find(|tag| tag.first().map(String::as_str) == Some(name))
    151         .expect("tag");
    152     *tag = replacement;
    153 }