diff --git a/radicle-cli/examples/rad-clone-partial-fail.md b/radicle-cli/examples/rad-clone-partial-fail.md new file mode 100644 index 00000000..c0270700 --- /dev/null +++ b/radicle-cli/examples/rad-clone-partial-fail.md @@ -0,0 +1,32 @@ +Eve knows about three seeds. + +``` +$ rad node routing +╭─────────────────────────────────────────────────────╮ +│ RID NID │ +├─────────────────────────────────────────────────────┤ +│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6MknSL…StBU8Vi │ +│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6MksFq…bS9wzpT │ +│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6Mkt67…v4N1tRk │ +╰─────────────────────────────────────────────────────╯ +``` +When she tries to clone, one of those will fail to fetch. But the clone command +still returns successfully. + +``` +$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --timeout 3 +✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all' +✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. error: failed to perform fetch handshake +✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi.. +✗ Connecting to z6MksFq…bS9wzpT@[..].. error: connection reset +✓ Creating checkout in ./heartwood.. +✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added +✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi +✓ Repository successfully cloned under [..]/heartwood/ +╭────────────────────────────────────╮ +│ heartwood │ +│ Radicle Heartwood Protocol & Stack │ +│ 0 issues · 0 patches │ +╰────────────────────────────────────╯ +Run `cd ./heartwood` to go to the repository directory. +``` diff --git a/radicle-cli/examples/rad-init-private-clone.md b/radicle-cli/examples/rad-init-private-clone.md index ba773a3d..599bfc23 100644 --- a/radicle-cli/examples/rad-init-private-clone.md +++ b/radicle-cli/examples/rad-init-private-clone.md @@ -22,7 +22,7 @@ $ rad sync --announce --timeout 3 ``` Bob can now fetch the private repo without specifying a seed, because he knows -that alice has the repo after she announced her refs: +that Alice has the repo after she announced her refs: ``` ~bob $ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch diff --git a/radicle-cli/src/commands/sync.rs b/radicle-cli/src/commands/sync.rs index 4edddd2a..ad1eaa30 100644 --- a/radicle-cli/src/commands/sync.rs +++ b/radicle-cli/src/commands/sync.rs @@ -540,8 +540,8 @@ fn connect( spinner.finish(); return true; } - Ok(node::ConnectResult::Disconnected { .. }) => { - spinner.failed(); + Ok(node::ConnectResult::Disconnected { reason }) => { + spinner.error(reason); continue; } Err(e) => { diff --git a/radicle-cli/tests/commands.rs b/radicle-cli/tests/commands.rs index 6bc41540..fb477cf7 100644 --- a/radicle-cli/tests/commands.rs +++ b/radicle-cli/tests/commands.rs @@ -9,7 +9,7 @@ use radicle::node::config::seeds::{RADICLE_COMMUNITY_NODE, RADICLE_TEAM_NODE}; use radicle::node::routing::Store as _; use radicle::node::Handle as _; use radicle::node::{Address, Alias, DEFAULT_TIMEOUT}; -use radicle::prelude::RepoId; +use radicle::prelude::{NodeId, RepoId}; use radicle::profile; use radicle::profile::Home; use radicle::storage::{ReadStorage, RefUpdate, RemoteRepository}; @@ -1190,6 +1190,63 @@ fn rad_clone_all() { eve.has_remote_of(&acme, &bob.id); } +#[test] +fn rad_clone_partial_fail() { + let mut environment = Environment::new(); + let mut alice = environment.node(Config::test(Alias::new("alice"))); + let bob = environment.node(Config::test(Alias::new("bob"))); + let mut eve = environment.node(Config::test(Alias::new("eve"))); + let working = environment.tmp().join("working"); + let carol = NodeId::from_str("z6MksFqXN3Yhqk8pTJdUGLwBTkRfQvwZXPqR2qMEhbS9wzpT").unwrap(); + + // Setup a test project. + let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + + // Make Even think she knows about a seed called "carol" that has the repo. + eve.db + .addresses_mut() + .insert( + &carol, + node::Features::SEED, + Alias::new("carol"), + 0, + localtime::LocalTime::now().as_secs(), + [node::KnownAddress::new( + // Eve will fail to connect to this address. + node::Address::from(net::SocketAddr::from(([0, 0, 0, 0], 19873))), + node::address::Source::Imported, + )], + ) + .unwrap(); + eve.db + .routing_mut() + .insert([&acme], carol, localtime::LocalTime::now().as_secs()) + .unwrap(); + eve.config.peers = node::config::PeerConfig::Static; + + let mut eve = eve.spawn(); + + alice.handle.seed(acme, Scope::All).unwrap(); + bob.handle.seed(acme, Scope::All).unwrap(); + + bob.connect(&alice).converge([&alice]); + eve.connect(&alice); + eve.connect(&bob); + eve.routes_to(&[(acme, carol), (acme, bob.id), (acme, alice.id)]); + bob.handle.unseed(acme).unwrap(); // Cause the fetch with bob to fail. + + test( + "examples/rad-clone-partial-fail.md", + working.join("eve"), + Some(&eve.home), + [], + ) + .unwrap(); +} + #[test] fn rad_clone_connect() { let mut environment = Environment::new();