Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Add `rad-clone` test
Alexis Sellier committed 3 years ago
commit 2649e9c6fe72d9ef148f9e76bad6883866c0bb75
parent a8531eff36e3a68c4809ea62f5fe380918949825
6 files changed +126 -32
added radicle-cli/examples/rad-clone.md
@@ -0,0 +1,45 @@
+
To create a local copy of a repository on the radicle network, we use the
+
`clone` command, followed by the identifier or *RID* of the repository:
+

+
```
+
$ rad clone rad:zVNuptPuk5XauitpCWSNVCXGGfXW
+
ok Tracking relationship established for rad:zVNuptPuk5XauitpCWSNVCXGGfXW
+
ok Fetching rad:zVNuptPuk5XauitpCWSNVCXGGfXW from z6MknSL…StBU8Vi..
+
ok Forking under z6Mkt67…v4N1tRk..
+
ok Creating checkout in ./acme..
+
ok Remote z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi created
+
ok Remote-tracking branch z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
+

+
🌱 Project successfully cloned under [..]/acme/
+

+
```
+

+
We can now have a look at the new working copy that was created from the cloned
+
repository:
+

+
```
+
$ cd acme
+
$ ls
+
README
+
$ cat README
+
Hello World!
+
```
+

+
Let's check that the remote tracking branch was setup correctly:
+

+
```
+
$ git branch --remotes
+
  rad/master
+
  z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
+
```
+

+
The first branch is ours, and the second points to the repository delegate.
+
We can also take a look at the remotes:
+

+
```
+
$ git remote -v
+
rad	rad://zVNuptPuk5XauitpCWSNVCXGGfXW/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (fetch)
+
rad	rad://zVNuptPuk5XauitpCWSNVCXGGfXW/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (push)
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi	rad://zVNuptPuk5XauitpCWSNVCXGGfXW/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (fetch)
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi	rad://zVNuptPuk5XauitpCWSNVCXGGfXW/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (push)
+
```
modified radicle-cli/src/commands/clone.rs
@@ -144,7 +144,7 @@ pub fn clone<G: Signer>(
    // Track.
    if node.track_repo(id)? {
        term::success!(
-
            "Tracking relationship restablished for {}",
+
            "Tracking relationship established for {}",
            term::format::tertiary(id)
        );
    }
modified radicle-cli/tests/commands.rs
@@ -5,7 +5,10 @@ use radicle::git;
use radicle::profile::Home;
use radicle::test::fixtures;

-
use radicle_node::test::environment::Environment;
+
use radicle_node::test::{
+
    environment::{Config, Environment},
+
    logger,
+
};

mod framework;
use framework::TestFormula;
@@ -64,15 +67,15 @@ fn rad_issue() {
    let mut environment = Environment::new();
    let profile = environment.profile("alice");
    let home = &profile.home;
-
    let working = tempfile::tempdir().unwrap();
+
    let working = environment.tmp().join("working");

    // Setup a test repository.
-
    fixtures::repository(working.path());
+
    fixtures::repository(&working);
    // Set a fixed commit time.
    env::set_var(radicle_cob::git::RAD_COMMIT_TIME, "1671125284");

-
    test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
-
    test("examples/rad-issue.md", working.path(), Some(home), []).unwrap();
+
    test("examples/rad-init.md", &working, Some(home), []).unwrap();
+
    test("examples/rad-issue.md", &working, Some(home), []).unwrap();
}

#[test]
@@ -149,3 +152,23 @@ fn rad_patch() {
    test("examples/rad-issue.md", working.path(), Some(home), []).unwrap();
    test("examples/rad-patch.md", working.path(), Some(home), []).unwrap();
}
+

+
#[test]
+
fn rad_clone() {
+
    logger::init(log::Level::Debug);
+

+
    let mut environment = Environment::new();
+
    let mut alice = environment.node("alice");
+
    let bob = environment.node("bob");
+
    let working = environment.tmp().join("working");
+

+
    // Setup a test project.
+
    let _ = alice.project("acme", "");
+

+
    let alice = alice.spawn(Config::default());
+
    let mut bob = bob.spawn(Config::default());
+

+
    bob.connect(&alice).converge([&alice]);
+

+
    test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap();
+
}
modified radicle-cli/tests/framework/mod.rs
@@ -139,6 +139,8 @@ impl TestFormula {
    pub fn run(&mut self) -> Result<bool, io::Error> {
        let assert = Assert::new().substitutions(self.subs.clone());

+
        fs::create_dir_all(&self.cwd)?;
+

        for test in &self.tests {
            for assertion in &test.assertions {
                let program = if assertion.program == "rad" {
@@ -162,7 +164,14 @@ impl TestFormula {
                } else {
                    PathBuf::from(&assertion.program)
                };
+
                log::debug!(target: "test", "Running `{}` in `{}`..", program.display(), self.cwd.display());

+
                if !program.exists() {
+
                    log::error!(target: "test", "Program {} does not exist..", program.display());
+
                }
+
                if !self.cwd.exists() {
+
                    log::error!(target: "test", "Directory {} does not exist..", self.cwd.display());
+
                }
                let result = Command::new(program.clone())
                    .env_clear()
                    .envs(env::vars().filter(|(k, _)| k == "PATH"))
modified radicle-node/src/test/environment.rs
@@ -1,5 +1,5 @@
use std::mem::ManuallyDrop;
-
use std::path::Path;
+
use std::path::{Path, PathBuf};
use std::{
    collections::{BTreeMap, BTreeSet},
    iter, net, thread,
@@ -23,6 +23,8 @@ use crate::node::NodeId;
use crate::storage::git::transport;
use crate::{runtime, runtime::Handle, service, Runtime};

+
pub use service::Config;
+

/// Test environment.
pub struct Environment {
    tempdir: tempfile::TempDir,
@@ -44,6 +46,11 @@ impl Environment {
        Self::default()
    }

+
    /// Return the temp directory path.
+
    pub fn tmp(&self) -> PathBuf {
+
        self.tempdir.path().join("misc")
+
    }
+

    /// Create a new node in this environment. This should be used when a running node
    /// is required. Use [`Environment::profile`] otherwise.
    pub fn node(&mut self, name: &str) -> Node<MemorySigner> {
@@ -60,7 +67,9 @@ impl Environment {
    /// Create a new profile in this environment.
    /// This should be used when a running node is not required.
    pub fn profile(&mut self, name: &str) -> Profile {
-
        let home = Home::new(self.tempdir.path().join(name)).init().unwrap();
+
        let home = Home::new(self.tmp().join("home").join(name))
+
            .init()
+
            .unwrap();
        let storage = Storage::open(home.storage()).unwrap();
        let keystore = Keystore::new(&home.keys());
        let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
@@ -94,6 +103,7 @@ pub struct NodeHandle<G: Signer + cyphernet::EcSign + 'static> {
    pub id: NodeId,
    pub storage: Storage,
    pub signer: G,
+
    pub home: Home,
    pub addr: net::SocketAddr,
    pub thread: ManuallyDrop<thread::JoinHandle<Result<(), runtime::Error>>>,
    pub handle: ManuallyDrop<Handle<G>>,
@@ -115,7 +125,7 @@ impl<G: Signer + cyphernet::EcSign + 'static> Drop for NodeHandle<G> {

impl<G: Signer + cyphernet::EcSign> NodeHandle<G> {
    /// Connect this node to another node, and wait for the connection to be established both ways.
-
    pub fn connect(&mut self, remote: &NodeHandle<G>) {
+
    pub fn connect(&mut self, remote: &NodeHandle<G>) -> &mut Self {
        self.handle.connect(remote.id, remote.addr.into()).unwrap();

        loop {
@@ -137,6 +147,15 @@ impl<G: Signer + cyphernet::EcSign> NodeHandle<G> {
            }
            thread::sleep(Duration::from_millis(100));
        }
+
        self
+
    }
+

+
    /// Wait until this node's routing table matches the remotes.
+
    pub fn converge<'a>(
+
        &'a self,
+
        remotes: impl IntoIterator<Item = &'a NodeHandle<G>>,
+
    ) -> BTreeSet<(Id, NodeId)> {
+
        converge(iter::once(self).chain(remotes.into_iter()))
    }
}

@@ -167,7 +186,7 @@ impl<G: cyphernet::EcSign<Pk = NodeId, Sig = Signature> + Signer + Clone> Node<G
        let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050);
        let daemon = ([0, 0, 0, 0], fastrand::u16(1025..)).into();
        let rt = Runtime::init(
-
            self.home,
+
            self.home.clone(),
            config,
            listen,
            proxy,
@@ -189,6 +208,7 @@ impl<G: cyphernet::EcSign<Pk = NodeId, Sig = Signature> + Signer + Clone> Node<G
            id,
            storage: self.storage,
            signer: self.signer,
+
            home: self.home,
            addr,
            handle,
            thread,
@@ -196,18 +216,15 @@ impl<G: cyphernet::EcSign<Pk = NodeId, Sig = Signature> + Signer + Clone> Node<G
    }

    /// Populate a storage instance with a project.
-
    pub fn project(&mut self, name: &str) -> Id {
+
    pub fn project(&mut self, name: &str, description: &str) -> Id {
        transport::local::register(self.storage.clone());

        let tmp = tempfile::tempdir().unwrap();
        let (repo, _) = fixtures::gen::repository(tmp.path());
-
        let description = iter::repeat_with(fastrand::alphabetic)
-
            .take(12)
-
            .collect::<String>();
        let id = rad::init(
            &repo,
            name,
-
            &description,
+
            description,
            refname!("master"),
            &self.signer,
            &self.storage,
modified radicle-node/src/tests/e2e.rs
@@ -20,8 +20,8 @@ fn test_inventory_sync_basic() {
    let mut alice = Node::init(tmp.path());
    let mut bob = Node::init(tmp.path());

-
    alice.project("alice");
-
    bob.project("bob");
+
    alice.project("alice", "");
+
    bob.project("bob", "");

    let mut alice = alice.spawn(service::Config::default());
    let bob = bob.spawn(service::Config::default());
@@ -45,9 +45,9 @@ fn test_inventory_sync_bridge() {
    let mut bob = Node::init(tmp.path());
    let mut eve = Node::init(tmp.path());

-
    alice.project("alice");
-
    bob.project("bob");
-
    eve.project("eve");
+
    alice.project("alice", "");
+
    bob.project("bob", "");
+
    eve.project("eve", "");

    let mut alice = alice.spawn(service::Config::default());
    let mut bob = bob.spawn(service::Config::default());
@@ -76,10 +76,10 @@ fn test_inventory_sync_ring() {
    let mut eve = Node::init(tmp.path());
    let mut carol = Node::init(tmp.path());

-
    alice.project("alice");
-
    bob.project("bob");
-
    eve.project("eve");
-
    carol.project("carol");
+
    alice.project("alice", "");
+
    bob.project("bob", "");
+
    eve.project("eve", "");
+
    carol.project("carol", "");

    let mut alice = alice.spawn(service::Config::default());
    let mut bob = bob.spawn(service::Config::default());
@@ -114,11 +114,11 @@ fn test_inventory_sync_star() {
    let mut carol = Node::init(tmp.path());
    let mut dave = Node::init(tmp.path());

-
    alice.project("alice");
-
    bob.project("bob");
-
    eve.project("eve");
-
    carol.project("carol");
-
    dave.project("dave");
+
    alice.project("alice", "");
+
    bob.project("bob", "");
+
    eve.project("eve", "");
+
    carol.project("carol", "");
+
    dave.project("dave", "");

    let alice = alice.spawn(service::Config::default());
    let mut bob = bob.spawn(service::Config::default());
@@ -142,7 +142,7 @@ fn test_replication() {
    let tmp = tempfile::tempdir().unwrap();
    let alice = Node::init(tmp.path());
    let mut bob = Node::init(tmp.path());
-
    let acme = bob.project("acme");
+
    let acme = bob.project("acme", "");

    let mut alice = alice.spawn(service::Config::default());
    let bob = bob.spawn(service::Config::default());
@@ -199,7 +199,7 @@ fn test_clone() {
    let tmp = tempfile::tempdir().unwrap();
    let alice = Node::init(tmp.path());
    let mut bob = Node::init(tmp.path());
-
    let acme = bob.project("acme");
+
    let acme = bob.project("acme", "");

    let mut alice = alice.spawn(service::Config::default());
    let bob = bob.spawn(service::Config::default());
@@ -249,7 +249,7 @@ fn test_fetch_up_to_date() {
    let tmp = tempfile::tempdir().unwrap();
    let alice = Node::init(tmp.path());
    let mut bob = Node::init(tmp.path());
-
    let acme = bob.project("acme");
+
    let acme = bob.project("acme", "");

    let mut alice = alice.spawn(service::Config::default());
    let bob = bob.spawn(service::Config::default());