lib

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

farm_workspace.rs (5400B)


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