summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/remake/delta.rs65
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();