geochat.rs (7487B)
1 mod common; 2 3 use common::{AUTHOR, EVENT_ID, EVENT_SIG}; 4 use radroots_event::{ 5 envelope::kind::{KIND_GEOCHAT, KIND_POST}, 6 social::geochat::GeoChat, 7 }; 8 use radroots_event_codec::decode::EventParseError; 9 use radroots_event_codec::decode::geochat::{ 10 data_from_event, geochat_from_tags, parsed_from_event, 11 }; 12 use radroots_event_codec::encode::EventEncodeError; 13 use radroots_event_codec::encode::geochat::{geochat_build_tags, to_wire_parts}; 14 15 #[test] 16 fn geochat_build_tags_requires_geohash() { 17 let geochat = GeoChat { 18 geohash: " ".to_string(), 19 content: "hello".to_string(), 20 nickname: None, 21 teleported: false, 22 }; 23 24 let err = geochat_build_tags(&geochat).unwrap_err(); 25 assert!(matches!( 26 err, 27 EventEncodeError::EmptyRequiredField("geohash") 28 )); 29 } 30 31 #[test] 32 fn geochat_build_tags_requires_nickname_if_present() { 33 let geochat = GeoChat { 34 geohash: "dr5rsj7".to_string(), 35 content: "hello".to_string(), 36 nickname: Some(" ".to_string()), 37 teleported: false, 38 }; 39 40 let err = geochat_build_tags(&geochat).unwrap_err(); 41 assert!(matches!( 42 err, 43 EventEncodeError::EmptyRequiredField("nickname") 44 )); 45 } 46 47 #[test] 48 fn geochat_build_tags_omits_optional_nickname_and_teleport_when_disabled() { 49 let geochat = GeoChat { 50 geohash: "dr5rsj7".to_string(), 51 content: "hello".to_string(), 52 nickname: None, 53 teleported: false, 54 }; 55 56 let tags = geochat_build_tags(&geochat).unwrap(); 57 assert_eq!(tags, vec![vec!["g".to_string(), "dr5rsj7".to_string()]]); 58 } 59 60 #[test] 61 fn geochat_to_wire_parts_requires_content() { 62 let geochat = GeoChat { 63 geohash: "dr5rsj7".to_string(), 64 content: " ".to_string(), 65 nickname: None, 66 teleported: false, 67 }; 68 69 let err = to_wire_parts(&geochat).unwrap_err(); 70 assert!(matches!( 71 err, 72 EventEncodeError::EmptyRequiredField("content") 73 )); 74 75 let geochat = GeoChat { 76 geohash: " ".to_string(), 77 content: "hello".to_string(), 78 nickname: None, 79 teleported: false, 80 }; 81 let err = to_wire_parts(&geochat).unwrap_err(); 82 assert!(matches!( 83 err, 84 EventEncodeError::EmptyRequiredField("geohash") 85 )); 86 } 87 88 #[test] 89 fn geochat_to_wire_parts_sets_tags() { 90 let geochat = GeoChat { 91 geohash: "dr5rsj7".to_string(), 92 content: "hello".to_string(), 93 nickname: Some("alex".to_string()), 94 teleported: true, 95 }; 96 97 let parts = to_wire_parts(&geochat).unwrap(); 98 assert_eq!(parts.kind, KIND_GEOCHAT); 99 assert_eq!(parts.content, "hello"); 100 assert_eq!( 101 parts.tags, 102 vec![ 103 vec!["g".to_string(), "dr5rsj7".to_string()], 104 vec!["n".to_string(), "alex".to_string()], 105 vec!["t".to_string(), "teleport".to_string()], 106 ] 107 ); 108 } 109 110 #[test] 111 fn geochat_from_tags_requires_kind_geohash_and_content() { 112 let tags = vec![vec!["g".to_string(), "dr5rsj7".to_string()]]; 113 let err = geochat_from_tags(KIND_POST, &tags, "hello").unwrap_err(); 114 assert!(matches!( 115 err, 116 EventParseError::InvalidKind { 117 expected: "20000", 118 got: KIND_POST 119 } 120 )); 121 122 let err = geochat_from_tags(KIND_GEOCHAT, &tags, " ").unwrap_err(); 123 assert!(matches!(err, EventParseError::InvalidTag("content"))); 124 125 let err = geochat_from_tags(KIND_GEOCHAT, &[], "hello").unwrap_err(); 126 assert!(matches!(err, EventParseError::MissingTag("g"))); 127 } 128 129 #[test] 130 fn geochat_roundtrip_from_tags() { 131 let tags = vec![ 132 vec!["g".to_string(), "dr5rsj7".to_string()], 133 vec!["n".to_string(), "alex".to_string()], 134 vec!["t".to_string(), "teleport".to_string()], 135 ]; 136 137 let geochat = geochat_from_tags(KIND_GEOCHAT, &tags, "hello").unwrap(); 138 139 assert_eq!(geochat.geohash, "dr5rsj7"); 140 assert_eq!(geochat.content, "hello"); 141 assert_eq!(geochat.nickname.as_deref(), Some("alex")); 142 assert!(geochat.teleported); 143 } 144 145 #[test] 146 fn geochat_from_tags_rejects_invalid_optional_tags() { 147 let err = geochat_from_tags( 148 KIND_GEOCHAT, 149 &[vec!["g".to_string(), " ".to_string()]], 150 "hello", 151 ) 152 .unwrap_err(); 153 assert!(matches!(err, EventParseError::InvalidTag("g"))); 154 155 let err = geochat_from_tags( 156 KIND_GEOCHAT, 157 &[ 158 vec!["g".to_string(), "dr5rsj7".to_string()], 159 vec!["n".to_string(), " ".to_string()], 160 ], 161 "hello", 162 ) 163 .unwrap_err(); 164 assert!(matches!(err, EventParseError::InvalidTag("n"))); 165 166 let err = geochat_from_tags( 167 KIND_GEOCHAT, 168 &[ 169 vec!["g".to_string(), "dr5rsj7".to_string()], 170 vec!["t".to_string(), " ".to_string()], 171 ], 172 "hello", 173 ) 174 .unwrap_err(); 175 assert!(matches!(err, EventParseError::InvalidTag("t"))); 176 177 let geochat = geochat_from_tags( 178 KIND_GEOCHAT, 179 &[ 180 vec!["g".to_string(), "dr5rsj7".to_string()], 181 vec!["t".to_string(), "moving".to_string()], 182 ], 183 "hello", 184 ) 185 .unwrap(); 186 assert!(!geochat.teleported); 187 } 188 189 #[test] 190 fn geochat_from_tags_rejects_missing_tag_values() { 191 let err = geochat_from_tags(KIND_GEOCHAT, &[vec!["g".to_string()]], "hello").unwrap_err(); 192 assert!(matches!(err, EventParseError::InvalidTag("g"))); 193 194 let err = geochat_from_tags( 195 KIND_GEOCHAT, 196 &[ 197 vec!["g".to_string(), "dr5rsj7".to_string()], 198 vec!["n".to_string()], 199 ], 200 "hello", 201 ) 202 .unwrap_err(); 203 assert!(matches!(err, EventParseError::InvalidTag("n"))); 204 205 let err = geochat_from_tags( 206 KIND_GEOCHAT, 207 &[ 208 vec!["g".to_string(), "dr5rsj7".to_string()], 209 vec!["t".to_string()], 210 ], 211 "hello", 212 ) 213 .unwrap_err(); 214 assert!(matches!(err, EventParseError::InvalidTag("t"))); 215 } 216 217 #[test] 218 fn geochat_metadata_and_index_from_event_roundtrip() { 219 let tags = vec![ 220 vec!["g".to_string(), "dr5rsj7".to_string()], 221 vec!["n".to_string(), "alex".to_string()], 222 vec!["t".to_string(), "teleport".to_string()], 223 ]; 224 let metadata = data_from_event( 225 "id".to_string(), 226 "author".to_string(), 227 77, 228 KIND_GEOCHAT, 229 "hello".to_string(), 230 tags.clone(), 231 ) 232 .unwrap(); 233 assert_eq!(metadata.id, "id"); 234 assert_eq!(metadata.author, "author"); 235 assert_eq!(metadata.published_at, 77); 236 assert_eq!(metadata.kind, KIND_GEOCHAT); 237 assert_eq!(metadata.data.geohash, "dr5rsj7"); 238 assert!(metadata.data.teleported); 239 240 let index = parsed_from_event( 241 EVENT_ID.to_string(), 242 AUTHOR.to_string(), 243 77, 244 KIND_GEOCHAT, 245 "hello".to_string(), 246 tags, 247 EVENT_SIG.to_string(), 248 ) 249 .unwrap(); 250 assert_eq!(index.event.kind_u32(), KIND_GEOCHAT); 251 assert_eq!(index.event.signature_hex(), EVENT_SIG); 252 assert_eq!(index.data.data.geohash, "dr5rsj7"); 253 } 254 255 #[test] 256 fn geochat_index_from_event_propagates_parse_errors() { 257 let err = parsed_from_event( 258 "id".to_string(), 259 "author".to_string(), 260 77, 261 KIND_POST, 262 "hello".to_string(), 263 vec![vec!["g".to_string(), "dr5rsj7".to_string()]], 264 "sig".to_string(), 265 ) 266 .unwrap_err(); 267 assert!(matches!( 268 err, 269 EventParseError::InvalidKind { 270 expected: "20000", 271 got: KIND_POST 272 } 273 )); 274 }