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