structured_decode.rs (30411B)
1 #![cfg(feature = "json")] 2 3 #[path = "../src/test_fixtures.rs"] 4 mod test_fixtures; 5 6 mod common; 7 8 use common::{EVENT_ID, EVENT_SIG}; 9 use radroots_core::{Decimal, Quantity, Unit}; 10 use radroots_event::envelope::kind::{ 11 KIND_COOP, KIND_DOCUMENT, KIND_FARM, KIND_PLOT, KIND_RESOURCE_AREA, KIND_RESOURCE_HARVEST_CAP, 12 }; 13 use radroots_event::farm::change_set::{GcsLocation, GeoJsonPoint, GeoJsonPolygon}; 14 use radroots_event::farm::coop::Coop; 15 use radroots_event::farm::plot::Plot; 16 use radroots_event::farm::resource_area::{ResourceArea, ResourceAreaLocation, ResourceAreaRef}; 17 use radroots_event::farm::resource_cap::{ResourceHarvestCap, ResourceHarvestProduct}; 18 use radroots_event::farm::{Farm, FarmRef}; 19 use radroots_event::post::document::{Document, DocumentSubject}; 20 use radroots_event::tag::name::TAG_D; 21 use radroots_event_codec::decode::EventParseError; 22 use radroots_event_codec::decode::coop::{ 23 coop_from_event, data_from_event as coop_metadata_from_event, 24 parsed_from_event as coop_index_from_event, 25 }; 26 use radroots_event_codec::decode::document::{ 27 data_from_event as document_metadata_from_event, document_from_event, 28 parsed_from_event as document_index_from_event, 29 }; 30 use radroots_event_codec::decode::farm::{ 31 data_from_event as farm_metadata_from_event, farm_from_event, 32 parsed_from_event as farm_index_from_event, 33 }; 34 use radroots_event_codec::decode::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 35 use radroots_event_codec::decode::plot::{ 36 data_from_event as plot_metadata_from_event, parsed_from_event as plot_index_from_event, 37 plot_from_event, 38 }; 39 use radroots_event_codec::decode::resource_area::{ 40 data_from_event as resource_area_metadata_from_event, 41 parsed_from_event as resource_area_index_from_event, resource_area_from_event, 42 }; 43 use radroots_event_codec::decode::resource_cap::{ 44 data_from_event as resource_cap_metadata_from_event, 45 parsed_from_event as resource_cap_index_from_event, resource_harvest_cap_from_event, 46 }; 47 use test_fixtures::{FIXTURE_ALICE_NPUB, FIXTURE_ALICE_PUBLIC_KEY_HEX}; 48 49 const TEST_NPUB: &str = FIXTURE_ALICE_NPUB; 50 const TEST_PUBKEY_HEX: &str = FIXTURE_ALICE_PUBLIC_KEY_HEX; 51 52 fn sample_gcs() -> GcsLocation { 53 GcsLocation { 54 lat: 37.0, 55 lng: -122.0, 56 geohash: "9q8yy".to_string(), 57 point: GeoJsonPoint { 58 r#type: "Point".to_string(), 59 coordinates: [-122.0, 37.0], 60 }, 61 polygon: GeoJsonPolygon { 62 r#type: "Polygon".to_string(), 63 coordinates: vec![vec![ 64 [-122.0, 37.0], 65 [-122.0, 37.0001], 66 [-122.0001, 37.0001], 67 [-122.0, 37.0], 68 ]], 69 }, 70 accuracy: None, 71 altitude: None, 72 tag_0: None, 73 label: None, 74 area: None, 75 elevation: None, 76 soil: None, 77 climate: None, 78 gc_id: None, 79 gc_name: None, 80 gc_admin1_id: None, 81 gc_admin1_name: None, 82 gc_country_id: None, 83 gc_country_name: None, 84 } 85 } 86 87 fn sample_farm(d_tag: &str) -> Farm { 88 Farm { 89 d_tag: d_tag.to_string(), 90 name: "Farm".to_string(), 91 about: None, 92 website: None, 93 picture: None, 94 banner: None, 95 location: None, 96 tags: None, 97 } 98 } 99 100 fn sample_coop(d_tag: &str) -> Coop { 101 Coop { 102 d_tag: d_tag.to_string(), 103 name: "Coop".to_string(), 104 about: None, 105 website: None, 106 picture: None, 107 banner: None, 108 location: None, 109 tags: None, 110 } 111 } 112 113 fn sample_plot(d_tag: &str, farm_pubkey: &str, farm_d_tag: &str) -> Plot { 114 Plot { 115 d_tag: d_tag.to_string(), 116 farm: FarmRef { 117 pubkey: farm_pubkey.to_string(), 118 d_tag: farm_d_tag.to_string(), 119 }, 120 name: "Plot".to_string(), 121 about: None, 122 location: None, 123 tags: None, 124 } 125 } 126 127 fn sample_document(d_tag: &str, subject_pubkey: &str, subject_address: Option<&str>) -> Document { 128 Document { 129 d_tag: d_tag.to_string(), 130 doc_type: "charter".to_string(), 131 title: "Charter".to_string(), 132 version: "1.0.0".to_string(), 133 summary: None, 134 effective_at: None, 135 body_markdown: None, 136 subject: DocumentSubject { 137 pubkey: subject_pubkey.to_string(), 138 address: subject_address.map(str::to_string), 139 }, 140 tags: None, 141 } 142 } 143 144 fn sample_resource_area(d_tag: &str) -> ResourceArea { 145 ResourceArea { 146 d_tag: d_tag.to_string(), 147 name: "Area".to_string(), 148 about: None, 149 location: ResourceAreaLocation { 150 primary: None, 151 city: None, 152 region: None, 153 country: None, 154 gcs: sample_gcs(), 155 }, 156 tags: None, 157 } 158 } 159 160 fn sample_resource_cap(d_tag: &str) -> ResourceHarvestCap { 161 ResourceHarvestCap { 162 d_tag: d_tag.to_string(), 163 resource_area: ResourceAreaRef { 164 pubkey: TEST_PUBKEY_HEX.to_string(), 165 d_tag: "AAAAAAAAAAAAAAAAAAAAAw".to_string(), 166 }, 167 product: ResourceHarvestProduct { 168 key: "nutmeg".to_string(), 169 category: Some("spice".to_string()), 170 }, 171 start: 100, 172 end: 200, 173 cap_quantity: Quantity::try_new(Decimal::from(1000u32), Unit::MassG).unwrap(), 174 display_amount: None, 175 display_unit: None, 176 display_label: None, 177 tags: None, 178 } 179 } 180 181 fn d_tag_tags(d_tag: &str) -> Vec<Vec<String>> { 182 vec![vec![TAG_D.to_string(), d_tag.to_string()]] 183 } 184 185 #[test] 186 fn farm_decode_handles_success_fill_and_error_paths() { 187 let d_tag = "AAAAAAAAAAAAAAAAAAAAAA"; 188 let tags = d_tag_tags(d_tag); 189 let farm = sample_farm(d_tag); 190 let content = serde_json::to_string(&farm).expect("farm content"); 191 let parsed = farm_from_event(KIND_FARM, &tags, &content).expect("farm parse"); 192 assert_eq!(parsed.d_tag, d_tag); 193 194 let mut farm_missing = sample_farm(""); 195 let content_missing = serde_json::to_string(&farm_missing).expect("farm missing content"); 196 let filled = farm_from_event(KIND_FARM, &tags, &content_missing).expect("farm fill d"); 197 assert_eq!(filled.d_tag, d_tag); 198 199 farm_missing.d_tag = "AAAAAAAAAAAAAAAAAAAAAQ".to_string(); 200 let mismatch_content = serde_json::to_string(&farm_missing).expect("farm mismatch content"); 201 let mismatch = farm_from_event(KIND_FARM, &tags, &mismatch_content).expect_err("mismatch"); 202 assert!(matches!(mismatch, EventParseError::InvalidTag("d"))); 203 204 let wrong_kind = farm_from_event(KIND_COOP, &tags, &content).expect_err("wrong kind"); 205 assert!(matches!( 206 wrong_kind, 207 EventParseError::InvalidKind { 208 expected: "30340", 209 got: KIND_COOP 210 } 211 )); 212 213 let missing_d = farm_from_event(KIND_FARM, &[], &content).expect_err("missing d"); 214 assert!(matches!(missing_d, EventParseError::MissingTag("d"))); 215 216 let invalid_d = farm_from_event( 217 KIND_FARM, 218 &[vec![TAG_D.to_string(), "farm:invalid".to_string()]], 219 &content, 220 ) 221 .expect_err("invalid d"); 222 assert!(matches!(invalid_d, EventParseError::InvalidTag("d"))); 223 224 let invalid_json = farm_from_event(KIND_FARM, &tags, "").expect_err("invalid content"); 225 assert!(matches!( 226 invalid_json, 227 EventParseError::InvalidJson("content") 228 )); 229 230 let private_field_content = serde_json::json!({ 231 "d_tag": d_tag, 232 "name": "Farm", 233 "workspace": {"pubkey": TEST_PUBKEY_HEX} 234 }) 235 .to_string(); 236 let private_field = 237 farm_from_event(KIND_FARM, &tags, &private_field_content).expect_err("private field"); 238 assert!(matches!( 239 private_field, 240 EventParseError::InvalidJson("content") 241 )); 242 243 let non_object = farm_from_event(KIND_FARM, &tags, "[]").expect_err("non object"); 244 assert!(matches!( 245 non_object, 246 EventParseError::InvalidJson("content") 247 )); 248 } 249 250 #[test] 251 fn farm_metadata_and_index_decode_roundtrip() { 252 let d_tag = "AAAAAAAAAAAAAAAAAAAAAA"; 253 let content = serde_json::to_string(&sample_farm(d_tag)).expect("farm content"); 254 let tags = d_tag_tags(d_tag); 255 let metadata: RadrootsParsedData<Farm> = farm_metadata_from_event( 256 "id1".to_string(), 257 TEST_PUBKEY_HEX.to_string(), 258 55, 259 KIND_FARM, 260 content.clone(), 261 tags.clone(), 262 ) 263 .expect("farm metadata"); 264 assert_eq!(metadata.id, "id1"); 265 assert_eq!(metadata.data.d_tag, d_tag); 266 267 let index: RadrootsParsedEvent<Farm> = farm_index_from_event( 268 EVENT_ID.to_string(), 269 TEST_PUBKEY_HEX.to_string(), 270 55, 271 KIND_FARM, 272 content, 273 tags, 274 EVENT_SIG.to_string(), 275 ) 276 .expect("farm index"); 277 assert_eq!(index.event.id_hex(), EVENT_ID); 278 assert_eq!(index.data.data.d_tag, d_tag); 279 } 280 281 #[test] 282 fn coop_decode_handles_success_fill_and_error_paths() { 283 let d_tag = "BAAAAAAAAAAAAAAAAAAAAA"; 284 let tags = d_tag_tags(d_tag); 285 let coop = sample_coop(d_tag); 286 let content = serde_json::to_string(&coop).expect("coop content"); 287 let parsed = coop_from_event(KIND_COOP, &tags, &content).expect("coop parse"); 288 assert_eq!(parsed.d_tag, d_tag); 289 290 let content_missing = serde_json::to_string(&sample_coop("")).expect("coop missing content"); 291 let filled = coop_from_event(KIND_COOP, &tags, &content_missing).expect("coop fill d"); 292 assert_eq!(filled.d_tag, d_tag); 293 294 let mismatch_content = 295 serde_json::to_string(&sample_coop("AAAAAAAAAAAAAAAAAAAAAQ")).expect("coop mismatch"); 296 let mismatch = coop_from_event(KIND_COOP, &tags, &mismatch_content).expect_err("mismatch"); 297 assert!(matches!(mismatch, EventParseError::InvalidTag("d"))); 298 299 let wrong_kind = coop_from_event(KIND_FARM, &tags, &content).expect_err("wrong kind"); 300 assert!(matches!( 301 wrong_kind, 302 EventParseError::InvalidKind { 303 expected: "30360", 304 got: KIND_FARM 305 } 306 )); 307 308 let missing_d = coop_from_event(KIND_COOP, &[], &content).expect_err("missing d"); 309 assert!(matches!(missing_d, EventParseError::MissingTag("d"))); 310 } 311 312 #[test] 313 fn coop_metadata_and_index_decode_roundtrip() { 314 let d_tag = "BAAAAAAAAAAAAAAAAAAAAA"; 315 let content = serde_json::to_string(&sample_coop(d_tag)).expect("coop content"); 316 let tags = d_tag_tags(d_tag); 317 let metadata: RadrootsParsedData<Coop> = coop_metadata_from_event( 318 "id2".to_string(), 319 TEST_PUBKEY_HEX.to_string(), 320 56, 321 KIND_COOP, 322 content.clone(), 323 tags.clone(), 324 ) 325 .expect("coop metadata"); 326 assert_eq!(metadata.id, "id2"); 327 assert_eq!(metadata.data.d_tag, d_tag); 328 329 let index: RadrootsParsedEvent<Coop> = coop_index_from_event( 330 EVENT_ID.to_string(), 331 TEST_PUBKEY_HEX.to_string(), 332 56, 333 KIND_COOP, 334 content, 335 tags, 336 EVENT_SIG.to_string(), 337 ) 338 .expect("coop index"); 339 assert_eq!(index.event.kind_u32(), KIND_COOP); 340 assert_eq!(index.data.data.d_tag, d_tag); 341 } 342 343 #[test] 344 fn plot_decode_handles_success_fill_and_tag_error_paths() { 345 let d_tag = "AAAAAAAAAAAAAAAAAAAAAQ"; 346 let farm_d_tag = "AAAAAAAAAAAAAAAAAAAAAA"; 347 let tags = vec![ 348 vec![TAG_D.to_string(), d_tag.to_string()], 349 vec![ 350 "a".to_string(), 351 format!("30340:{TEST_PUBKEY_HEX}:{farm_d_tag}"), 352 ], 353 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 354 ]; 355 356 let content = serde_json::to_string(&sample_plot(d_tag, TEST_PUBKEY_HEX, farm_d_tag)) 357 .expect("plot content"); 358 let parsed = plot_from_event(KIND_PLOT, &tags, &content).expect("plot parse"); 359 assert_eq!(parsed.farm.pubkey, TEST_PUBKEY_HEX); 360 361 let filled_content = 362 serde_json::to_string(&sample_plot("", "", "")).expect("plot missing content"); 363 let filled = plot_from_event(KIND_PLOT, &tags, &filled_content).expect("plot fill"); 364 assert_eq!(filled.d_tag, d_tag); 365 assert_eq!(filled.farm.d_tag, farm_d_tag); 366 367 let partial_farm_content = 368 serde_json::to_string(&sample_plot(d_tag, TEST_PUBKEY_HEX, "")).expect("plot partial farm"); 369 let partial_farm = 370 plot_from_event(KIND_PLOT, &tags, &partial_farm_content).expect("partial farm fill"); 371 assert_eq!(partial_farm.farm.d_tag, farm_d_tag); 372 373 let bad_a = plot_from_event( 374 KIND_PLOT, 375 &[ 376 vec![TAG_D.to_string(), d_tag.to_string()], 377 vec![ 378 "a".to_string(), 379 format!("30361:{TEST_PUBKEY_HEX}:AAAAAAAAAAAAAAAAAAAAAA"), 380 ], 381 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 382 ], 383 &content, 384 ) 385 .expect_err("bad a"); 386 assert!(matches!(bad_a, EventParseError::InvalidTag("a"))); 387 388 let bad_p = plot_from_event( 389 KIND_PLOT, 390 &[ 391 vec![TAG_D.to_string(), d_tag.to_string()], 392 vec![ 393 "a".to_string(), 394 format!("30340:{TEST_PUBKEY_HEX}:{farm_d_tag}"), 395 ], 396 vec!["p".to_string(), TEST_NPUB.to_string()], 397 ], 398 &content, 399 ) 400 .expect_err("bad p"); 401 assert!(matches!(bad_p, EventParseError::InvalidTag("p"))); 402 403 let missing_a = plot_from_event( 404 KIND_PLOT, 405 &[ 406 vec![TAG_D.to_string(), d_tag.to_string()], 407 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 408 ], 409 &content, 410 ) 411 .expect_err("missing a"); 412 assert!(matches!(missing_a, EventParseError::MissingTag("a"))); 413 414 let invalid_kind = plot_from_event(KIND_FARM, &tags, &content).expect_err("invalid kind"); 415 assert!(matches!( 416 invalid_kind, 417 EventParseError::InvalidKind { 418 expected: "30350", 419 got: KIND_FARM 420 } 421 )); 422 423 let blank_content = plot_from_event(KIND_PLOT, &tags, " ").expect_err("blank content"); 424 assert!(matches!( 425 blank_content, 426 EventParseError::InvalidJson("content") 427 )); 428 429 let empty_d = plot_from_event( 430 KIND_PLOT, 431 &[ 432 vec![TAG_D.to_string(), " ".to_string()], 433 vec![ 434 "a".to_string(), 435 format!("30340:{TEST_PUBKEY_HEX}:{farm_d_tag}"), 436 ], 437 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 438 ], 439 &content, 440 ) 441 .expect_err("empty d"); 442 assert!(matches!(empty_d, EventParseError::InvalidTag("d"))); 443 444 let empty_a_pubkey = plot_from_event( 445 KIND_PLOT, 446 &[ 447 vec![TAG_D.to_string(), d_tag.to_string()], 448 vec!["a".to_string(), format!("30340::{farm_d_tag}")], 449 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 450 ], 451 &content, 452 ) 453 .expect_err("empty a pubkey"); 454 assert!(matches!(empty_a_pubkey, EventParseError::InvalidTag("a"))); 455 456 let empty_a_d_tag = plot_from_event( 457 KIND_PLOT, 458 &[ 459 vec![TAG_D.to_string(), d_tag.to_string()], 460 vec!["a".to_string(), format!("30340:{TEST_PUBKEY_HEX}:")], 461 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 462 ], 463 &content, 464 ) 465 .expect_err("empty a d_tag"); 466 assert!(matches!(empty_a_d_tag, EventParseError::InvalidTag("a"))); 467 468 let empty_p = plot_from_event( 469 KIND_PLOT, 470 &[ 471 vec![TAG_D.to_string(), d_tag.to_string()], 472 vec![ 473 "a".to_string(), 474 format!("30340:{TEST_PUBKEY_HEX}:{farm_d_tag}"), 475 ], 476 vec!["p".to_string(), " ".to_string()], 477 ], 478 &content, 479 ) 480 .expect_err("empty p"); 481 assert!(matches!(empty_p, EventParseError::InvalidTag("p"))); 482 483 let mismatched_d_content = serde_json::to_string(&sample_plot( 484 "BAAAAAAAAAAAAAAAAAAAAA", 485 TEST_PUBKEY_HEX, 486 farm_d_tag, 487 )) 488 .expect("mismatched d content"); 489 let mismatched_d = 490 plot_from_event(KIND_PLOT, &tags, &mismatched_d_content).expect_err("mismatched d"); 491 assert!(matches!(mismatched_d, EventParseError::InvalidTag("d"))); 492 493 let mismatched_farm_pubkey_content = 494 serde_json::to_string(&sample_plot(d_tag, TEST_NPUB, farm_d_tag)) 495 .expect("mismatched farm pubkey content"); 496 let mismatched_farm_pubkey = plot_from_event(KIND_PLOT, &tags, &mismatched_farm_pubkey_content) 497 .expect_err("mismatched farm pubkey"); 498 assert!(matches!( 499 mismatched_farm_pubkey, 500 EventParseError::InvalidTag("a") 501 )); 502 503 let mismatched_farm_d_tag_content = serde_json::to_string(&sample_plot( 504 d_tag, 505 TEST_PUBKEY_HEX, 506 "BAAAAAAAAAAAAAAAAAAAAA", 507 )) 508 .expect("mismatched farm d_tag content"); 509 let mismatched_farm_d_tag = plot_from_event(KIND_PLOT, &tags, &mismatched_farm_d_tag_content) 510 .expect_err("mismatched farm d_tag"); 511 assert!(matches!( 512 mismatched_farm_d_tag, 513 EventParseError::InvalidTag("a") 514 )); 515 516 let parsed_err = plot_index_from_event( 517 "id".to_string(), 518 TEST_PUBKEY_HEX.to_string(), 519 57, 520 KIND_FARM, 521 content, 522 tags, 523 "sig".to_string(), 524 ) 525 .expect_err("parsed error"); 526 assert!(matches!( 527 parsed_err, 528 EventParseError::InvalidKind { 529 expected: "30350", 530 got: KIND_FARM 531 } 532 )); 533 } 534 535 #[test] 536 fn plot_metadata_and_index_decode_roundtrip() { 537 let d_tag = "AAAAAAAAAAAAAAAAAAAAAQ"; 538 let farm_d_tag = "AAAAAAAAAAAAAAAAAAAAAA"; 539 let tags = vec![ 540 vec![TAG_D.to_string(), d_tag.to_string()], 541 vec![ 542 "a".to_string(), 543 format!("30340:{TEST_PUBKEY_HEX}:{farm_d_tag}"), 544 ], 545 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 546 ]; 547 let content = serde_json::to_string(&sample_plot(d_tag, TEST_PUBKEY_HEX, farm_d_tag)) 548 .expect("plot content"); 549 550 let metadata: RadrootsParsedData<Plot> = plot_metadata_from_event( 551 "id3".to_string(), 552 TEST_PUBKEY_HEX.to_string(), 553 57, 554 KIND_PLOT, 555 content.clone(), 556 tags.clone(), 557 ) 558 .expect("plot metadata"); 559 assert_eq!(metadata.data.d_tag, d_tag); 560 561 let index: RadrootsParsedEvent<Plot> = plot_index_from_event( 562 EVENT_ID.to_string(), 563 TEST_PUBKEY_HEX.to_string(), 564 57, 565 KIND_PLOT, 566 content, 567 tags, 568 EVENT_SIG.to_string(), 569 ) 570 .expect("plot index"); 571 assert_eq!(index.event.author().to_hex(), TEST_PUBKEY_HEX); 572 assert_eq!(index.data.data.d_tag, d_tag); 573 } 574 575 #[test] 576 fn document_decode_handles_subject_and_address_paths() { 577 let d_tag = "EAAAAAAAAAAAAAAAAAAAAA"; 578 let tag_address = format!("30360:{TEST_PUBKEY_HEX}:BAAAAAAAAAAAAAAAAAAAAA"); 579 let tags = vec![ 580 vec![TAG_D.to_string(), d_tag.to_string()], 581 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 582 vec!["a".to_string(), tag_address.clone()], 583 ]; 584 let content = 585 serde_json::to_string(&sample_document(d_tag, TEST_PUBKEY_HEX, Some(&tag_address))) 586 .expect("document content"); 587 let parsed = document_from_event(KIND_DOCUMENT, &tags, &content).expect("document parse"); 588 assert_eq!(parsed.subject.pubkey, TEST_PUBKEY_HEX); 589 assert_eq!( 590 parsed.subject.address.as_deref(), 591 Some(tag_address.as_str()) 592 ); 593 594 let fill_content = serde_json::to_string(&sample_document("", "", None)).expect("fill"); 595 let filled = document_from_event(KIND_DOCUMENT, &tags, &fill_content).expect("document fill"); 596 assert_eq!(filled.d_tag, d_tag); 597 assert_eq!(filled.subject.pubkey, TEST_PUBKEY_HEX); 598 assert_eq!( 599 filled.subject.address.as_deref(), 600 Some(tag_address.as_str()) 601 ); 602 603 let missing_a_err = document_from_event( 604 KIND_DOCUMENT, 605 &[ 606 vec![TAG_D.to_string(), d_tag.to_string()], 607 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 608 ], 609 &content, 610 ) 611 .expect_err("missing a"); 612 assert!(matches!(missing_a_err, EventParseError::MissingTag("a"))); 613 614 let mismatch_p_content = 615 serde_json::to_string(&sample_document(d_tag, TEST_NPUB, Some(&tag_address))) 616 .expect("mismatch p content"); 617 let mismatch_p = 618 document_from_event(KIND_DOCUMENT, &tags, &mismatch_p_content).expect_err("mismatch p"); 619 assert!(matches!(mismatch_p, EventParseError::InvalidTag("p"))); 620 621 let empty_a_content = 622 serde_json::to_string(&sample_document(d_tag, TEST_PUBKEY_HEX, Some(""))).expect("empty a"); 623 let empty_a = 624 document_from_event(KIND_DOCUMENT, &tags, &empty_a_content).expect_err("empty address"); 625 assert!(matches!(empty_a, EventParseError::InvalidTag("a"))); 626 627 let invalid_kind = document_from_event(KIND_FARM, &tags, &content).expect_err("invalid kind"); 628 assert!(matches!( 629 invalid_kind, 630 EventParseError::InvalidKind { 631 expected: "30361", 632 got: KIND_FARM 633 } 634 )); 635 636 let blank_content = document_from_event(KIND_DOCUMENT, &tags, " ").expect_err("blank content"); 637 assert!(matches!( 638 blank_content, 639 EventParseError::InvalidJson("content") 640 )); 641 642 let empty_d_tag = document_from_event( 643 KIND_DOCUMENT, 644 &[ 645 vec![TAG_D.to_string(), " ".to_string()], 646 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 647 vec!["a".to_string(), tag_address.clone()], 648 ], 649 &content, 650 ) 651 .expect_err("empty d"); 652 assert!(matches!(empty_d_tag, EventParseError::InvalidTag("d"))); 653 654 let empty_p_tag = document_from_event( 655 KIND_DOCUMENT, 656 &[ 657 vec![TAG_D.to_string(), d_tag.to_string()], 658 vec!["p".to_string(), " ".to_string()], 659 vec!["a".to_string(), tag_address.clone()], 660 ], 661 &content, 662 ) 663 .expect_err("empty p"); 664 assert!(matches!(empty_p_tag, EventParseError::InvalidTag("p"))); 665 666 let empty_a_tag = document_from_event( 667 KIND_DOCUMENT, 668 &[ 669 vec![TAG_D.to_string(), d_tag.to_string()], 670 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 671 vec!["a".to_string(), " ".to_string()], 672 ], 673 &content, 674 ) 675 .expect_err("empty a tag"); 676 assert!(matches!(empty_a_tag, EventParseError::InvalidTag("a"))); 677 678 let mismatched_d_content = serde_json::to_string(&sample_document( 679 "FAAAAAAAAAAAAAAAAAAAAA", 680 TEST_PUBKEY_HEX, 681 None, 682 )) 683 .expect("mismatched d content"); 684 let mismatched_d = 685 document_from_event(KIND_DOCUMENT, &tags, &mismatched_d_content).expect_err("mismatched d"); 686 assert!(matches!(mismatched_d, EventParseError::InvalidTag("d"))); 687 688 let mismatched_a_content = serde_json::to_string(&sample_document( 689 d_tag, 690 TEST_PUBKEY_HEX, 691 Some("30360:author:other"), 692 )) 693 .expect("mismatched address content"); 694 let mismatched_a = 695 document_from_event(KIND_DOCUMENT, &tags, &mismatched_a_content).expect_err("mismatched a"); 696 assert!(matches!(mismatched_a, EventParseError::InvalidTag("a"))); 697 698 let parsed_err = document_index_from_event( 699 "id".to_string(), 700 TEST_PUBKEY_HEX.to_string(), 701 58, 702 KIND_FARM, 703 content, 704 tags, 705 "sig".to_string(), 706 ) 707 .expect_err("parsed error"); 708 assert!(matches!( 709 parsed_err, 710 EventParseError::InvalidKind { 711 expected: "30361", 712 got: KIND_FARM 713 } 714 )); 715 } 716 717 #[test] 718 fn document_metadata_and_index_decode_roundtrip() { 719 let d_tag = "EAAAAAAAAAAAAAAAAAAAAA"; 720 let tag_address = format!("30360:{TEST_PUBKEY_HEX}:BAAAAAAAAAAAAAAAAAAAAA"); 721 let tags = vec![ 722 vec![TAG_D.to_string(), d_tag.to_string()], 723 vec!["p".to_string(), TEST_PUBKEY_HEX.to_string()], 724 vec!["a".to_string(), tag_address.clone()], 725 ]; 726 let content = 727 serde_json::to_string(&sample_document(d_tag, TEST_PUBKEY_HEX, Some(&tag_address))) 728 .expect("document content"); 729 730 let metadata: RadrootsParsedData<Document> = document_metadata_from_event( 731 "id4".to_string(), 732 TEST_PUBKEY_HEX.to_string(), 733 58, 734 KIND_DOCUMENT, 735 content.clone(), 736 tags.clone(), 737 ) 738 .expect("document metadata"); 739 assert_eq!(metadata.data.d_tag, d_tag); 740 741 let index: RadrootsParsedEvent<Document> = document_index_from_event( 742 EVENT_ID.to_string(), 743 TEST_PUBKEY_HEX.to_string(), 744 58, 745 KIND_DOCUMENT, 746 content, 747 tags, 748 EVENT_SIG.to_string(), 749 ) 750 .expect("document index"); 751 assert_eq!(index.event.kind_u32(), KIND_DOCUMENT); 752 assert_eq!(index.data.data.d_tag, d_tag); 753 } 754 755 #[test] 756 fn resource_area_decode_handles_success_fill_and_errors() { 757 let d_tag = "AAAAAAAAAAAAAAAAAAAAAw"; 758 let tags = d_tag_tags(d_tag); 759 let area = sample_resource_area(d_tag); 760 let content = serde_json::to_string(&area).expect("area content"); 761 let parsed = resource_area_from_event(KIND_RESOURCE_AREA, &tags, &content).expect("area"); 762 assert_eq!(parsed.d_tag, d_tag); 763 764 let fill_content = serde_json::to_string(&sample_resource_area("")).expect("area fill"); 765 let filled = 766 resource_area_from_event(KIND_RESOURCE_AREA, &tags, &fill_content).expect("area fill"); 767 assert_eq!(filled.d_tag, d_tag); 768 769 let mismatch_content = 770 serde_json::to_string(&sample_resource_area("AAAAAAAAAAAAAAAAAAAAAQ")).expect("mismatch"); 771 let mismatch = 772 resource_area_from_event(KIND_RESOURCE_AREA, &tags, &mismatch_content).expect_err("m"); 773 assert!(matches!(mismatch, EventParseError::InvalidTag("d"))); 774 775 let wrong_kind = resource_area_from_event(KIND_FARM, &tags, &content).expect_err("wrong kind"); 776 assert!(matches!( 777 wrong_kind, 778 EventParseError::InvalidKind { 779 expected: "30370", 780 got: KIND_FARM 781 } 782 )); 783 784 let blank_content = 785 resource_area_from_event(KIND_RESOURCE_AREA, &tags, " ").expect_err("blank content"); 786 assert!(matches!( 787 blank_content, 788 EventParseError::InvalidJson("content") 789 )); 790 791 let missing_d = 792 resource_area_from_event(KIND_RESOURCE_AREA, &[], &content).expect_err("missing d"); 793 assert!(matches!(missing_d, EventParseError::MissingTag("d"))); 794 795 let invalid_d = resource_area_from_event( 796 KIND_RESOURCE_AREA, 797 &[vec![TAG_D.to_string(), " ".to_string()]], 798 &content, 799 ) 800 .expect_err("invalid d"); 801 assert!(matches!(invalid_d, EventParseError::InvalidTag("d"))); 802 } 803 804 #[test] 805 fn resource_area_metadata_and_index_decode_roundtrip() { 806 let d_tag = "AAAAAAAAAAAAAAAAAAAAAw"; 807 let content = serde_json::to_string(&sample_resource_area(d_tag)).expect("area content"); 808 let tags = d_tag_tags(d_tag); 809 let metadata: RadrootsParsedData<ResourceArea> = resource_area_metadata_from_event( 810 "id5".to_string(), 811 TEST_PUBKEY_HEX.to_string(), 812 59, 813 KIND_RESOURCE_AREA, 814 content.clone(), 815 tags.clone(), 816 ) 817 .expect("area metadata"); 818 assert_eq!(metadata.data.d_tag, d_tag); 819 820 let index: RadrootsParsedEvent<ResourceArea> = resource_area_index_from_event( 821 EVENT_ID.to_string(), 822 TEST_PUBKEY_HEX.to_string(), 823 59, 824 KIND_RESOURCE_AREA, 825 content, 826 tags, 827 EVENT_SIG.to_string(), 828 ) 829 .expect("area index"); 830 assert_eq!(index.event.id_hex(), EVENT_ID); 831 assert_eq!(index.data.data.d_tag, d_tag); 832 } 833 834 #[test] 835 fn resource_cap_decode_handles_success_fill_and_errors() { 836 let d_tag = "DAAAAAAAAAAAAAAAAAAAAA"; 837 let tags = d_tag_tags(d_tag); 838 let cap = sample_resource_cap(d_tag); 839 let content = serde_json::to_string(&cap).expect("cap content"); 840 let parsed = resource_harvest_cap_from_event(KIND_RESOURCE_HARVEST_CAP, &tags, &content) 841 .expect("cap parse"); 842 assert_eq!(parsed.d_tag, d_tag); 843 844 let fill_content = serde_json::to_string(&sample_resource_cap("")).expect("cap fill"); 845 let filled = resource_harvest_cap_from_event(KIND_RESOURCE_HARVEST_CAP, &tags, &fill_content) 846 .expect("cap fill parse"); 847 assert_eq!(filled.d_tag, d_tag); 848 849 let mismatch_content = 850 serde_json::to_string(&sample_resource_cap("AAAAAAAAAAAAAAAAAAAAAQ")).expect("mismatch"); 851 let mismatch = 852 resource_harvest_cap_from_event(KIND_RESOURCE_HARVEST_CAP, &tags, &mismatch_content) 853 .expect_err("cap mismatch"); 854 assert!(matches!(mismatch, EventParseError::InvalidTag("d"))); 855 856 let wrong_kind = 857 resource_harvest_cap_from_event(KIND_FARM, &tags, &content).expect_err("wrong kind"); 858 assert!(matches!( 859 wrong_kind, 860 EventParseError::InvalidKind { 861 expected: "30371", 862 got: KIND_FARM 863 } 864 )); 865 866 let blank_content = resource_harvest_cap_from_event(KIND_RESOURCE_HARVEST_CAP, &tags, " ") 867 .expect_err("blank content"); 868 assert!(matches!( 869 blank_content, 870 EventParseError::InvalidJson("content") 871 )); 872 873 let missing_d = resource_harvest_cap_from_event(KIND_RESOURCE_HARVEST_CAP, &[], &content) 874 .expect_err("missing d"); 875 assert!(matches!(missing_d, EventParseError::MissingTag("d"))); 876 877 let invalid_d = resource_harvest_cap_from_event( 878 KIND_RESOURCE_HARVEST_CAP, 879 &[vec![TAG_D.to_string(), " ".to_string()]], 880 &content, 881 ) 882 .expect_err("invalid d"); 883 assert!(matches!(invalid_d, EventParseError::InvalidTag("d"))); 884 } 885 886 #[test] 887 fn resource_cap_metadata_and_index_decode_roundtrip() { 888 let d_tag = "DAAAAAAAAAAAAAAAAAAAAA"; 889 let content = serde_json::to_string(&sample_resource_cap(d_tag)).expect("cap content"); 890 let tags = d_tag_tags(d_tag); 891 let metadata: RadrootsParsedData<ResourceHarvestCap> = resource_cap_metadata_from_event( 892 "id6".to_string(), 893 TEST_PUBKEY_HEX.to_string(), 894 60, 895 KIND_RESOURCE_HARVEST_CAP, 896 content.clone(), 897 tags.clone(), 898 ) 899 .expect("cap metadata"); 900 assert_eq!(metadata.data.d_tag, d_tag); 901 902 let index: RadrootsParsedEvent<ResourceHarvestCap> = resource_cap_index_from_event( 903 EVENT_ID.to_string(), 904 TEST_PUBKEY_HEX.to_string(), 905 60, 906 KIND_RESOURCE_HARVEST_CAP, 907 content, 908 tags, 909 EVENT_SIG.to_string(), 910 ) 911 .expect("cap index"); 912 assert_eq!(index.event.signature_hex(), EVENT_SIG); 913 assert_eq!(index.data.data.d_tag, d_tag); 914 }