social_events.rs (7041B)
1 #![cfg(feature = "json")] 2 3 use radroots_event::{ 4 envelope::kind::{ 5 KIND_ARTICLE, KIND_CLASSIFIED_LISTING, KIND_FARM, KIND_FARM_CRDT_CHANGE, 6 KIND_GROUP_METADATA, KIND_HTTP_AUTH, KIND_POST, KIND_PUBLIC_FILE_METADATA, KIND_RELAY_AUTH, 7 KIND_REPORT, is_home_feed_candidate_kind, is_market_candidate_kind, 8 is_private_farm_ops_kind, is_public_social_kind, 9 }, 10 farm::crdt::FarmCrdtDocumentKind, 11 farm::file::{FarmFileDimensions, FarmFileMetadata}, 12 farm::workspace::FarmWorkspaceRef, 13 media::file_metadata::FileMetadata, 14 social::SocialMediaDimensions, 15 social::group::{GroupEditableMetadata, GroupMetadata}, 16 }; 17 use radroots_event_codec::{ 18 decode::EventParseError, decode::article::article_from_event, decode::farm::farm_from_event, 19 decode::farm_file::farm_file_metadata_from_event, 20 decode::file_metadata::file_metadata_from_event, decode::group::group_metadata_from_event, 21 decode::operational_listing::operational_listing_from_event, 22 encode::farm_file::to_wire_parts as farm_file_to_wire_parts, 23 encode::file_metadata::to_wire_parts as public_file_to_wire_parts, 24 encode::group::group_metadata_to_wire_parts, 25 }; 26 27 const SHA256: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; 28 const OTHER_SHA256: &str = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"; 29 const D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; 30 31 #[test] 32 fn social_events_keep_public_and_private_file_metadata_apis_separate() { 33 let public = public_file_to_wire_parts(&public_file_metadata()).unwrap(); 34 let decoded_public = 35 file_metadata_from_event(public.kind, &public.tags, &public.content).unwrap(); 36 assert_eq!(decoded_public.url, "https://media.example.test/public.jpg"); 37 assert!(matches!( 38 farm_file_metadata_from_event(public.kind, &public.tags, &public.content), 39 Err(EventParseError::MissingTag("d")) 40 )); 41 42 let private = farm_file_to_wire_parts(&private_farm_file_metadata()).unwrap(); 43 let decoded_private = 44 farm_file_metadata_from_event(private.kind, &private.tags, &private.content).unwrap(); 45 assert_eq!(decoded_private.owner_document_id, D_TAG); 46 assert!(matches!( 47 file_metadata_from_event(private.kind, &private.tags, &private.content), 48 Err(EventParseError::InvalidTag("radroots:owner_document")) 49 )); 50 } 51 52 #[test] 53 fn social_events_reject_private_farm_ops_semantics_in_public_codecs() { 54 let private_content = serde_json::json!({ 55 "workspace": { 56 "pubkey": "workspace_pubkey", 57 "d_tag": D_TAG 58 }, 59 "farm_group_id": "field-group", 60 "document_id": D_TAG, 61 "document_kind": "FarmTask", 62 "encoded_change": "abc-DEF_012" 63 }) 64 .to_string(); 65 let farm_tags = vec![vec!["d".to_string(), D_TAG.to_string()]]; 66 67 assert!(matches!( 68 farm_from_event(KIND_FARM, &farm_tags, &private_content), 69 Err(EventParseError::InvalidJson("content")) 70 )); 71 assert!(matches!( 72 article_from_event(KIND_FARM_CRDT_CHANGE, &[], "farm task"), 73 Err(EventParseError::InvalidKind { 74 expected: "30023", 75 got: KIND_FARM_CRDT_CHANGE 76 }) 77 )); 78 assert!(matches!( 79 operational_listing_from_event(KIND_FARM_CRDT_CHANGE, &[], "farm task"), 80 Err(EventParseError::InvalidKind { 81 expected: "30402", 82 got: KIND_FARM_CRDT_CHANGE 83 }) 84 )); 85 assert!(is_public_social_kind(KIND_POST)); 86 assert!(is_public_social_kind(KIND_ARTICLE)); 87 assert!(is_public_social_kind(KIND_PUBLIC_FILE_METADATA)); 88 assert!(!is_public_social_kind(KIND_FARM_CRDT_CHANGE)); 89 assert!(!is_public_social_kind(KIND_CLASSIFIED_LISTING)); 90 assert!(is_home_feed_candidate_kind(KIND_CLASSIFIED_LISTING)); 91 assert!(is_home_feed_candidate_kind(KIND_FARM)); 92 assert!(is_home_feed_candidate_kind(KIND_PUBLIC_FILE_METADATA)); 93 assert!(!is_home_feed_candidate_kind(30403)); 94 assert!(!is_home_feed_candidate_kind(KIND_REPORT)); 95 assert!(!is_home_feed_candidate_kind(KIND_FARM_CRDT_CHANGE)); 96 assert!(!is_home_feed_candidate_kind(KIND_RELAY_AUTH)); 97 assert!(!is_home_feed_candidate_kind(KIND_HTTP_AUTH)); 98 assert!(is_market_candidate_kind(KIND_CLASSIFIED_LISTING)); 99 assert!(!is_market_candidate_kind(30403)); 100 } 101 102 #[test] 103 fn social_events_keep_nip29_groups_out_of_public_social_classification() { 104 let group = GroupMetadata { 105 d_tag: "field-group".to_string(), 106 metadata: GroupEditableMetadata { 107 name: Some("Field Group".to_string()), 108 about: Some("Localhost field coordination".to_string()), 109 picture: None, 110 is_private: false, 111 is_restricted: false, 112 is_closed: false, 113 is_hidden: false, 114 supported_kinds: Some(vec![KIND_FARM_CRDT_CHANGE]), 115 }, 116 }; 117 let parts = group_metadata_to_wire_parts(&group).unwrap(); 118 assert_eq!(parts.kind, KIND_GROUP_METADATA); 119 assert!(!is_public_social_kind(KIND_GROUP_METADATA)); 120 assert!(!is_home_feed_candidate_kind(KIND_GROUP_METADATA)); 121 assert!(is_private_farm_ops_kind(KIND_GROUP_METADATA)); 122 assert_eq!( 123 group_metadata_from_event(parts.kind, &parts.tags, &parts.content) 124 .unwrap() 125 .metadata 126 .supported_kinds, 127 Some(vec![KIND_FARM_CRDT_CHANGE]) 128 ); 129 } 130 131 fn public_file_metadata() -> FileMetadata { 132 FileMetadata { 133 url: "https://media.example.test/public.jpg".to_string(), 134 mime_type: "image/jpeg".to_string(), 135 sha256: SHA256.to_string(), 136 original_sha256: Some(OTHER_SHA256.to_string()), 137 size: Some(4096), 138 dimensions: Some(SocialMediaDimensions { 139 width: 1200, 140 height: 800, 141 }), 142 blurhash: None, 143 thumbnails: None, 144 summary: Some("Public field photo".to_string()), 145 alt: Some("Rows after harvest".to_string()), 146 fallback: None, 147 magnet: None, 148 content_hashes: None, 149 services: None, 150 content: Some("caption".to_string()), 151 } 152 } 153 154 fn private_farm_file_metadata() -> FarmFileMetadata { 155 FarmFileMetadata { 156 d_tag: D_TAG.to_string(), 157 workspace: FarmWorkspaceRef { 158 pubkey: "workspace_pubkey".to_string(), 159 d_tag: D_TAG.to_string(), 160 }, 161 farm_group_id: "field-group".to_string(), 162 owner_document_id: D_TAG.to_string(), 163 owner_document_kind: FarmCrdtDocumentKind::FarmTask, 164 caption: Some("private caption".to_string()), 165 url: "https://media.example.test/private.jpg".to_string(), 166 mime_type: "image/jpeg".to_string(), 167 sha256: SHA256.to_string(), 168 original_sha256: Some(OTHER_SHA256.to_string()), 169 size_bytes: Some(4096), 170 dimensions: Some(FarmFileDimensions { w: 1200, h: 800 }), 171 blurhash: None, 172 thumb: None, 173 image: None, 174 alt: Some("Private farm task attachment".to_string()), 175 fallbacks: Vec::new(), 176 } 177 }