cli: Add `rad-clone` test

This commit is contained in:
Alexis Sellier 2023-02-05 01:04:47 +01:00
parent a8531eff36
commit 2649e9c6fe
No known key found for this signature in database
6 changed files with 126 additions and 32 deletions

View File

@ -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)
```

View File

@ -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)
);
}

View File

@ -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();
}

View File

@ -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"))

View File

@ -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,

View File

@ -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());