//! Undo snapshots and reversible namespace changes through a real world. use sausage::world::World; use sausage_vfs::{ Layer, Open, journal::{Tag, Undo}, }; use std::sync::Arc; fn tag(id: &str) -> Tag { Tag { id: id.into(), pid: 1, program: "/bin/test".into(), conversations: vec!["conversation".into()], } } async fn write(root: &Arc, path: &str, bytes: &[u8]) { root.open( path, Open { write: true, create: true, truncate: true, ..Default::default() }, ) .await .unwrap() .write(0, bytes.to_vec()) .await .unwrap(); } async fn read(root: &Arc, path: &str) -> Vec { root.open(path, Open::default()) .await .unwrap() .read(0, 100) .await .unwrap() } #[tokio::test] async fn conflicts_force_and_undo_of_undo() { let temp = tempfile::tempdir().unwrap(); let world = World::open(temp.path()).unwrap(); write(&world.root, "/a", b"old").await; let journal = world.root.journal().unwrap(); let a = journal.layer(tag("A")); let b = journal.layer(tag("B")); write(&a, "/a", b"A").await; write(&a, "/only-a", b"A").await; write(&b, "/a", b"B").await; write(&b, "/only-b", b"B").await; let request = Undo { tag: Some("A".into()), ..Default::default() }; let blocked = journal.undo(request.clone(), tag("blocked")).await.unwrap(); assert_eq!(blocked.conflicts, ["/a"]); assert!(blocked.tag.is_none()); assert_eq!(read(&world.root, "/a").await, b"B"); let result = journal .undo( Undo { force: true, ..request }, tag("restore"), ) .await .unwrap(); assert_eq!(read(&world.root, "/a").await, b"old"); assert!(world.root.stat("/only-a", false).await.is_err()); assert_eq!(read(&world.root, "/only-b").await, b"B"); journal .undo( Undo { tag: result.tag, ..Default::default() }, tag("redo"), ) .await .unwrap(); assert_eq!(read(&world.root, "/a").await, b"B"); assert_eq!(read(&world.root, "/only-a").await, b"A"); } #[tokio::test] async fn remove_rename_folder_and_inclusive_since() { let temp = tempfile::tempdir().unwrap(); let world = World::open(temp.path()).unwrap(); write(&world.root, "/old", b"old").await; write(&world.root, "/removed", b"removed").await; let journal = world.root.journal().unwrap(); let a = journal.layer(tag("A")); a.mkdir("/new").await.unwrap(); a.rename("/old", "/new/moved").await.unwrap(); a.remove_file("/removed").await.unwrap(); let entry = journal.list(None).await.unwrap()[0].entry; write(&journal.layer(tag("B")), "/new/child", b"child").await; journal .undo( Undo { since: Some(entry), ..Default::default() }, tag("restore"), ) .await .unwrap(); assert_eq!(read(&world.root, "/old").await, b"old"); assert_eq!(read(&world.root, "/removed").await, b"removed"); assert!(world.root.stat("/new", false).await.is_err()); }