| 1 | use yrs::updates::decoder::Decode; |
| 2 | use yrs::{Doc, GetString, Options, ReadTxn, StateVector, Text, Transact, Update}; |
| 3 | |
| 4 | #[test] |
| 5 | fn partially_known_delete_range_remains_deleted_when_tail_arrives() { |
| 6 | check_missing_tail(false, false); |
| 7 | } |
| 8 | |
| 9 | #[test] |
| 10 | fn partially_known_delete_range_survives_snapshot_before_tail_arrives() { |
| 11 | check_missing_tail(true, false); |
| 12 | } |
| 13 | |
| 14 | #[test] |
| 15 | fn deletion_received_before_any_source_update_is_preserved() { |
| 16 | check_missing_tail(false, true); |
| 17 | } |
| 18 | |
| 19 | #[test] |
| 20 | fn unknown_client_deletion_survives_snapshot_before_source_arrives() { |
| 21 | check_missing_tail(true, true); |
| 22 | } |
| 23 | |
| 24 | fn check_missing_tail(reload: bool, deletion_first: bool) { |
| 25 | let sender = Doc::with_options(Options::with_guid_and_client_id( |
| 26 | "delete-tail-review".into(), |
| 27 | 111, |
| 28 | )); |
| 29 | let text = sender.get_or_insert_text("text"); |
| 30 | text.insert(&mut sender.transact_mut(), 0, "abcdef"); |
| 31 | let first = sender |
| 32 | .transact() |
| 33 | .encode_state_as_update_v1(&StateVector::default()); |
| 34 | let before_tail = sender.transact().state_vector(); |
| 35 | text.insert(&mut sender.transact_mut(), 6, "ghij"); |
| 36 | let delayed = sender.transact().encode_state_as_update_v1(&before_tail); |
| 37 | let before_delete = sender.transact().state_vector(); |
| 38 | text.remove_range(&mut sender.transact_mut(), 2, 8); |
| 39 | let deletion = sender.transact().encode_state_as_update_v1(&before_delete); |
| 40 | let mut receiver = Doc::with_options(Options::with_guid_and_client_id( |
| 41 | "delete-tail-review".into(), |
| 42 | 222, |
| 43 | )); |
| 44 | let updates = if deletion_first { |
| 45 | [deletion, first] |
| 46 | } else { |
| 47 | [first, deletion] |
| 48 | }; |
| 49 | for (index, update) in updates.into_iter().enumerate() { |
| 50 | receiver |
| 51 | .transact_mut() |
| 52 | .apply_update(Update::decode_v1(&update).unwrap()) |
| 53 | .unwrap(); |
| 54 | if reload && index == if deletion_first { 0 } else { 1 } { |
| 55 | let snapshot = receiver |
| 56 | .transact() |
| 57 | .encode_state_as_update_v1(&StateVector::default()); |
| 58 | receiver = Doc::with_options(Options::with_guid_and_client_id( |
| 59 | "delete-tail-review".into(), |
| 60 | 333, |
| 61 | )); |
| 62 | receiver |
| 63 | .transact_mut() |
| 64 | .apply_update(Update::decode_v1(&snapshot).unwrap()) |
| 65 | .unwrap(); |
| 66 | } |
| 67 | } |
| 68 | receiver |
| 69 | .transact_mut() |
| 70 | .apply_update(Update::decode_v1(&delayed).unwrap()) |
| 71 | .unwrap(); |
| 72 | let received = receiver.get_or_insert_text("text"); |
| 73 | assert_eq!(text.get_string(&sender.transact()), "ab"); |
| 74 | assert_eq!( |
| 75 | received.get_string(&receiver.transact()), |
| 76 | "ab", |
| 77 | "A deletion covering an unseen suffix must survive until that suffix arrives" |
| 78 | ); |
| 79 | } |
| 80 | |