mod.rs (25396B)
1 pub mod encode; 2 3 #[cfg(feature = "serde_json")] 4 pub mod decode; 5 6 #[cfg(all(test, feature = "serde_json"))] 7 mod tests { 8 use radroots_events::{ 9 farm_crdt::{ 10 KIND_FARM_CRDT_CHANGE, RADROOTS_FARM_CRDT_CHANGE_SCHEMA, RADROOTS_FARM_CRDT_TAG, 11 RadrootsCrdtBackend, RadrootsFarmCrdtChange, RadrootsFarmCrdtDocumentKind, 12 RadrootsFarmSemanticKind, 13 }, 14 farm_workspace::{KIND_FARM_WORKSPACE_MANIFEST, RadrootsFarmWorkspaceRef}, 15 kinds::KIND_POST, 16 }; 17 18 use crate::error::{EventEncodeError, EventParseError}; 19 use crate::farm_crdt::decode::{ 20 data_from_event, farm_crdt_change_from_event, farm_crdt_change_from_event_with_author, 21 parsed_from_event, 22 }; 23 use crate::farm_crdt::encode::{ 24 farm_crdt_change_build_tags, farm_crdt_change_build_tags_with_author, to_wire_parts, 25 to_wire_parts_with_author, to_wire_parts_with_kind, to_wire_parts_with_kind_and_author, 26 }; 27 28 const WORKSPACE_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; 29 const DOCUMENT_ID: &str = "AAAAAAAAAAAAAAAAAAAAAg"; 30 const GROUP_ID: &str = "field-group"; 31 const AUTHOR: &str = "author_pubkey"; 32 33 #[test] 34 fn farm_crdt_change_encodes_and_decodes_task_change() { 35 let change = sample_change(); 36 let parts = to_wire_parts_with_author(&change, AUTHOR).expect("crdt wire parts"); 37 38 assert_eq!(parts.kind, KIND_FARM_CRDT_CHANGE); 39 assert!(parts.tags.contains(&tag("h", GROUP_ID))); 40 assert!(parts.tags.contains(&tag("d", DOCUMENT_ID))); 41 assert!( 42 parts 43 .tags 44 .contains(&tag("a", "30078:workspace_pubkey:AAAAAAAAAAAAAAAAAAAAAA")) 45 ); 46 assert!(parts.tags.contains(&tag("p", AUTHOR))); 47 assert!(parts.tags.contains(&tag("t", RADROOTS_FARM_CRDT_TAG))); 48 49 let decoded = farm_crdt_change_from_event_with_author( 50 parts.kind, 51 &parts.tags, 52 &parts.content, 53 AUTHOR, 54 ) 55 .expect("crdt decode"); 56 assert_eq!(decoded.schema, RADROOTS_FARM_CRDT_CHANGE_SCHEMA); 57 assert_eq!(decoded.document_id, DOCUMENT_ID); 58 assert_eq!(decoded.workspace.d_tag, WORKSPACE_D_TAG); 59 assert_eq!(decoded.business_time_ms, 1_780_000_000_000); 60 } 61 62 #[test] 63 fn farm_crdt_change_roundtrips_representative_mvp_semantics() { 64 let cases = vec![ 65 ( 66 RadrootsFarmCrdtDocumentKind::FarmTask, 67 RadrootsFarmSemanticKind::FarmTaskCreate, 68 ), 69 ( 70 RadrootsFarmCrdtDocumentKind::FarmTask, 71 RadrootsFarmSemanticKind::FarmTaskStatusSet, 72 ), 73 ( 74 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 75 RadrootsFarmSemanticKind::FarmWorkSessionStart, 76 ), 77 ( 78 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 79 RadrootsFarmSemanticKind::FarmWorkSessionStop, 80 ), 81 ( 82 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 83 RadrootsFarmSemanticKind::FarmWorkSessionSubmit, 84 ), 85 ( 86 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 87 RadrootsFarmSemanticKind::FarmWorkSessionApprove, 88 ), 89 ( 90 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 91 RadrootsFarmSemanticKind::FarmWorkSessionReject, 92 ), 93 ( 94 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 95 RadrootsFarmSemanticKind::FarmWorkSessionCorrect, 96 ), 97 ( 98 RadrootsFarmCrdtDocumentKind::FarmHarvestRecord, 99 RadrootsFarmSemanticKind::FarmHarvestLineAdd, 100 ), 101 ( 102 RadrootsFarmCrdtDocumentKind::FarmHarvestRecord, 103 RadrootsFarmSemanticKind::FarmHarvestLineCorrect, 104 ), 105 ( 106 RadrootsFarmCrdtDocumentKind::FarmHarvestRecord, 107 RadrootsFarmSemanticKind::FarmHarvestLineVoid, 108 ), 109 ( 110 RadrootsFarmCrdtDocumentKind::FarmMembership, 111 RadrootsFarmSemanticKind::FarmMemberInviteCreate, 112 ), 113 ( 114 RadrootsFarmCrdtDocumentKind::FarmMembership, 115 RadrootsFarmSemanticKind::FarmMemberApprove, 116 ), 117 ( 118 RadrootsFarmCrdtDocumentKind::FarmMembership, 119 RadrootsFarmSemanticKind::FarmMemberRoleSet, 120 ), 121 ( 122 RadrootsFarmCrdtDocumentKind::FarmPayPeriod, 123 RadrootsFarmSemanticKind::FarmPayPeriodClose, 124 ), 125 ( 126 RadrootsFarmCrdtDocumentKind::FarmPayPeriod, 127 RadrootsFarmSemanticKind::FarmReportExportMark, 128 ), 129 ]; 130 131 for (index, (document_kind, semantic_kind)) in cases.into_iter().enumerate() { 132 let document_id = document_id(index); 133 let change = sample_change_with(document_id.as_str(), document_kind, semantic_kind); 134 let parts = to_wire_parts_with_author(&change, AUTHOR).expect("crdt wire parts"); 135 let decoded = farm_crdt_change_from_event_with_author( 136 parts.kind, 137 &parts.tags, 138 &parts.content, 139 AUTHOR, 140 ) 141 .expect("crdt decode"); 142 143 assert_eq!(decoded.document_id, document_id); 144 assert_eq!(decoded.document_kind, change.document_kind); 145 assert_eq!(decoded.semantic_kind, change.semantic_kind); 146 } 147 } 148 149 #[test] 150 fn farm_crdt_change_rejects_missing_t_and_d_mismatch() { 151 let parts = to_wire_parts(&sample_change()).expect("crdt wire parts"); 152 let without_t = parts 153 .tags 154 .iter() 155 .filter(|tag| tag.first().map(|value| value.as_str()) != Some("t")) 156 .cloned() 157 .collect::<Vec<_>>(); 158 159 let missing_t = 160 farm_crdt_change_from_event(parts.kind, &without_t, &parts.content).unwrap_err(); 161 assert!(matches!(missing_t, EventParseError::MissingTag("t"))); 162 163 let mut mismatched_d = parts.tags.clone(); 164 for tag in mismatched_d.iter_mut() { 165 if tag.first().map(|value| value.as_str()) == Some("d") { 166 tag[1] = WORKSPACE_D_TAG.to_string(); 167 } 168 } 169 let mismatch = 170 farm_crdt_change_from_event(parts.kind, &mismatched_d, &parts.content).unwrap_err(); 171 assert!(matches!(mismatch, EventParseError::InvalidTag("d"))); 172 } 173 174 #[test] 175 fn farm_crdt_change_rejects_bad_workspace_address_and_author() { 176 let parts = to_wire_parts_with_author(&sample_change(), AUTHOR).expect("crdt wire parts"); 177 let mut bad_workspace = parts.tags.clone(); 178 for tag in bad_workspace.iter_mut() { 179 if tag.first().map(|value| value.as_str()) == Some("a") { 180 tag[1] = format!("{KIND_FARM_WORKSPACE_MANIFEST}:workspace_pubkey:bad"); 181 } 182 } 183 let workspace_err = 184 farm_crdt_change_from_event(parts.kind, &bad_workspace, &parts.content).unwrap_err(); 185 assert!(matches!(workspace_err, EventParseError::InvalidTag("a"))); 186 187 let author_err = farm_crdt_change_from_event_with_author( 188 parts.kind, 189 &parts.tags, 190 &parts.content, 191 "other_author", 192 ) 193 .unwrap_err(); 194 assert!(matches!(author_err, EventParseError::InvalidTag("p"))); 195 } 196 197 #[test] 198 fn farm_crdt_change_rejects_bad_encoded_change_missing_h_and_kind() { 199 let mut bad_change = sample_change(); 200 bad_change.encoded_change = "abc/def".to_string(); 201 let encode_err = farm_crdt_change_build_tags(&bad_change).unwrap_err(); 202 assert!(matches!( 203 encode_err, 204 EventEncodeError::InvalidField("encoded_change") 205 )); 206 207 let parts = to_wire_parts(&sample_change()).expect("crdt wire parts"); 208 let without_h = parts 209 .tags 210 .iter() 211 .filter(|tag| tag.first().map(|value| value.as_str()) != Some("h")) 212 .cloned() 213 .collect::<Vec<_>>(); 214 let missing_h = 215 farm_crdt_change_from_event(parts.kind, &without_h, &parts.content).unwrap_err(); 216 assert!(matches!(missing_h, EventParseError::MissingTag("h"))); 217 218 let wrong_kind = to_wire_parts_with_kind(&sample_change(), KIND_POST).unwrap_err(); 219 assert!(matches!( 220 wrong_kind, 221 EventEncodeError::InvalidKind(KIND_POST) 222 )); 223 224 let decode_wrong_kind = 225 farm_crdt_change_from_event(KIND_POST, &parts.tags, &parts.content).unwrap_err(); 226 assert!(matches!( 227 decode_wrong_kind, 228 EventParseError::InvalidKind { 229 expected: "78", 230 got: KIND_POST 231 } 232 )); 233 } 234 235 #[test] 236 fn farm_crdt_change_rejects_zero_business_time_and_schema_mismatch() { 237 let mut zero_time = sample_change(); 238 zero_time.business_time_ms = 0; 239 let zero_err = to_wire_parts(&zero_time).unwrap_err(); 240 assert!(matches!( 241 zero_err, 242 EventEncodeError::InvalidField("business_time_ms") 243 )); 244 245 let parts = to_wire_parts(&sample_change()).expect("crdt wire parts"); 246 let mut bad_schema = sample_change(); 247 bad_schema.schema = "radroots.farm.crdt.invalid".to_string(); 248 let content = serde_json::to_string(&bad_schema).expect("bad schema content"); 249 let schema_err = 250 farm_crdt_change_from_event(parts.kind, &parts.tags, &content).unwrap_err(); 251 assert!(matches!(schema_err, EventParseError::InvalidJson("schema"))); 252 } 253 254 #[test] 255 fn farm_crdt_change_wrappers_preserve_event_metadata() { 256 let change = sample_change(); 257 let parts = to_wire_parts_with_author(&change, AUTHOR).expect("crdt wire parts"); 258 259 let data = data_from_event( 260 "event-id".to_string(), 261 AUTHOR.to_string(), 262 99, 263 parts.kind, 264 parts.content.clone(), 265 parts.tags.clone(), 266 ) 267 .expect("parsed data"); 268 assert_eq!(data.id, "event-id"); 269 assert_eq!(data.author, AUTHOR); 270 assert_eq!(data.published_at, 99); 271 assert_eq!(data.kind, KIND_FARM_CRDT_CHANGE); 272 assert_eq!(data.data, change); 273 274 let parsed = parsed_from_event( 275 "event-id".to_string(), 276 AUTHOR.to_string(), 277 99, 278 parts.kind, 279 parts.content, 280 parts.tags, 281 "sig".to_string(), 282 ) 283 .expect("parsed event"); 284 assert_eq!(parsed.event.sig, "sig"); 285 assert_eq!(parsed.data.data, change); 286 287 let no_author_parts = to_wire_parts(&change).expect("crdt wire parts"); 288 let decoded = farm_crdt_change_from_event_with_author( 289 no_author_parts.kind, 290 &no_author_parts.tags, 291 &no_author_parts.content, 292 AUTHOR, 293 ) 294 .expect("author context without p tag remains valid"); 295 assert_eq!(decoded, change); 296 297 let empty_author = farm_crdt_change_from_event_with_author( 298 no_author_parts.kind, 299 &no_author_parts.tags, 300 &no_author_parts.content, 301 " ", 302 ) 303 .unwrap_err(); 304 assert!(matches!(empty_author, EventParseError::InvalidTag("p"))); 305 } 306 307 #[test] 308 fn farm_crdt_change_rejects_decode_tag_and_content_edges() { 309 let parts = to_wire_parts_with_author(&sample_change(), AUTHOR).expect("crdt wire parts"); 310 311 let empty_content = farm_crdt_change_from_event(parts.kind, &parts.tags, " ").unwrap_err(); 312 assert!(matches!( 313 empty_content, 314 EventParseError::InvalidJson("content") 315 )); 316 317 let bad_json = farm_crdt_change_from_event(parts.kind, &parts.tags, "{").unwrap_err(); 318 assert!(matches!(bad_json, EventParseError::InvalidJson("content"))); 319 320 let mut empty_author_tag = parts.tags.clone(); 321 replace_first_tag(&mut empty_author_tag, "p", tag("p", " ")); 322 let err = farm_crdt_change_from_event_with_author( 323 parts.kind, 324 &empty_author_tag, 325 &parts.content, 326 AUTHOR, 327 ) 328 .unwrap_err(); 329 assert!(matches!(err, EventParseError::InvalidTag("p"))); 330 331 let mut bad_document_tag = parts.tags.clone(); 332 replace_first_tag(&mut bad_document_tag, "d", tag("d", "bad")); 333 let err = 334 farm_crdt_change_from_event(parts.kind, &bad_document_tag, &parts.content).unwrap_err(); 335 assert!(matches!(err, EventParseError::InvalidTag("d"))); 336 337 for replacement in [ 338 tag("a", "30023:workspace_pubkey:AAAAAAAAAAAAAAAAAAAAAA"), 339 tag("a", "30078::AAAAAAAAAAAAAAAAAAAAAA"), 340 tag("a", "30078:workspace_pubkey:bad d"), 341 tag("a", "30078:workspace_pubkey:AAAAAAAAAAAAAAAAAAAAAA:extra"), 342 ] { 343 let mut tags = parts.tags.clone(); 344 replace_first_tag(&mut tags, "a", replacement); 345 let err = farm_crdt_change_from_event(parts.kind, &tags, &parts.content).unwrap_err(); 346 assert!(matches!(err, EventParseError::InvalidTag("a"))); 347 } 348 349 let mut wrong_marker = parts.tags.clone(); 350 remove_tags(&mut wrong_marker, "t"); 351 wrong_marker.push(tag("t", "radroots:farm:other")); 352 let err = 353 farm_crdt_change_from_event(parts.kind, &wrong_marker, &parts.content).unwrap_err(); 354 assert!(matches!(err, EventParseError::MissingTag("t"))); 355 356 let mut group_mismatch = parts.tags.clone(); 357 replace_first_tag(&mut group_mismatch, "h", tag("h", "other-group")); 358 let err = 359 farm_crdt_change_from_event(parts.kind, &group_mismatch, &parts.content).unwrap_err(); 360 assert!(matches!(err, EventParseError::InvalidTag("h"))); 361 362 let mut pubkey_mismatch = sample_change(); 363 pubkey_mismatch.workspace.pubkey = "other_workspace_pubkey".to_string(); 364 let content = serde_json::to_string(&pubkey_mismatch).expect("crdt content"); 365 let err = farm_crdt_change_from_event(parts.kind, &parts.tags, &content).unwrap_err(); 366 assert!(matches!(err, EventParseError::InvalidTag("a"))); 367 368 let mut d_tag_mismatch = sample_change(); 369 d_tag_mismatch.workspace.d_tag = DOCUMENT_ID.to_string(); 370 let content = serde_json::to_string(&d_tag_mismatch).expect("crdt content"); 371 let err = farm_crdt_change_from_event(parts.kind, &parts.tags, &content).unwrap_err(); 372 assert!(matches!(err, EventParseError::InvalidTag("a"))); 373 } 374 375 #[test] 376 fn farm_crdt_change_rejects_content_validation_edges() { 377 let parts = to_wire_parts(&sample_change()).expect("crdt wire parts"); 378 379 for (change, expected) in [ 380 { 381 let mut change = sample_change(); 382 change.farm_group_id.clear(); 383 (change, EventParseError::InvalidTag("h")) 384 }, 385 { 386 let mut change = sample_change(); 387 change.document_id = "bad".to_string(); 388 (change, EventParseError::InvalidTag("d")) 389 }, 390 { 391 let mut change = sample_change(); 392 change.workspace.pubkey.clear(); 393 (change, EventParseError::InvalidTag("a")) 394 }, 395 { 396 let mut change = sample_change(); 397 change.workspace.d_tag = "bad".to_string(); 398 (change, EventParseError::InvalidTag("a")) 399 }, 400 { 401 let mut change = sample_change(); 402 change.encoded_change = "abc/def".to_string(); 403 (change, EventParseError::InvalidJson("encoded_change")) 404 }, 405 { 406 let mut change = sample_change(); 407 change.change_hash.clear(); 408 (change, EventParseError::InvalidJson("change_hash")) 409 }, 410 { 411 let mut change = sample_change(); 412 change.business_time_ms = 0; 413 (change, EventParseError::InvalidJson("business_time_ms")) 414 }, 415 { 416 let mut change = sample_change(); 417 change.actor_id.clear(); 418 (change, EventParseError::InvalidJson("actor_id")) 419 }, 420 { 421 let mut change = sample_change(); 422 change.dependencies.push(String::new()); 423 (change, EventParseError::InvalidJson("dependencies")) 424 }, 425 { 426 let mut change = sample_change(); 427 change.crdt_backend_version = Some(" ".to_string()); 428 (change, EventParseError::InvalidJson("crdt_backend_version")) 429 }, 430 { 431 let mut change = sample_change(); 432 change.author_member_id = Some(" ".to_string()); 433 (change, EventParseError::InvalidJson("author_member_id")) 434 }, 435 { 436 let mut change = sample_change(); 437 change.app_version = Some(" ".to_string()); 438 (change, EventParseError::InvalidJson("app_version")) 439 }, 440 ] { 441 let content = serde_json::to_string(&change).expect("crdt content"); 442 let err = farm_crdt_change_from_event(parts.kind, &parts.tags, &content).unwrap_err(); 443 assert_same_parse_error(err, expected); 444 } 445 } 446 447 #[test] 448 fn farm_crdt_change_rejects_encoder_validation_edges() { 449 for (change, expected) in [ 450 { 451 let mut change = sample_change(); 452 change.schema = "radroots.farm.crdt.invalid".to_string(); 453 (change, EventEncodeError::InvalidField("schema")) 454 }, 455 { 456 let mut change = sample_change(); 457 change.farm_group_id.clear(); 458 ( 459 change, 460 EventEncodeError::EmptyRequiredField("farm_group_id"), 461 ) 462 }, 463 { 464 let mut change = sample_change(); 465 change.document_id = "bad".to_string(); 466 (change, EventEncodeError::InvalidField("document_id")) 467 }, 468 { 469 let mut change = sample_change(); 470 change.workspace.pubkey.clear(); 471 ( 472 change, 473 EventEncodeError::EmptyRequiredField("workspace.pubkey"), 474 ) 475 }, 476 { 477 let mut change = sample_change(); 478 change.workspace.d_tag = "bad".to_string(); 479 (change, EventEncodeError::InvalidField("workspace.d_tag")) 480 }, 481 { 482 let mut change = sample_change(); 483 change.actor_id.clear(); 484 (change, EventEncodeError::EmptyRequiredField("actor_id")) 485 }, 486 { 487 let mut change = sample_change(); 488 change.change_hash.clear(); 489 (change, EventEncodeError::EmptyRequiredField("change_hash")) 490 }, 491 { 492 let mut change = sample_change(); 493 change.dependencies.push(String::new()); 494 (change, EventEncodeError::EmptyRequiredField("dependencies")) 495 }, 496 { 497 let mut change = sample_change(); 498 change.encoded_change = "abc/def".to_string(); 499 (change, EventEncodeError::InvalidField("encoded_change")) 500 }, 501 { 502 let mut change = sample_change(); 503 change.business_time_ms = 0; 504 (change, EventEncodeError::InvalidField("business_time_ms")) 505 }, 506 { 507 let mut change = sample_change(); 508 change.crdt_backend_version = Some(" ".to_string()); 509 ( 510 change, 511 EventEncodeError::EmptyRequiredField("crdt_backend_version"), 512 ) 513 }, 514 { 515 let mut change = sample_change(); 516 change.author_member_id = Some(" ".to_string()); 517 ( 518 change, 519 EventEncodeError::EmptyRequiredField("author_member_id"), 520 ) 521 }, 522 { 523 let mut change = sample_change(); 524 change.app_version = Some(" ".to_string()); 525 (change, EventEncodeError::EmptyRequiredField("app_version")) 526 }, 527 ] { 528 let err = farm_crdt_change_build_tags(&change).unwrap_err(); 529 assert_same_encode_error(err, expected); 530 } 531 532 let author_err = 533 farm_crdt_change_build_tags_with_author(&sample_change(), Some(" ")).unwrap_err(); 534 assert_same_encode_error( 535 author_err, 536 EventEncodeError::EmptyRequiredField("author_pubkey"), 537 ); 538 539 let wrong_kind = 540 to_wire_parts_with_kind_and_author(&sample_change(), KIND_POST, Some(AUTHOR)) 541 .unwrap_err(); 542 assert_same_encode_error(wrong_kind, EventEncodeError::InvalidKind(KIND_POST)); 543 } 544 545 fn sample_change() -> RadrootsFarmCrdtChange { 546 sample_change_with( 547 DOCUMENT_ID, 548 RadrootsFarmCrdtDocumentKind::FarmTask, 549 RadrootsFarmSemanticKind::FarmTaskCreate, 550 ) 551 } 552 553 fn sample_change_with( 554 document_id: &str, 555 document_kind: RadrootsFarmCrdtDocumentKind, 556 semantic_kind: RadrootsFarmSemanticKind, 557 ) -> RadrootsFarmCrdtChange { 558 RadrootsFarmCrdtChange { 559 schema: RADROOTS_FARM_CRDT_CHANGE_SCHEMA.to_string(), 560 workspace: RadrootsFarmWorkspaceRef { 561 pubkey: "workspace_pubkey".to_string(), 562 d_tag: WORKSPACE_D_TAG.to_string(), 563 }, 564 farm_group_id: GROUP_ID.to_string(), 565 document_id: document_id.to_string(), 566 document_kind, 567 crdt_backend: RadrootsCrdtBackend::Automerge, 568 crdt_backend_version: Some("0.x".to_string()), 569 actor_id: "actor_abc".to_string(), 570 change_hash: "crdt_hash_abc".to_string(), 571 dependencies: Vec::new(), 572 encoded_change: "abc-DEF_012".to_string(), 573 semantic_kind, 574 business_time_ms: 1_780_000_000_000, 575 author_member_id: Some("member_abc".to_string()), 576 app_version: Some("0.1.0".to_string()), 577 } 578 } 579 580 fn document_id(index: usize) -> String { 581 format!("{index:02}AAAAAAAAAAAAAAAAAAAA") 582 } 583 584 fn tag(key: &str, value: &str) -> Vec<String> { 585 vec![key.to_string(), value.to_string()] 586 } 587 588 fn remove_tags(tags: &mut Vec<Vec<String>>, name: &str) { 589 tags.retain(|tag| tag.first().map(String::as_str) != Some(name)); 590 } 591 592 fn replace_first_tag(tags: &mut [Vec<String>], name: &str, replacement: Vec<String>) { 593 let tag = tags 594 .iter_mut() 595 .find(|tag| tag.first().map(String::as_str) == Some(name)) 596 .expect("tag"); 597 *tag = replacement; 598 } 599 600 fn assert_same_parse_error(actual: EventParseError, expected: EventParseError) { 601 match (actual, expected) { 602 (EventParseError::MissingTag(actual), EventParseError::MissingTag(expected)) 603 | (EventParseError::InvalidTag(actual), EventParseError::InvalidTag(expected)) 604 | (EventParseError::InvalidJson(actual), EventParseError::InvalidJson(expected)) => { 605 assert_eq!(actual, expected); 606 } 607 ( 608 EventParseError::InvalidKind { 609 expected: actual_expected, 610 got: actual_got, 611 }, 612 EventParseError::InvalidKind { expected, got }, 613 ) => { 614 assert_eq!(actual_expected, expected); 615 assert_eq!(actual_got, got); 616 } 617 ( 618 EventParseError::InvalidNumber(actual, _), 619 EventParseError::InvalidNumber(expected, _), 620 ) => { 621 assert_eq!(actual, expected); 622 } 623 (actual, expected) => { 624 panic!("unexpected parse error {actual:?}, expected {expected:?}") 625 } 626 } 627 } 628 629 fn assert_same_encode_error(actual: EventEncodeError, expected: EventEncodeError) { 630 match (actual, expected) { 631 ( 632 EventEncodeError::EmptyRequiredField(actual), 633 EventEncodeError::EmptyRequiredField(expected), 634 ) 635 | (EventEncodeError::InvalidField(actual), EventEncodeError::InvalidField(expected)) => { 636 assert_eq!(actual, expected); 637 } 638 (EventEncodeError::InvalidKind(actual), EventEncodeError::InvalidKind(expected)) => { 639 assert_eq!(actual, expected); 640 } 641 (EventEncodeError::Json, EventEncodeError::Json) => {} 642 (actual, expected) => { 643 panic!("unexpected encode error {actual:?}, expected {expected:?}") 644 } 645 } 646 } 647 }