aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/document/sync.rs48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/document/sync.rs b/src/document/sync.rs
index 4ae41ba..7695b82 100644
--- a/src/document/sync.rs
+++ b/src/document/sync.rs
@@ -1553,6 +1553,54 @@ mod tests {
assert_eq!(ours.get_heads(), theirs.get_heads());
}
+ /// A push leaves the receiver where Automerge's own message would have: same
+ /// document, same answer, and the sender's next turn the same. If an Automerge
+ /// upgrade changes what a sync message's parts mean, this fails, not the swarm.
+ #[test]
+ fn pushes_do_what_automerges_messages_do() {
+ use crate::document::testing::change;
+ use automerge::{ROOT, transaction::Transactable};
+ let (now, bytes) = (Instant::now(), SyncBytes::default());
+ for concurrent in [false, true] {
+ let (mut ours, mut theirs) = (Document::new(), Document::new());
+ change(&mut ours, |doc| doc.put(ROOT, "early", 1).unwrap());
+ let (a, b) = in_step(&mut ours, &mut theirs);
+ if concurrent {
+ change(&mut theirs, |doc| doc.put(ROOT, "theirs", 3).unwrap());
+ }
+ change(&mut ours, |doc| doc.put(ROOT, "news", 2).unwrap());
+
+ // Each way: what the receiver holds, whether it answers, with what, and
+ // what the sender holds and says next once it has the answer.
+ let outcome = |pushing: bool| {
+ let (mut a, mut b) = (a.clone(), b.clone());
+ let (mut ours, mut theirs) = (ours.clone(), theirs.clone());
+ let message = if pushing {
+ let (heads, changes) = a.push(&ours, 1 << 20).expect("news to push");
+ Received::pushed(heads, changes).expect("decodes")
+ } else {
+ decode(&a.generate(&ours, now, bytes).expect("news")).unwrap()
+ };
+ a.assume_delivered(&ours.get_heads());
+ a.send_succeeded(a.id());
+ let before = theirs.get_heads();
+ b.receive(&mut theirs, message).unwrap();
+ b.came_from_peer(&theirs, &before);
+ let level = b.answered_as_delivered(&theirs, &theirs.get_heads());
+ let answer = b.generate(&theirs, now, bytes).map(|m| decode(&m).unwrap());
+ let answered = answer.as_ref().map(|m| (m.heads().to_vec(), m.sizes().0));
+ if let Some(answer) = answer {
+ a.receive(&mut ours, answer).unwrap();
+ }
+ let next = a
+ .generate(&ours, now, bytes)
+ .map(|m| decode(&m).unwrap().sizes().0);
+ (theirs.get_heads(), level, answered, ours.get_heads(), next)
+ };
+ assert_eq!(outcome(true), outcome(false), "concurrent: {concurrent}");
+ }
+ }
+
/// A push without changes, or without heads and changes that don't decode, is no
/// message: no heads would say the sender lost all it had.
#[test]