From 1f3dc6aea0f020c6103c11676a0a7b3fa51386cd Mon Sep 17 00:00:00 2001 From: Adrian Duke Date: Tue, 3 Mar 2026 15:42:45 +0000 Subject: [PATCH] cli/tests: Refactor clone command tests --- crates/radicle-cli/tests/commands.rs | 287 +------------------- crates/radicle-cli/tests/commands/clone.rs | 297 +++++++++++++++++++++ 2 files changed, 299 insertions(+), 285 deletions(-) create mode 100644 crates/radicle-cli/tests/commands/clone.rs diff --git a/crates/radicle-cli/tests/commands.rs b/crates/radicle-cli/tests/commands.rs index 2f0155be..12540837 100644 --- a/crates/radicle-cli/tests/commands.rs +++ b/crates/radicle-cli/tests/commands.rs @@ -11,10 +11,9 @@ use radicle::node::config::seeds::RADICLE_NODE_BOOTSTRAP_IRIS; use radicle::node::config::DefaultSeedingPolicy; use radicle::node::events::Event; use radicle::node::policy::Scope; -use radicle::node::routing::Store as _; use radicle::node::UserAgent; use radicle::node::{Address, Alias, Config, Handle as _, DEFAULT_TIMEOUT}; -use radicle::prelude::{NodeId, RepoId}; +use radicle::prelude::RepoId; use radicle::profile; use radicle::profile::Home; use radicle::storage::{ReadStorage, RefUpdate, RemoteRepository}; @@ -32,6 +31,7 @@ use util::formula::formula; mod commands { mod checkout; + mod clone; } /// Run a CLI test file. @@ -1247,250 +1247,6 @@ fn rad_block() { test("examples/rad-block.md", working, Some(&alice.home), []).unwrap(); } -#[test] -fn rad_clone() { - let mut environment = Environment::new(); - let mut alice = environment.node("alice"); - let bob = environment.node("bob"); - let working = environment.tempdir().join("working"); - - // Setup a test project. - let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); - - let mut alice = alice.spawn(); - let mut bob = bob.spawn(); - // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. - alice.handle.seed(acme, Scope::Followed).unwrap(); - - bob.connect(&alice).converge([&alice]); - - test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap(); -} - -#[test] -fn rad_clone_bare() { - let mut environment = Environment::new(); - let mut alice = environment.node("alice"); - let bob = environment.node("bob"); - let working = environment.tempdir().join("working"); - - // Setup a test project. - let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); - - let mut alice = alice.spawn(); - let mut bob = bob.spawn(); - // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. - alice.handle.seed(acme, Scope::Followed).unwrap(); - - bob.connect(&alice).converge([&alice]); - - test("examples/rad-clone-bare.md", working, Some(&bob.home), []).unwrap(); -} - -#[test] -fn rad_clone_directory() { - let mut environment = Environment::new(); - let mut alice = environment.node("alice"); - let bob = environment.node("bob"); - let working = environment.tempdir().join("working"); - - // Setup a test project. - let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); - - let mut alice = alice.spawn(); - let mut bob = bob.spawn(); - // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. - alice.handle.seed(acme, Scope::Followed).unwrap(); - - bob.connect(&alice).converge([&alice]); - - test( - "examples/rad-clone-directory.md", - working, - Some(&bob.home), - [], - ) - .unwrap(); -} - -#[test] -fn rad_clone_all() { - let mut environment = Environment::new(); - let mut alice = environment.node("alice"); - let bob = environment.node("bob"); - let eve = environment.node("eve"); - - // Setup a test project. - let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); - - let mut alice = alice.spawn(); - let mut bob = bob.spawn(); - let mut eve = eve.spawn(); - - alice.handle.seed(acme, Scope::All).unwrap(); - bob.connect(&alice).converge([&alice]); - eve.connect(&alice).converge([&alice]); - - // Fork and sync repo. - bob.fork(acme, bob.home.path()).unwrap(); - bob.announce(acme, 2, bob.home.path()).unwrap(); - bob.has_remote_of(&acme, &alice.id); - alice.has_remote_of(&acme, &bob.id); - - test( - "examples/rad-clone-all.md", - environment.work(&eve), - Some(&eve.home), - [], - ) - .unwrap(); - eve.has_remote_of(&acme, &bob.id); -} - -#[test] -fn rad_clone_partial_fail() { - let mut environment = Environment::new(); - let mut alice = environment.node("alice"); - let bob = environment.node("bob"); - let mut eve = environment.node("eve"); - 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, - PROTOCOL_VERSION, - node::Features::SEED, - &Alias::new("carol"), - 0, - &UserAgent::default(), - LocalTime::now().into(), - [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() - .add_inventory([&acme], carol, LocalTime::now().into()) - .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.storage.repository(acme).unwrap().remove().unwrap(); // Cause the fetch from Bob to fail. - bob.storage.temporary_repository(acme).ok(); // Prevent repo from being re-fetched. - - test( - "examples/rad-clone-partial-fail.md", - environment.work(&eve), - Some(&eve.home), - [], - ) - .unwrap(); -} - -#[test] -fn rad_clone_connect() { - let mut environment = Environment::new(); - let working = environment.tempdir().join("working"); - let alice = environment.node("alice"); - let bob = environment.node("bob"); - let mut eve = environment.node("eve"); - let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap(); - let ua = UserAgent::default(); - let now = LocalTime::now().into(); - - fixtures::repository(working.join("acme")); - - test( - "examples/rad-init.md", - working.join("acme"), - Some(&alice.home), - [], - ) - .unwrap(); - - let mut alice = alice.spawn(); - let mut bob = bob.spawn(); - - // Let Eve know about Alice and Bob having the repo. - eve.db - .addresses_mut() - .insert( - &alice.id, - PROTOCOL_VERSION, - node::Features::SEED, - &Alias::new("alice"), - 0, - &ua, - now, - [node::KnownAddress::new( - node::Address::from(alice.addr), - node::address::Source::Imported, - )], - ) - .unwrap(); - eve.db - .addresses_mut() - .insert( - &bob.id, - PROTOCOL_VERSION, - node::Features::SEED, - &Alias::new("bob"), - 0, - &ua, - now, - [node::KnownAddress::new( - node::Address::from(bob.addr), - node::address::Source::Imported, - )], - ) - .unwrap(); - eve.db - .routing_mut() - .add_inventory([&acme], alice.id, now) - .unwrap(); - eve.db - .routing_mut() - .add_inventory([&acme], bob.id, now) - .unwrap(); - eve.config.peers = node::config::PeerConfig::Static; - - let eve = eve.spawn(); - - alice.handle.seed(acme, Scope::Followed).unwrap(); - bob.handle.seed(acme, Scope::Followed).unwrap(); - alice.connect(&bob); - bob.routes_to(&[(acme, alice.id)]); - eve.routes_to(&[(acme, alice.id), (acme, bob.id)]); - alice.routes_to(&[(acme, alice.id), (acme, bob.id)]); - - test( - "examples/rad-clone-connect.md", - working.join("acme"), - Some(&eve.home), - [], - ) - .unwrap(); -} - #[test] fn rad_sync_without_node() { let mut environment = Environment::new(); @@ -1534,23 +1290,6 @@ fn rad_self() { test("examples/rad-self.md", working, Some(&alice.home), []).unwrap(); } -#[test] -fn rad_clone_unknown() { - let mut environment = Environment::new(); - let alice = environment.node("alice"); - let working = environment.tempdir().join("working"); - - let alice = alice.spawn(); - - test( - "examples/rad-clone-unknown.md", - working, - Some(&alice.home), - [], - ) - .unwrap(); -} - #[test] fn rad_init_sync_not_connected() { let mut environment = Environment::new(); @@ -1724,28 +1463,6 @@ fn rad_diff() { test("examples/rad-diff.md", tmp, None, []).unwrap(); } -#[test] -// User tries to clone; no seeds are available, but user has the repo locally. -fn test_clone_without_seeds() { - let mut environment = Environment::new(); - let mut alice = environment.node("alice"); - let working = environment.tempdir().join("working"); - let rid = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); - let mut alice = alice.spawn(); - let seeds = alice.handle.seeds_for(rid, [alice.id]).unwrap(); - let connected = seeds.connected().collect::>(); - - assert!(connected.is_empty()); - - alice - .rad("clone", &[rid.to_string().as_str()], working.as_path()) - .unwrap(); - - alice - .rad("inspect", &[], working.join("heartwood").as_path()) - .unwrap(); -} - #[test] fn test_cob_replication() { let mut environment = Environment::new(); diff --git a/crates/radicle-cli/tests/commands/clone.rs b/crates/radicle-cli/tests/commands/clone.rs new file mode 100644 index 00000000..e6b69695 --- /dev/null +++ b/crates/radicle-cli/tests/commands/clone.rs @@ -0,0 +1,297 @@ +use crate::test; +use crate::util::environment::Environment; +use radicle::node; +use radicle::node::address::Store as _; +use radicle::node::policy::Scope; +use radicle::node::routing::Store as _; +use radicle::node::UserAgent; +use radicle::node::{Alias, Handle as _}; +use radicle::prelude::{NodeId, RepoId}; +use radicle::storage::ReadStorage as _; +use radicle_localtime::LocalTime; +use radicle_node::PROTOCOL_VERSION; +use std::net; +use std::str::FromStr; + +#[test] +fn rad_clone() { + let mut environment = Environment::new(); + let mut alice = environment.node("alice"); + let bob = environment.node("bob"); + let working = environment.tempdir().join("working"); + + // Setup a test project. + let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. + alice.handle.seed(acme, Scope::Followed).unwrap(); + + bob.connect(&alice).converge([&alice]); + + test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap(); +} + +#[test] +fn rad_clone_bare() { + let mut environment = Environment::new(); + let mut alice = environment.node("alice"); + let bob = environment.node("bob"); + let working = environment.tempdir().join("working"); + + // Setup a test project. + let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. + alice.handle.seed(acme, Scope::Followed).unwrap(); + + bob.connect(&alice).converge([&alice]); + + test("examples/rad-clone-bare.md", working, Some(&bob.home), []).unwrap(); +} + +#[test] +fn rad_clone_directory() { + let mut environment = Environment::new(); + let mut alice = environment.node("alice"); + let bob = environment.node("bob"); + let working = environment.tempdir().join("working"); + + // Setup a test project. + let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. + alice.handle.seed(acme, Scope::Followed).unwrap(); + + bob.connect(&alice).converge([&alice]); + + test( + "examples/rad-clone-directory.md", + working, + Some(&bob.home), + [], + ) + .unwrap(); +} + +#[test] +fn rad_clone_all() { + let mut environment = Environment::new(); + let mut alice = environment.node("alice"); + let bob = environment.node("bob"); + let eve = environment.node("eve"); + + // Setup a test project. + let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + let mut eve = eve.spawn(); + + alice.handle.seed(acme, Scope::All).unwrap(); + bob.connect(&alice).converge([&alice]); + eve.connect(&alice).converge([&alice]); + + // Fork and sync repo. + bob.fork(acme, bob.home.path()).unwrap(); + bob.announce(acme, 2, bob.home.path()).unwrap(); + bob.has_remote_of(&acme, &alice.id); + alice.has_remote_of(&acme, &bob.id); + + test( + "examples/rad-clone-all.md", + environment.work(&eve), + Some(&eve.home), + [], + ) + .unwrap(); + eve.has_remote_of(&acme, &bob.id); +} + +#[test] +fn rad_clone_partial_fail() { + let mut environment = Environment::new(); + let mut alice = environment.node("alice"); + let bob = environment.node("bob"); + let mut eve = environment.node("eve"); + 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, + PROTOCOL_VERSION, + node::Features::SEED, + &Alias::new("carol"), + 0, + &UserAgent::default(), + LocalTime::now().into(), + [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() + .add_inventory([&acme], carol, LocalTime::now().into()) + .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.storage.repository(acme).unwrap().remove().unwrap(); // Cause the fetch from Bob to fail. + bob.storage.temporary_repository(acme).ok(); // Prevent repo from being re-fetched. + + test( + "examples/rad-clone-partial-fail.md", + environment.work(&eve), + Some(&eve.home), + [], + ) + .unwrap(); +} + +#[test] +fn rad_clone_connect() { + let mut environment = Environment::new(); + let working = environment.tempdir().join("working"); + let alice = environment.node("alice"); + let bob = environment.node("bob"); + let mut eve = environment.node("eve"); + let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap(); + let ua = UserAgent::default(); + let now = LocalTime::now().into(); + + radicle::test::fixtures::repository(working.join("acme")); + + test( + "examples/rad-init.md", + working.join("acme"), + Some(&alice.home), + [], + ) + .unwrap(); + + let mut alice = alice.spawn(); + let mut bob = bob.spawn(); + + // Let Eve know about Alice and Bob having the repo. + eve.db + .addresses_mut() + .insert( + &alice.id, + PROTOCOL_VERSION, + node::Features::SEED, + &Alias::new("alice"), + 0, + &ua, + now, + [node::KnownAddress::new( + node::Address::from(alice.addr), + node::address::Source::Imported, + )], + ) + .unwrap(); + eve.db + .addresses_mut() + .insert( + &bob.id, + PROTOCOL_VERSION, + node::Features::SEED, + &Alias::new("bob"), + 0, + &ua, + now, + [node::KnownAddress::new( + node::Address::from(bob.addr), + node::address::Source::Imported, + )], + ) + .unwrap(); + eve.db + .routing_mut() + .add_inventory([&acme], alice.id, now) + .unwrap(); + eve.db + .routing_mut() + .add_inventory([&acme], bob.id, now) + .unwrap(); + eve.config.peers = node::config::PeerConfig::Static; + + let eve = eve.spawn(); + + alice.handle.seed(acme, Scope::Followed).unwrap(); + bob.handle.seed(acme, Scope::Followed).unwrap(); + alice.connect(&bob); + bob.routes_to(&[(acme, alice.id)]); + eve.routes_to(&[(acme, alice.id), (acme, bob.id)]); + alice.routes_to(&[(acme, alice.id), (acme, bob.id)]); + + test( + "examples/rad-clone-connect.md", + working.join("acme"), + Some(&eve.home), + [], + ) + .unwrap(); +} + +#[test] +fn rad_clone_unknown() { + let mut environment = Environment::new(); + let alice = environment.node("alice"); + let working = environment.tempdir().join("working"); + + let alice = alice.spawn(); + + test( + "examples/rad-clone-unknown.md", + working, + Some(&alice.home), + [], + ) + .unwrap(); +} + +#[test] +// User tries to clone; no seeds are available, but user has the repo locally. +fn test_clone_without_seeds() { + let mut environment = Environment::new(); + let mut alice = environment.node("alice"); + let working = environment.tempdir().join("working"); + let rid = alice.project("heartwood", "Radicle Heartwood Protocol & Stack"); + let mut alice = alice.spawn(); + let seeds = alice.handle.seeds_for(rid, [alice.id]).unwrap(); + let connected = seeds.connected().collect::>(); + + assert!(connected.is_empty()); + + alice + .rad("clone", &[rid.to_string().as_str()], working.as_path()) + .unwrap(); + + alice + .rad("inspect", &[], working.join("heartwood").as_path()) + .unwrap(); +}