Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Test clone failure for unknown repo
Alexis Sellier committed 3 years ago
commit b108342faba43dedafa1e1ef75e3c86ab6e016d9
parent 69ac8be8fdf5941d3149b0950468bdc17b8143d3
7 files changed +69 -13
added radicle-cli/examples/rad-clone-unknown.md
@@ -0,0 +1,7 @@
+
Trying to clone a repository that is not in our routing table returns an error:
+

+
```
+
$ rad clone rad:zVNuptPuk5XauitpCWSNVCXGGfXW
+
ok Tracking relationship established for rad:zVNuptPuk5XauitpCWSNVCXGGfXW
+
== Clone failed no seeds found for rad:zVNuptPuk5XauitpCWSNVCXGGfXW
+
```
modified radicle-cli/examples/rad-init.md
@@ -3,7 +3,7 @@ To create your first radicle project, navigate to a git repository, and run
the `init` command:

```
-
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm
+
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --no-sync

Initializing local 🌱 project in .

modified radicle-cli/src/commands/clone.rs
@@ -131,6 +131,8 @@ pub enum CloneError {
    Payload(#[from] doc::PayloadError),
    #[error("project error: {0}")]
    Project(#[from] ProjectError),
+
    #[error("no seeds found for {0}")]
+
    NotFound(Id),
}

pub fn clone<G: Signer>(
@@ -149,8 +151,11 @@ pub fn clone<G: Signer>(
        );
    }

-
    // Get seeds.
+
    // Get seeds. This consults the local routing table only.
    let seeds = node.seeds(id)?;
+
    if seeds.is_empty() {
+
        return Err(CloneError::NotFound(id));
+
    }
    // Fetch from all seeds.
    for seed in seeds {
        let spinner = term::spinner(format!(
modified radicle-cli/src/terminal/io.rs
@@ -87,7 +87,7 @@ pub fn help(name: &str, version: &str, description: &str, usage: &str) {
}

pub fn usage(name: &str, usage: &str) {
-
    eprintln!(
+
    println!(
        "{} {}\n{}",
        style("==").red(),
        style(format!("Error: rad-{name}: invalid usage")).red(),
@@ -95,8 +95,8 @@ pub fn usage(name: &str, usage: &str) {
    );
}

-
pub fn eprintln(prefix: impl fmt::Display, msg: impl fmt::Display) {
-
    eprintln!("{prefix} {msg}");
+
pub fn println(prefix: impl fmt::Display, msg: impl fmt::Display) {
+
    println!("{prefix} {msg}");
}

pub fn indented(msg: impl fmt::Display) {
@@ -108,7 +108,7 @@ pub fn subcommand(msg: impl fmt::Display) {
}

pub fn warning(warning: &str) {
-
    eprintln!(
+
    println!(
        "{} {} {}",
        style("**").yellow(),
        style("Warning:").yellow().bold(),
@@ -117,7 +117,7 @@ pub fn warning(warning: &str) {
}

pub fn error(error: impl fmt::Display) {
-
    eprintln!("{} {}", style("==").red(), style(error).red());
+
    println!("{} {}", style("==").red(), style(error).red());
}

pub fn fail(header: &str, error: &anyhow::Error) {
@@ -129,7 +129,7 @@ pub fn fail(header: &str, error: &anyhow::Error) {
        " "
    };

-
    eprintln!(
+
    println!(
        "{} {}{}{}",
        style("==").red(),
        style(header).red().reverse(),
@@ -139,7 +139,7 @@ pub fn fail(header: &str, error: &anyhow::Error) {

    let cause = error.root_cause();
    if cause.to_string() != error.to_string() {
-
        eprintln!(
+
        println!(
            "{} {}",
            style("==").red().dim(),
            style(error.root_cause()).red().dim()
@@ -148,7 +148,7 @@ pub fn fail(header: &str, error: &anyhow::Error) {
    }

    if let Some(Error::WithHint { hint, .. }) = error.downcast_ref::<Error>() {
-
        eprintln!("{} {}", style("==").yellow(), style(hint).yellow(),);
+
        println!("{} {}", style("==").yellow(), style(hint).yellow(),);
        blank();
    }
}
modified radicle-cli/src/terminal/spinner.rs
@@ -35,7 +35,7 @@ impl Spinner {
    pub fn error(mut self, msg: impl ToString) {
        let msg = msg.to_string();

-
        self.message = format!("{} (error: {})", self.message, msg);
+
        self.message = format!("{} error: {}", self.message, msg);
        self.set_failed();
    }

@@ -52,7 +52,7 @@ impl Spinner {

    pub fn set_failed(&mut self) {
        self.progress.finish_and_clear();
-
        term::eprintln(style("!!").red().reverse(), &self.message);
+
        term::println(style("!!").red().reverse(), &self.message);
    }
}

modified radicle-cli/tests/commands.rs
@@ -174,6 +174,25 @@ fn rad_clone() {
}

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

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

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

+
    test(
+
        "examples/rad-clone-unknown.md",
+
        working,
+
        Some(&alice.home),
+
        [],
+
    )
+
    .unwrap();
+
}
+

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

modified radicle-cli/tests/framework/mod.rs
@@ -8,6 +8,9 @@ use snapbox::cmd::{Command, OutputAssert};
use snapbox::{Assert, Substitutions};
use thiserror::Error;

+
/// Error lines in the CLI are prefixed with this string.
+
const ERROR_PREFIX: &str = "==";
+

#[derive(Error, Debug)]
pub enum Error {
    #[error("parsing failed")]
@@ -18,6 +21,12 @@ pub enum Error {
    Snapbox(#[from] snapbox::Error),
}

+
#[derive(Debug, PartialEq, Eq)]
+
enum ExitStatus {
+
    Success,
+
    Failure,
+
}
+

/// A test which may contain multiple assertions.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Test {
@@ -36,6 +45,8 @@ pub struct Assertion {
    args: Vec<String>,
    /// Expected output (stdout or stderr).
    expected: String,
+
    /// Expected exit status.
+
    exit: ExitStatus,
}

#[derive(Debug, Default, PartialEq, Eq)]
@@ -112,8 +123,12 @@ impl TestFormula {
                        command: cmd.to_owned(),
                        args: args.to_owned(),
                        expected: String::new(),
+
                        exit: ExitStatus::Success,
                    });
                } else if let Some(test) = test.assertions.last_mut() {
+
                    if line.starts_with(ERROR_PREFIX) {
+
                        test.exit = ExitStatus::Failure;
+
                    }
                    test.expected.push_str(line.as_str());
                    test.expected.push('\n');
                } else {
@@ -181,7 +196,14 @@ impl TestFormula {
                match result {
                    Ok(output) => {
                        let assert = OutputAssert::new(output).with_assert(assert.clone());
-
                        assert.stdout_matches(&assertion.expected).success();
+
                        match assertion.exit {
+
                            ExitStatus::Success => {
+
                                assert.stdout_matches(&assertion.expected).success();
+
                            }
+
                            ExitStatus::Failure => {
+
                                assert.stdout_matches(&assertion.expected).failure();
+
                            }
+
                        }
                    }
                    Err(err) => {
                        if err.kind() == io::ErrorKind::NotFound {
@@ -246,6 +268,7 @@ $ rad sync
                            expected: String::from(
                                "Tracking relationship established for @dave.\nNothing to do.\n\n",
                            ),
+
                            exit: ExitStatus::Success,
                        },
                        Assertion {
                            command: String::from("rad"),
@@ -253,6 +276,7 @@ $ rad sync
                            expected: String::from(
                                "Tracking relationship established for @sean.\nNothing to do.\n",
                            ),
+
                            exit: ExitStatus::Success,
                        },
                    ],
                },
@@ -262,6 +286,7 @@ $ rad sync
                        command: String::from("rad"),
                        args: vec![String::from("sync")],
                        expected: String::new(),
+
                        exit: ExitStatus::Success,
                    }],
                },
            ],