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