113 lines
3.3 KiB
Rust
113 lines
3.3 KiB
Rust
//! 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<dyn Layer>, 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<dyn Layer>, path: &str) -> Vec<u8> {
|
|
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());
|
|
}
|