diff options
| author | tv <tv@krebsco.de> | 2026-10-31 19:03:15 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-31 19:03:15 +0100 |
| commit | e4f801db5d150fb1bf5540d9ba00f491a6129a5c (patch) | |
| tree | 38a8b5ffd8233df7d24f9bb5effac58cbd92187e /src/document | |
| parent | 4a6403d0242cf730a3f864c8d26e0ba15a98fcf0 (diff) | |
tests write through document::testing helpers
Diffstat (limited to 'src/document')
| -rw-r--r-- | src/document/changes.rs | 11 | ||||
| -rw-r--r-- | src/document/file.rs | 7 | ||||
| -rw-r--r-- | src/document/mod.rs | 2 | ||||
| -rw-r--r-- | src/document/store.rs | 70 | ||||
| -rw-r--r-- | src/document/sync.rs | 19 | ||||
| -rw-r--r-- | src/document/testing.rs | 29 |
6 files changed, 103 insertions, 35 deletions
diff --git a/src/document/changes.rs b/src/document/changes.rs index 655063b..33029fc 100644 --- a/src/document/changes.rs +++ b/src/document/changes.rs @@ -101,12 +101,13 @@ pub(crate) fn load_delta(doc: &mut Document, bytes: &[u8]) -> Result<()> { #[cfg(test)] mod tests { use super::*; + use crate::document::testing::change; use automerge::{ActorId, ROOT, transaction::Transactable}; fn random_write(rng: &mut crate::testrand::Rng, doc: &mut Document) { let key = format!("k{}", rng.below(6)); - doc.put(ROOT, key, rng.below(100) as i64).unwrap(); - doc.commit(); + let value = rng.below(100) as i64; + change(doc, |doc| doc.put(ROOT, key, value).unwrap()); } fn sorted(mut heads: Vec<ChangeHash>) -> Vec<ChangeHash> { @@ -158,8 +159,7 @@ mod tests { #[test] fn no_delta_without_its_heads() { let mut doc = Document::new(); - doc.put(ROOT, "k", 1_i64).unwrap(); - doc.commit(); + change(&mut doc, |doc| doc.put(ROOT, "k", 1_i64).unwrap()); assert!(delta_bytes(&mut doc, &[], &[ChangeHash([7; 32])]).is_none()); } @@ -167,8 +167,7 @@ mod tests { #[test] fn a_change_is_checked_against_its_hash() { let mut doc = Document::new(); - doc.put(ROOT, "k", 1_i64).unwrap(); - doc.commit(); + change(&mut doc, |doc| doc.put(ROOT, "k", 1_i64).unwrap()); let hash = doc.get_heads()[0]; let bytes = change_bytes(&mut doc, &hash).unwrap(); assert_eq!(change_from_bytes(bytes.clone(), hash).unwrap().hash(), hash); diff --git a/src/document/file.rs b/src/document/file.rs index e03c2e6..6439e27 100644 --- a/src/document/file.rs +++ b/src/document/file.rs @@ -322,19 +322,18 @@ impl DocumentFile { #[cfg(test)] mod tests { use super::*; + use crate::document::testing::change; use automerge::{ROOT, ReadDoc, transaction::Transactable}; #[test] fn whole_then_increments() { let mut doc = AutoCommit::new(); - doc.put(ROOT, "a", 1_i64).unwrap(); - doc.commit(); + change(&mut doc, |doc| doc.put(ROOT, "a", 1_i64).unwrap()); let actor = doc.get_actor().clone(); let heads = doc.get_heads(); let mut bytes = whole(std::slice::from_ref(&actor), &doc.save()).unwrap(); - doc.put(ROOT, "b", 2_i64).unwrap(); - doc.commit(); + change(&mut doc, |doc| doc.put(ROOT, "b", 2_i64).unwrap()); bytes.extend(record(&doc.save_after(&heads))); let (loaded, dropped) = parse(&bytes).unwrap(); diff --git a/src/document/mod.rs b/src/document/mod.rs index f11fef0..a348774 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -37,6 +37,8 @@ pub(crate) mod meta; pub(crate) mod replica; pub(crate) mod store; pub(crate) mod sync; +#[cfg(test)] +pub(crate) mod testing; #[cfg(test)] mod tests { diff --git a/src/document/store.rs b/src/document/store.rs index dff0cfb..c6c428c 100644 --- a/src/document/store.rs +++ b/src/document/store.rs @@ -1071,6 +1071,7 @@ fn nested<T>( #[cfg(test)] mod tests { use super::*; + use crate::document::testing::try_change; use serde_json::json; fn path(at: &str) -> Vec<String> { @@ -1080,7 +1081,10 @@ mod tests { #[test] fn paths_go_through_lists() { let mut doc = AutoCommit::new(); - write_path(&mut doc, &path("list"), &json!(["b", {"k": 1}, "d"])).unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("list"), &json!(["b", {"k": 1}, "d"])) + }) + .unwrap(); assert_eq!(read_path(&doc, &path("list/0")).unwrap(), Some(json!("b"))); assert_eq!(read_path(&doc, &path("list/1/k")).unwrap(), Some(json!(1))); // Not an index, or past the end: nothing. @@ -1088,18 +1092,27 @@ mod tests { assert_eq!(read_path(&doc, &path(missing)).unwrap(), None, "{missing}"); } // Replaced, and appended at the end; past it, nothing. - write_path(&mut doc, &path("list/0"), &json!("a")).unwrap(); - write_path(&mut doc, &path("list/3"), &json!("e")).unwrap(); - write_path(&mut doc, &path("list/1/k"), &json!(2)).unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("list/0"), &json!("a")) + }) + .unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("list/3"), &json!("e")) + }) + .unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("list/1/k"), &json!(2)) + }) + .unwrap(); assert!(matches!( - write_path(&mut doc, &path("list/9"), &json!(0)), + try_change(&mut doc, |doc| write_path(doc, &path("list/9"), &json!(0))), Err(StoreError::NotFound) )); assert_eq!( read_path(&doc, &path("list")).unwrap(), Some(json!(["a", {"k": 2}, "d", "e"])) ); - delete_path(&mut doc, &path("list/2"), false).unwrap(); + try_change(&mut doc, |doc| delete_path(doc, &path("list/2"), false)).unwrap(); assert_eq!( read_path(&doc, &path("list")).unwrap(), Some(json!(["a", {"k": 2}, "e"])) @@ -1113,8 +1126,14 @@ mod tests { #[test] fn negative_indexes_count_from_the_end() { let mut doc = AutoCommit::new(); - write_path(&mut doc, &path("list"), &json!(["a", "b"])).unwrap(); - write_path(&mut doc, &path("map"), &json!({"-1": "a key"})).unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("list"), &json!(["a", "b"])) + }) + .unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("map"), &json!({"-1": "a key"})) + }) + .unwrap(); assert_eq!( positive_path(&doc, &path("list/-1"), false).unwrap(), Some(path("list/1")) @@ -1135,28 +1154,47 @@ mod tests { #[test] fn inserts_into_lists() { let mut doc = AutoCommit::new(); - write_path(&mut doc, &path("list"), &json!(["b", "d"])).unwrap(); - insert_path(&mut doc, &path("list/0"), &json!("a")).unwrap(); - insert_path(&mut doc, &path("list/2"), &json!("c")).unwrap(); - insert_path(&mut doc, &path("list/4"), &json!({"e": 5})).unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("list"), &json!(["b", "d"])) + }) + .unwrap(); + try_change(&mut doc, |doc| { + insert_path(doc, &path("list/0"), &json!("a")) + }) + .unwrap(); + try_change(&mut doc, |doc| { + insert_path(doc, &path("list/2"), &json!("c")) + }) + .unwrap(); + try_change(&mut doc, |doc| { + insert_path(doc, &path("list/4"), &json!({"e": 5})) + }) + .unwrap(); assert_eq!( read_path(&doc, &path("list")).unwrap(), Some(json!(["a", "b", "c", "d", {"e": 5}])) ); assert!(matches!( - insert_path(&mut doc, &path("list/6"), &json!(1)), + try_change(&mut doc, |doc| insert_path(doc, &path("list/6"), &json!(1))), Err(StoreError::PastTheEnd { index: 6, length: 5 }) )); - write_path(&mut doc, &path("map"), &json!({"k": 1})).unwrap(); + try_change(&mut doc, |doc| { + write_path(doc, &path("map"), &json!({"k": 1})) + }) + .unwrap(); assert!(matches!( - insert_path(&mut doc, &path("map/0"), &json!(1)), + try_change(&mut doc, |doc| insert_path(doc, &path("map/0"), &json!(1))), Err(StoreError::NotAList) )); assert!(matches!( - insert_path(&mut doc, &path("nothing/0"), &json!(1)), + try_change(&mut doc, |doc| insert_path( + doc, + &path("nothing/0"), + &json!(1) + )), Err(StoreError::NotFound) )); } diff --git a/src/document/sync.rs b/src/document/sync.rs index dc286fa..625d805 100644 --- a/src/document/sync.rs +++ b/src/document/sync.rs @@ -384,11 +384,12 @@ mod tests { /// trip (`answer_for_everything_since_shared`). #[test] fn missing_predecessors_go_at_once() { + use crate::document::testing::change; use automerge::transaction::Transactable; let mut a = Document::new(); let mut b = Document::new(); let (mut at_a, mut at_b) = (sync::State::new(), sync::State::new()); - a.put(automerge::ROOT, "early", 1).unwrap(); + change(&mut a, |a| a.put(automerge::ROOT, "early", 1).unwrap()); // Until in step. for _ in 0..10 { let to_b = a.sync().generate_sync_message(&mut at_a); @@ -406,12 +407,12 @@ mod tests { assert_eq!(a.get_heads(), b.get_heads()); // Five changes, whose message is lost; then a sixth, which arrives alone. for i in 0..5 { - a.put(automerge::ROOT, format!("lost{i}"), i).unwrap(); - a.commit(); + change(&mut a, |a| { + a.put(automerge::ROOT, format!("lost{i}"), i).unwrap() + }); } let _lost = a.sync().generate_sync_message(&mut at_a).unwrap(); - a.put(automerge::ROOT, "last", 6).unwrap(); - a.commit(); + change(&mut a, |a| a.put(automerge::ROOT, "last", 6).unwrap()); at_a.in_flight = false; // As after our reply timeout. let last = a.sync().generate_sync_message(&mut at_a).unwrap(); assert_eq!(last.changes.len(), 1); @@ -467,6 +468,7 @@ mod tests { /// overdue answer, gets the same changes in the next message. #[test] fn withdrawing_for_a_delta_keeps_the_changes_for_later() { + use crate::document::testing::change; use automerge::{ROOT, transaction::Transactable}; // 16 KiB of hex, about 8 KiB of information: Automerge compresses what it sends @@ -476,8 +478,7 @@ mod tests { .map(|_| format!("{:016x}", rng.next_u64())) .collect(); let mut ours = Document::new(); - ours.put(ROOT, "big", big).unwrap(); - ours.commit(); + change(&mut ours, |ours| ours.put(ROOT, "big", big).unwrap()); let mut theirs = Document::new(); let (mut a, mut b) = (session(), session()); let now = Instant::now(); @@ -515,6 +516,7 @@ mod tests { /// has everything (`have_all_theirs`). #[test] fn after_a_delta_the_peer_learns_our_heads() { + use crate::document::testing::change; use automerge::{ROOT, transaction::Transactable}; let mut rng = crate::testrand::Rng::new(2); @@ -522,8 +524,7 @@ mod tests { .map(|_| format!("{:016x}", rng.next_u64())) .collect(); let mut ours = Document::new(); - ours.put(ROOT, "big", big).unwrap(); - ours.commit(); + change(&mut ours, |ours| ours.put(ROOT, "big", big).unwrap()); let mut theirs = Document::new(); let (mut a, mut b) = (session(), session()); let now = Instant::now(); diff --git a/src/document/testing.rs b/src/document/testing.rs new file mode 100644 index 0000000..b34d85f --- /dev/null +++ b/src/document/testing.rs @@ -0,0 +1,29 @@ +//! For tests: writing to documents the way the node does, one change at a time, so +//! that tests don't depend on how the engine groups writes into changes. + +use super::Document; + +/// The writes `f` makes to `doc`, as one change; what `f` returns. +pub(crate) fn change<T>(doc: &mut Document, f: impl FnOnce(&mut Document) -> T) -> T { + let out = f(doc); + doc.commit(); + out +} + +/// The writes `f` makes to `doc`, as one change if it succeeds, dropped if not; what +/// `f` returns. Committed at time 0: the same writes make the same change. +pub(crate) fn try_change<T, E>( + doc: &mut Document, + f: impl FnOnce(&mut Document) -> Result<T, E>, +) -> Result<T, E> { + let out = f(doc); + match &out { + Ok(_) => { + doc.commit_with(automerge::transaction::CommitOptions::default().with_time(0)); + } + Err(_) => { + doc.rollback(); + } + } + out +} |
