use super::{answer, Machine};
use crate::peer::store::MachineStore;
use wsb::peer::converge::{Baseline, Entry, EntryKind};
use wsb::peer::identity::{Fingerprint, PairedPeer};
use crate::peer::coordinator::LocalWorkspace;
use wsb::peer::model::{PeerId, RepositoryId, WorkspaceId};
use wsb::peer::protocol::{baseline_digest, RefusalCode, Request, Response, WorkspaceSummary};
use std::sync::Arc;
struct Held {
store: Arc<MachineStore>,
workspace: Arc<LocalWorkspace>,
staging: std::path::PathBuf,
}
impl Machine for Held {
fn workspaces(&self) -> Vec<WorkspaceSummary> {
Vec::new()
}
fn workspace(&self, id: &WorkspaceId) -> Result<Arc<LocalWorkspace>, String> {
(*id == self.workspace.id)
.then(|| Arc::clone(&self.workspace))
.ok_or_else(|| "this machine does not have that worktree open".to_string())
}
fn store(&self) -> Arc<MachineStore> {
Arc::clone(&self.store)
}
fn staging_root(&self) -> std::path::PathBuf {
self.staging.clone()
}
fn plugins(&self) -> Arc<crate::plugin::PluginRegistry> {
Arc::new(crate::plugin::PluginRegistry::new(self.staging.clone()))
}
}
fn entry(bytes: &[u8]) -> Entry {
Entry {
kind: EntryKind::File,
hash: wsb::peer::converge::hash_bytes(bytes),
size: bytes.len() as u64,
executable: false,
}
}
fn peer_id() -> PeerId {
PeerId::parse("aaaa1111bbbb2222").expect("a peer id")
}
fn workspace_id() -> WorkspaceId {
WorkspaceId::parse("cccc3333@main").expect("a workspace id")
}
fn held(baseline: &Baseline) -> (tempfile::TempDir, Held) {
let dir = tempfile::tempdir().expect("a directory");
let store = MachineStore::open(dir.path()).expect("a machine store");
store
.add_peer(&PairedPeer {
id: peer_id(),
name: "laptop".to_string(),
public_key: "key".to_string(),
fingerprint: Fingerprint::from_stored("ff".to_string()),
address: "127.0.0.1:7420".to_string(),
paired_at: 1,
})
.expect("the peer pairs");
store
.put_baseline(&peer_id(), &workspace_id(), &RepositoryId::root(), baseline)
.expect("the baseline is stored");
let workspace = Arc::new(LocalWorkspace {
id: workspace_id(),
repositories: Vec::new(),
policy: wsb::peer::scan::Policy::default(),
});
let machine = Held {
store: Arc::new(store),
workspace,
staging: dir.path().join("staging"),
};
(dir, machine)
}
fn confirm(digest: &str) -> Request {
Request::Confirm {
workspace: workspace_id(),
repository: RepositoryId::root(),
baseline_digest: digest.to_string(),
}
}
#[test]
fn test11587_a_matching_baseline_is_confirmed() {
let mut baseline = Baseline::new();
baseline.insert("carried.txt".to_string(), entry(b"carried\n"));
let (_dir, machine) = held(&baseline);
let response = answer(&machine, &peer_id(), confirm(&baseline_digest(&baseline)));
assert!(
matches!(response, Response::Acknowledged),
"a machine would not confirm the baseline it holds: {response:?}"
);
}
#[test]
fn test11588_a_drifted_baseline_is_refused_so_the_asker_reseeds() {
let mut baseline = Baseline::new();
baseline.insert("carried.txt".to_string(), entry(b"carried\n"));
let (_dir, machine) = held(&baseline);
let mut asker = baseline.clone();
asker.insert("after-commit.txt".to_string(), entry(b"carried again\n"));
let response = answer(&machine, &peer_id(), confirm(&baseline_digest(&asker)));
match response {
Response::Refused { code, .. } => assert_eq!(
code,
RefusalCode::BaselineMismatch,
"refused with a code that does not make the asker reseed"
),
other => panic!("a drifted baseline was confirmed: {other:?}"),
}
}
#[test]
fn test11589_an_unknown_workspace_is_not_confirmed() {
let (_dir, machine) = held(&Baseline::new());
let other = WorkspaceId::parse("dddd4444@main").expect("another workspace id");
let response = answer(
&machine,
&peer_id(),
Request::Confirm {
workspace: other,
repository: RepositoryId::root(),
baseline_digest: baseline_digest(&Baseline::new()),
},
);
match response {
Response::Refused { code, .. } => assert_eq!(code, RefusalCode::UnknownWorkspace),
other => panic!("a workspace this machine does not have was confirmed: {other:?}"),
}
}
#[test]
fn test20043_files_are_given_only_while_continuity_is_enabled() {
let (_dir, machine) = held(&Baseline::new());
let fetch = || Request::Fetch {
workspace: workspace_id(),
repository: RepositoryId::root(),
epoch: wsb::peer::compatibility::Epoch {
repository: RepositoryId::root(),
root_commit: "0".repeat(40),
branch: Some("main".to_string()),
head: Some("0".repeat(40)),
git_state: wsb::peer::compatibility::GitState::Settled,
},
paths: vec!["notes.md".to_string()],
};
let refusal = |response: Response| match response {
Response::Refused { code, .. } => code,
other => panic!("a fetch was answered: {other:?}"),
};
for desired in [wsb::peer::model::Desired::Paused, wsb::peer::model::Desired::Disabled] {
machine.store.set_desired(&peer_id(), &workspace_id(), desired, 2).expect("set");
assert_eq!(refusal(answer(&machine, &peer_id(), fetch())), RefusalCode::NotEnabled, "{desired:?}");
}
machine
.store
.set_desired(&peer_id(), &workspace_id(), wsb::peer::model::Desired::Enabled, 3)
.expect("set");
assert_eq!(refusal(answer(&machine, &peer_id(), fetch())), RefusalCode::UnknownRepository);
}
fn repository(baseline: &Baseline) -> (tempfile::TempDir, Held, wsb::peer::compatibility::Epoch) {
let (dir, mut machine) = held(baseline);
let workdir = dir.path().join("workspace");
std::fs::create_dir_all(&workdir).expect("a working tree");
let git = |args: &[&str]| {
let status = std::process::Command::new("git")
.args(["-c", "user.name=t", "-c", "user.email=t@t", "-c", "init.defaultBranch=main"])
.args(args)
.current_dir(&workdir)
.status()
.expect("git runs");
assert!(status.success(), "git {args:?}");
};
git(&["init", "-q"]);
std::fs::write(workdir.join("kept.txt"), "base\n").expect("write");
git(&["add", "kept.txt"]);
git(&["commit", "-q", "-m", "base"]);
let git_dir = workdir.join(".git");
let epoch = wsb::peer::compatibility::RepositoryFacts::read(RepositoryId::root(), &workdir, &git_dir)
.expect("the repository reads")
.epoch();
machine.workspace = Arc::new(LocalWorkspace {
id: workspace_id(),
repositories: vec![crate::peer::coordinator::LocalRepository {
id: RepositoryId::root(),
workdir,
git_dir,
}],
policy: wsb::peer::scan::Policy::default(),
});
machine
.store
.set_desired(&peer_id(), &workspace_id(), wsb::peer::model::Desired::Enabled, 2)
.expect("continuity is enabled");
(dir, machine, epoch)
}
fn write_of(path: &str, bytes: &[u8]) -> wsb::peer::protocol::Write {
wsb::peer::protocol::Write {
path: path.to_string(),
entry: entry(bytes),
content: wsb::peer::identity::base64_encode(bytes),
}
}
fn applied(response: Response) -> Vec<String> {
match response {
Response::Applied { applied } => applied,
other => panic!("the transfer was not applied: {other:?}"),
}
}
#[test]
fn test20047_a_file_edited_after_the_plan_read_it_is_not_replaced() {
let mut baseline = Baseline::new();
baseline.insert("kept.txt".to_string(), entry(b"base\n"));
let (_dir, machine, epoch) = repository(&baseline);
let workdir = machine.workspace.repositories[0].workdir.clone();
std::fs::write(workdir.join("kept.txt"), "laptop\n").expect("the edit after the report");
let response = answer(
&machine,
&peer_id(),
Request::Apply {
workspace: workspace_id(),
repository: RepositoryId::root(),
epoch,
writes: vec![write_of("kept.txt", b"desktop\n"), write_of("fresh.txt", b"new\n")],
deletions: Vec::new(),
found: [("kept.txt".to_string(), entry(b"base\n"))].into_iter().collect(),
baseline_digest: baseline_digest(&baseline),
},
);
assert_eq!(applied(response), vec!["fresh.txt".to_string()], "only the untouched path is reported");
assert_eq!(
std::fs::read_to_string(workdir.join("kept.txt")).expect("read"),
"laptop\n",
"an edit made after the report was replaced by the peer's version"
);
assert_eq!(std::fs::read_to_string(workdir.join("fresh.txt")).expect("read"), "new\n");
}
#[test]
fn test20048_a_file_edited_after_the_plan_read_it_is_not_deleted() {
let mut baseline = Baseline::new();
baseline.insert("kept.txt".to_string(), entry(b"base\n"));
baseline.insert("gone.txt".to_string(), entry(b"agreed\n"));
let (_dir, machine, epoch) = repository(&baseline);
let workdir = machine.workspace.repositories[0].workdir.clone();
std::fs::write(workdir.join("kept.txt"), "edited\n").expect("the edit after the report");
std::fs::write(workdir.join("gone.txt"), "agreed\n").expect("an untouched file");
let response = answer(
&machine,
&peer_id(),
Request::Apply {
workspace: workspace_id(),
repository: RepositoryId::root(),
epoch,
writes: Vec::new(),
deletions: vec!["kept.txt".to_string(), "gone.txt".to_string()],
found: [
("kept.txt".to_string(), entry(b"base\n")),
("gone.txt".to_string(), entry(b"agreed\n")),
]
.into_iter()
.collect(),
baseline_digest: baseline_digest(&baseline),
},
);
assert_eq!(applied(response), vec!["gone.txt".to_string()]);
assert_eq!(
std::fs::read_to_string(workdir.join("kept.txt")).expect("an edited file was deleted"),
"edited\n"
);
assert!(!workdir.join("gone.txt").exists(), "the agreed file was not deleted");
}