#![allow(dead_code)]
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;
use grain_id::GrainId;
use sapphire_framework_sync::testing::ManualClock;
use sapphire_framework_sync::{Replica, ReplicaConfig, Report, ScanOutcome};
pub const APP: &str = "test-app";
pub struct Node {
pub replica: Replica,
pub root: PathBuf,
pub clock: Arc<ManualClock>,
_dir: tempfile::TempDir,
}
pub fn node(n: u64) -> Node {
node_with(n, |_| {})
}
pub fn node_with(n: u64, tweak: impl FnOnce(&mut ReplicaConfig)) -> Node {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("root");
std::fs::create_dir_all(root.join(format!(".{APP}"))).unwrap();
let mut config = ReplicaConfig::new(
APP,
&root,
GrainId::from_u64(n).unwrap(),
&dir.path().join("state"),
);
tweak(&mut config);
let clock = ManualClock::new(1_000_000 * n);
let replica = Replica::open(config, clock.clone()).unwrap();
Node {
replica,
root,
clock,
_dir: dir,
}
}
pub fn reopen(node: Node) -> Node {
let Node {
replica,
root,
clock,
_dir,
} = node;
let config = replica.config().clone();
drop(replica);
let replica = Replica::open(config, clock.clone()).unwrap();
Node {
replica,
root,
clock,
_dir,
}
}
pub fn reset_store(node: Node) -> Node {
let Node {
replica,
root,
clock,
_dir,
} = node;
let config = replica.config().clone();
drop(replica);
std::fs::remove_file(&config.store_path).unwrap();
let _ = std::fs::remove_dir_all(&config.staging_dir);
let replica = Replica::open(config, clock.clone()).unwrap();
Node {
replica,
root,
clock,
_dir,
}
}
pub fn write(node: &Node, rel: &str, body: &str) {
let path = node.root.join(rel);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, body).unwrap();
}
pub fn remove(node: &Node, rel: &str) {
let _ = std::fs::remove_file(node.root.join(rel));
}
pub fn read(node: &Node, rel: &str) -> Option<String> {
std::fs::read_to_string(node.root.join(rel)).ok()
}
pub fn scan(node: &mut Node) -> Report {
node.clock.advance(10);
match node.replica.scan().unwrap() {
ScanOutcome::Scanned(report) => report,
ScanOutcome::Paused(reason) => panic!("unexpectedly paused: {reason:?}"),
}
}
pub fn push(from: &Node, to: &mut Node) -> Report {
let from_vv = from.replica.vv().clone();
let updates = from.replica.delta_for(to.replica.vv()).unwrap();
let report = to.replica.apply(&updates, &from.replica).unwrap();
to.replica.commit_session(&from_vv).unwrap();
report
}
pub fn sync(a: &mut Node, b: &mut Node) {
push(a, b);
push(b, a);
}
pub fn tree(node: &Node) -> BTreeMap<String, String> {
let marker = node.root.join(format!(".{APP}"));
walkdir::WalkDir::new(&node.root)
.into_iter()
.map(Result::unwrap)
.filter(|e| e.file_type().is_file() && !e.path().starts_with(&marker))
.map(|e| {
let rel = e
.path()
.strip_prefix(&node.root)
.unwrap()
.to_string_lossy()
.replace('\\', "/");
(rel, std::fs::read_to_string(e.path()).unwrap())
})
.collect()
}