diff options
| -rw-r--r-- | src/remake/delta.rs | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/src/remake/delta.rs b/src/remake/delta.rs index 51cc409..59acc8c 100644 --- a/src/remake/delta.rs +++ b/src/remake/delta.rs @@ -279,6 +279,71 @@ mod tests { store::read_path(doc, &[]).unwrap().unwrap() } + /// A random store write as the API makes it (one change): a scalar or a small map + /// at a path up to three keys deep, or a delete. Writes the store refuses (e.g. into + /// a scalar) are rolled back. + fn random_write(rng: &mut crate::testrand::Rng, doc: &mut AutoCommit) { + const KEYS: [&str; 3] = ["a", "b", "c"]; + let depth = rng.between(1, 3); + let path: Vec<String> = (0..depth).map(|_| rng.pick(&KEYS).to_string()).collect(); + let result = if rng.chance(0.2) { + store::delete_path(doc, &path, false) + } else { + let value = match rng.below(4) { + 0 => json!(rng.below(100)), + 1 => json!(format!("s{}", rng.below(100))), + 2 => json!(rng.chance(0.5)), + _ => { + let mut map = serde_json::Map::new(); + map.insert(rng.pick(&KEYS).to_string(), json!(rng.below(100))); + serde_json::Value::Object(map) + } + }; + store::write_path(doc, &path, &value) + }; + match result { + Ok(()) => { + doc.commit_with(automerge::transaction::CommitOptions::default().with_time(0)); + } + Err(_) => { + doc.rollback(); + } + } + } + + /// Compaction's promise: a snapshot of the base plus everything replayed since gives + /// the old document's content, whatever several nodes wrote concurrently. + #[test] + fn replay_reproduces_the_document() { + crate::testrand::for_seeds(200, |rng| { + let actor = |i: u8| ActorId::from(&[i; 16][..]); + let mut old = AutoCommit::new().with_actor(actor(0)); + for _ in 0..rng.between(0, 8) { + random_write(rng, &mut old); + } + let base = old.get_heads(); + let mut new = AutoCommit::new().with_actor(actor(100)); + store::materialize_into(&old, &mut new, &|_| false).unwrap(); + new.commit(); + + let mut nodes: Vec<AutoCommit> = (1..=rng.between(1, 3)) + .map(|i| old.fork().with_actor(actor(i as u8))) + .collect(); + for _ in 0..rng.between(1, 20) { + let node = rng.below(nodes.len()); + random_write(rng, &mut nodes[node]); + } + for node in &mut nodes { + old.merge(node).unwrap(); + } + let after = old.get_heads(); + + let stats = replay(&mut old, &base, &after, &mut new, &Translator::default()); + assert_eq!(stats.failed, 0); + assert_eq!(view(&new), view(&old)); + }); + } + #[test] fn minimal_paths() { let mut doc = AutoCommit::new(); |
