radicle-heartwood-lfs/radicle-cli/tests/commands.rs

3190 lines
85 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::path::Path;
use std::str::FromStr;
use std::{net, thread, time};
use radicle::git;
use radicle::node;
use radicle::node::address::Store as _;
use radicle::node::config::seeds::{RADICLE_COMMUNITY_NODE, RADICLE_TEAM_NODE};
use radicle::node::config::DefaultSeedingPolicy;
use radicle::node::routing::Store as _;
use radicle::node::Handle as _;
use radicle::node::UserAgent;
use radicle::node::{Address, Alias, DEFAULT_TIMEOUT};
use radicle::prelude::{NodeId, RepoId};
use radicle::profile;
use radicle::profile::{env, Home};
use radicle::storage::{ReadStorage, RefUpdate, RemoteRepository};
use radicle::test::fixtures;
use radicle_cli_test::TestFormula;
use radicle_node::service::policy::Scope;
use radicle_node::service::Event;
use radicle_node::test::environment::{Config, Environment, Node};
#[allow(unused_imports)]
use radicle_node::test::logger;
use radicle_node::PROTOCOL_VERSION;
/// Seed used in tests.
const RAD_SEED: &str = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
mod config {
use super::*;
use radicle::node::config::{Config, Limits, Network, RateLimit, RateLimits};
use radicle::profile;
/// Configuration for a test seed node.
///
/// It sets the `RateLimit::capacity` to `usize::MAX` ensuring
/// that there are no rate limits for test nodes, since they all
/// operate on the same IP address. This prevents any announcement
/// messages from being dropped.
pub fn seed(alias: &'static str) -> Config {
Config {
network: Network::Test,
relay: node::config::Relay::Always,
limits: Limits {
rate: RateLimits {
inbound: RateLimit {
fill_rate: 1.0,
capacity: usize::MAX,
},
outbound: RateLimit {
fill_rate: 1.0,
capacity: usize::MAX,
},
},
..Limits::default()
},
external_addresses: vec![node::Address::from_str(&format!(
"{alias}.radicle.example:8776"
))
.unwrap()],
..node(alias)
}
}
/// Relay node config.
pub fn relay(alias: &'static str) -> Config {
Config {
relay: node::config::Relay::Always,
..node(alias)
}
}
/// Test node config.
pub fn node(alias: &'static str) -> Config {
Config::test(Alias::new(alias))
}
/// Test profile config.
pub fn profile(alias: &'static str) -> profile::Config {
Environment::config(Alias::new(alias))
}
}
/// Run a CLI test file.
fn test<'a>(
test: impl AsRef<Path>,
cwd: impl AsRef<Path>,
home: Option<&Home>,
envs: impl IntoIterator<Item = (&'a str, &'a str)>,
) -> Result<(), Box<dyn std::error::Error>> {
let tmp = tempfile::tempdir().unwrap();
let home = if let Some(home) = home {
home.path().to_path_buf()
} else {
tmp.path().to_path_buf()
};
formula(cwd.as_ref(), test)?
.env("RAD_HOME", home.to_string_lossy())
.envs(envs)
.run()?;
Ok(())
}
fn formula(root: &Path, test: impl AsRef<Path>) -> Result<TestFormula, Box<dyn std::error::Error>> {
let mut formula = TestFormula::new(root.to_path_buf());
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
formula
.env("GIT_AUTHOR_DATE", "1671125284")
.env("GIT_AUTHOR_EMAIL", "radicle@localhost")
.env("GIT_AUTHOR_NAME", "radicle")
.env("GIT_COMMITTER_DATE", "1671125284")
.env("GIT_COMMITTER_EMAIL", "radicle@localhost")
.env("GIT_COMMITTER_NAME", "radicle")
.env("EDITOR", "true")
.env("TZ", "UTC")
.env("LANG", "C")
.env("USER", "alice")
.env(env::RAD_PASSPHRASE, "radicle")
.env(env::RAD_KEYGEN_SEED, RAD_SEED)
.env(env::RAD_RNG_SEED, "0")
.env(env::RAD_LOCAL_TIME, "1671125284")
.envs(git::env::GIT_DEFAULT_CONFIG)
.build(&[
("radicle-remote-helper", "git-remote-rad"),
("radicle-cli", "rad"),
])
.file(base.join(test))?;
Ok(formula)
}
#[test]
fn rad_auth() {
test("examples/rad-auth.md", Path::new("."), None, []).unwrap();
}
#[test]
fn rad_auth_errors() {
test("examples/rad-auth-errors.md", Path::new("."), None, []).unwrap();
}
#[test]
fn rad_issue() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let home = &profile.home;
let working = environment.tmp().join("working");
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-issue.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_update() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = environment.tmp().join("working");
let home = &profile.home;
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-update.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_update_identity() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = environment.tmp().join("working");
let home = &profile.home;
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test(
"examples/rad-cob-update-identity.md",
&working,
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_cob_multiset() {
{
// `rad-cob-multiset` is a `jq` script, which requires `jq` to be installed.
// We test whether `jq` is installed, and have this test succeed if it is not.
// Programmatic skipping of tests is not supported as of 2024-08.
use std::io::ErrorKind;
use std::process::{Command, Stdio};
match Command::new("jq").arg("-V").stdout(Stdio::null()).status() {
Err(e) if e.kind() == ErrorKind::NotFound => {
log::warn!(target: "test", "`jq` not found. Succeeding prematurely.");
return;
}
Err(e) => panic!("while checking for jq: {}", e),
Ok(_) => {}
}
}
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let home = &profile.home;
let working = environment.tmp().join("working");
let base = Path::new(env!("CARGO_MANIFEST_DIR"));
std::fs::create_dir_all(base).unwrap();
std::fs::create_dir_all(working.clone()).unwrap();
// Copy over the script that implements the multiset COB.
std::fs::copy(
base.join("examples").join("rad-cob-multiset"),
working.join("rad-cob-multiset"),
)
.unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-multiset.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_log() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let home = &profile.home;
let working = environment.tmp().join("working");
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-log.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_show() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let home = &profile.home;
let working = environment.tmp().join("working");
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-show.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_cob_migrate() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let home = &profile.home;
let working = environment.tmp().join("working");
home.cobs_db_mut()
.unwrap()
.raw_query(|conn| conn.execute("PRAGMA user_version = 0"))
.unwrap();
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-cob-migrate.md", &working, Some(home), []).unwrap();
}
#[test]
fn rad_init() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
// Setup a test repository.
fixtures::repository(working.path());
test(
"examples/rad-init.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_existing() {
let mut environment = Environment::new();
let mut profile = environment.node(config::node("alice"));
let working = tempfile::tempdir().unwrap();
let rid = profile.project("heartwood", "Radicle Heartwood Protocol & Stack");
test(
"examples/rad-init-existing.md",
working.path(),
Some(&profile.home),
[(
"URL",
git::url::File::new(profile.storage.path())
.rid(rid)
.to_string()
.as_str(),
)],
)
.unwrap();
}
#[test]
fn rad_init_no_seed() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init-no-seed.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_with_existing_remote() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
// Setup a test repository.
fixtures::repository(working.path());
test(
"examples/rad-init-with-existing-remote.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_no_git() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
test(
"examples/rad-init-no-git.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
}
#[test]
fn rad_inspect() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
// Setup a test repository.
fixtures::repository(working.path());
test(
"examples/rad-init.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
test(
"examples/rad-inspect.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
test("examples/rad-inspect-noauth.md", working.path(), None, []).unwrap();
}
#[test]
fn rad_config() {
let mut environment = Environment::new();
let alias = Alias::new("alice");
let profile = environment.profile(profile::Config {
preferred_seeds: vec![RADICLE_COMMUNITY_NODE.clone(), RADICLE_TEAM_NODE.clone()],
..profile::Config::new(alias)
});
let working = tempfile::tempdir().unwrap();
test(
"examples/rad-config.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
}
#[test]
fn rad_checkout() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let copy = tempfile::tempdir().unwrap();
// Setup a test repository.
fixtures::repository(working.path());
test(
"examples/rad-init.md",
working.path(),
Some(&profile.home),
[],
)
.unwrap();
test(
"examples/rad-checkout.md",
copy.path(),
Some(&profile.home),
[],
)
.unwrap();
if cfg!(target_os = "linux") {
test(
"examples/rad-checkout-repo-config-linux.md",
copy.path(),
Some(&profile.home),
[],
)
.unwrap();
} else if cfg!(target_os = "macos") {
test(
"examples/rad-checkout-repo-config-macos.md",
copy.path(),
Some(&profile.home),
[],
)
.unwrap();
}
}
#[test]
fn rad_id() {
let mut environment = Environment::new();
let alice = environment.node(config::node("alice"));
let bob = environment.node(config::node("bob"));
let working = tempfile::tempdir().unwrap();
let working = working.path();
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let bob = bob.spawn();
alice.handle.seed(acme, Scope::All).unwrap();
alice.connect(&bob).converge([&bob]);
let events = alice.handle.events();
bob.fork(acme, bob.home.path()).unwrap();
bob.announce(acme, 2, bob.home.path()).unwrap();
alice.has_remote_of(&acme, &bob.id);
// Alice must have Bob to try add them as a delegate
events
.wait(
|e| matches!(e, Event::RefsFetched { .. }).then_some(()),
time::Duration::from_secs(6),
)
.unwrap();
test(
"examples/rad-id.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_id_threshold() {
let mut environment = Environment::new();
let alice = environment.node(config::node("alice"));
let bob = environment.node(config::node("bob"));
let seed = environment.node(config::node("seed"));
let working = tempfile::tempdir().unwrap();
let working = working.path();
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut seed = seed.spawn();
let mut bob = bob.spawn();
seed.handle.seed(acme, Scope::All).unwrap();
alice.handle.seed(acme, Scope::Followed).unwrap();
alice
.handle
.follow(seed.id, Some(Alias::new("seed")))
.unwrap();
alice.connect(&seed);
bob.connect(&seed).connect(&alice);
alice.routes_to(&[(acme, seed.id)]);
seed.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();
formula(&environment.tmp(), "examples/rad-id-threshold.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.home(
"seed",
working.join("seed"),
[("RAD_HOME", seed.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_id_threshold_soft_fork() {
let mut environment = Environment::new();
let alice = environment.node(config::node("alice"));
let bob = environment.node(config::node("bob"));
let working = tempfile::tempdir().unwrap();
let working = working.path();
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let events = bob.handle.events();
bob.handle.seed(acme, Scope::All).unwrap();
alice.connect(&bob).converge([&bob]);
events
.wait(
|e| matches!(e, Event::RefsFetched { .. }).then_some(()),
time::Duration::from_secs(6),
)
.unwrap();
formula(&environment.tmp(), "examples/rad-id-threshold-soft-fork.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_id_update_delete_field() {
let mut environment = Environment::new();
let alice = environment.node(config::node("alice"));
let working = tempfile::tempdir().unwrap();
let working = working.path();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/rad-id-update-delete-field.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_id_multi_delegate() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let eve = environment.node(Config::test(Alias::new("eve")));
let working = tempfile::tempdir().unwrap();
let working = working.path();
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let mut eve = eve.spawn();
alice.handle.seed(acme, Scope::All).unwrap();
bob.handle.follow(eve.id, None).unwrap();
alice.connect(&bob).converge([&bob]);
eve.connect(&alice).converge([&alice]);
bob.fork(acme, working.join("bob")).unwrap();
bob.has_remote_of(&acme, &alice.id);
alice.has_remote_of(&acme, &bob.id);
eve.fork(acme, working.join("eve")).unwrap();
eve.has_remote_of(&acme, &bob.id);
alice.has_remote_of(&acme, &eve.id);
alice.is_synced_with(&acme, &eve.id);
alice.is_synced_with(&acme, &bob.id);
// TODO: Have formula with two connected nodes and a tracked project.
formula(&environment.tmp(), "examples/rad-id-multi-delegate.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
#[ignore = "slow"]
fn rad_id_collaboration() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let eve = environment.node(Config::test(Alias::new("eve")));
let seed = environment.node(config::seed("seed"));
let distrustful = environment.node(config::seed("distrustful"));
let working = tempfile::tempdir().unwrap();
let working = working.path();
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let mut eve = eve.spawn();
let mut seed = seed.spawn();
let mut distrustful = distrustful.spawn();
// Alice sets up the seed and follows Bob and Eve via the CLI
alice.handle.seed(acme, Scope::Followed).unwrap();
alice
.handle
.follow(seed.id, Some(Alias::new("seed")))
.unwrap();
// The seed is trustful and will fetch from anyone
seed.handle.seed(acme, Scope::All).unwrap();
// The distrustful seed will only interact with Alice and Bob
distrustful.handle.seed(acme, Scope::Followed).unwrap();
distrustful.handle.follow(alice.id, None).unwrap();
distrustful.handle.follow(bob.id, None).unwrap();
alice
.connect(&seed)
.connect(&distrustful)
.converge([&seed, &distrustful]);
bob.connect(&seed)
.connect(&distrustful)
.converge([&seed, &distrustful]);
eve.connect(&seed)
.connect(&distrustful)
.converge([&seed, &distrustful]);
seed.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();
distrustful
.handle
.fetch(acme, alice.id, DEFAULT_TIMEOUT)
.unwrap();
formula(&environment.tmp(), "examples/rad-id-collaboration.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.home(
"eve",
working.join("eve"),
[("RAD_HOME", eve.home.path().display())],
)
.run()
.unwrap();
// Ensure the seeds have fetched all nodes.
let repo = seed.storage.repository(acme).unwrap();
let mut remotes = repo
.remote_ids()
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut expected = vec![alice.id, bob.id, eve.id];
remotes.sort();
expected.sort();
assert_eq!(remotes, expected);
let repo = distrustful.storage.repository(acme).unwrap();
let mut remotes = repo
.remote_ids()
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut expected = vec![alice.id, bob.id, eve.id];
remotes.sort();
expected.sort();
assert_eq!(remotes, expected);
}
#[test]
fn rad_id_conflict() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = tempfile::tempdir().unwrap();
let working = working.path();
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let bob = bob.spawn();
alice.connect(&bob).converge([&bob]);
bob.fork(acme, working.join("bob")).unwrap();
bob.announce(acme, 2, bob.home.path()).unwrap();
alice.has_remote_of(&acme, &bob.id);
formula(&environment.tmp(), "examples/rad-id-conflict.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_id_unknown_field() {
let mut environment = Environment::new();
let alice = environment.node(config::node("alice"));
let working = tempfile::tempdir().unwrap();
let working = working.path();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
test(
"examples/rad-id-unknown-field.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_id_private() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/rad-id-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_node_connect() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = tempfile::tempdir().unwrap();
let alice = alice.spawn();
let bob = bob.spawn();
alice
.rad(
"node",
&["connect", format!("{}@{}", bob.id, bob.addr).as_str()],
working.path(),
)
.unwrap();
let sessions = alice.handle.sessions().unwrap();
let session = sessions.first().unwrap();
assert_eq!(session.nid, bob.id);
assert_eq!(session.addr, bob.addr.into());
assert!(session.state.is_connected());
}
#[test]
fn rad_node() {
let mut environment = Environment::new();
let alice = environment.node(Config {
external_addresses: vec![
Address::from(net::SocketAddr::from(([41, 12, 98, 112], 8776))),
Address::from_str("seed.cloudhead.io:8776").unwrap(),
],
seeding_policy: DefaultSeedingPolicy::Block,
..Config::test(Alias::new("alice"))
});
let working = tempfile::tempdir().unwrap();
let alice = alice.spawn();
fixtures::repository(working.path().join("alice"));
test(
"examples/rad-init-sync-not-connected.md",
working.path().join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/rad-node.md",
working.path().join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_patch() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test("examples/rad-issue.md", working.path(), Some(home), []).unwrap();
test("examples/rad-patch.md", working.path(), Some(home), []).unwrap();
}
#[test]
fn rad_patch_diff() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test("examples/rad-patch-diff.md", working.path(), Some(home), []).unwrap();
}
#[test]
fn rad_patch_edit() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test("examples/rad-patch-edit.md", working.path(), Some(home), []).unwrap();
}
#[test]
fn rad_patch_checkout() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-checkout.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_checkout_revision() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-checkout.md",
working.path(),
Some(home),
[],
)
.unwrap();
test(
"examples/rad-patch-checkout-revision.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_checkout_force() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
bob.handle.seed(acme, Scope::All).unwrap();
alice.connect(&bob).converge([&bob]);
test(
"examples/rad-clone.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
formula(&environment.tmp(), "examples/rad-patch-checkout-force.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_patch_update() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-update.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
#[cfg(not(target_os = "macos"))]
fn rad_patch_ahead_behind() {
use std::fs;
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
fs::write(working.path().join("CONTRIBUTORS"), "Alice Jones\n").unwrap();
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-ahead-behind.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_change_base() {
logger::init(log::Level::Debug);
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-change-base.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_draft() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-draft.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_via_push() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-via-push.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_detached_head() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-detached-head.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_merge_draft() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-merge-draft.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_revert_merge() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-patch-revert-merge.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
#[cfg(not(target_os = "macos"))]
fn rad_review_by_hunk() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let working = tempfile::tempdir().unwrap();
let home = &profile.home;
// Setup a test repository.
fixtures::repository(working.path());
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
test(
"examples/rad-review-by-hunk.md",
working.path(),
Some(home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_delete() {
let mut environment = Environment::new();
let alice = environment.node(config::relay("alice"));
let bob = environment.node(config::relay("bob"));
let seed = environment.node(config::relay("seed"));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let mut seed = seed.spawn();
bob.handle.seed(acme, Scope::All).unwrap();
seed.handle.seed(acme, Scope::All).unwrap();
alice.connect(&bob).connect(&seed).converge([&bob, &seed]);
bob.connect(&seed).converge([&seed]);
bob.routes_to(&[(acme, seed.id)]);
test(
"examples/rad-clone.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
formula(&environment.tmp(), "examples/rad-patch-delete.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.home(
"seed",
working.join("seed"),
[("RAD_HOME", seed.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_clean() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let eve = environment.node(Config::test(Alias::new("eve")));
let working = environment.tmp().join("working");
// Setup a test project.
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
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 mut eve = eve.spawn();
alice.handle.seed(acme, Scope::All).unwrap();
eve.handle.seed(acme, Scope::Followed).unwrap();
bob.connect(&alice).converge([&alice]);
eve.connect(&alice).converge([&alice]);
eve.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();
bob.fork(acme, bob.home.path()).unwrap();
bob.announce(acme, 1, bob.home.path()).unwrap();
bob.has_remote_of(&acme, &alice.id);
alice.has_remote_of(&acme, &bob.id);
eve.has_remote_of(&acme, &alice.id);
formula(&environment.tmp(), "examples/rad-clean.md")
.unwrap()
.home(
"alice",
working.join("acme"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.home(
"eve",
working.join("eve"),
[("RAD_HOME", eve.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_seed_and_follow() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = tempfile::tempdir().unwrap();
let alice = alice.spawn();
test(
"examples/rad-seed-and-follow.md",
working.path(),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_seed_many() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let mut bob = environment.node(Config::test(Alias::new("bob")));
// Bob creates two projects that Alice seeds in the test
let _ = bob.project("heartwood", "Radicle Heartwood Protocol & Stack");
let _ = bob.project("nixpkgs", "Home for Nix Packages");
let working = tempfile::tempdir().unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
test(
"examples/rad-seed-many.md",
working.path(),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_unseed() {
let mut environment = Environment::new();
let mut alice = environment.node(Config::test(Alias::new("alice")));
let working = tempfile::tempdir().unwrap();
// Setup a test project.
alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
let alice = alice.spawn();
test("examples/rad-unseed.md", working, Some(&alice.home), []).unwrap();
}
#[test]
fn rad_unseed_many() {
let mut environment = Environment::new();
let mut alice = environment.node(Config::test(Alias::new("alice")));
let working = tempfile::tempdir().unwrap();
// Setup a test project.
alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
alice.project("nixpkgs", "Home for Nix Packages");
let alice = alice.spawn();
test(
"examples/rad-unseed-many.md",
working,
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_block() {
let mut environment = Environment::new();
let alice = environment.node(Config {
seeding_policy: DefaultSeedingPolicy::permissive(),
..Config::test(Alias::new("alice"))
});
let working = tempfile::tempdir().unwrap();
test("examples/rad-block.md", working, Some(&alice.home), []).unwrap();
}
#[test]
fn rad_clone() {
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 working = environment.tmp().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_directory() {
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 working = environment.tmp().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(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let eve = environment.node(Config::test(Alias::new("eve")));
let working = environment.tmp().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();
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",
working.join("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(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();
logger::init(log::Level::Debug);
// 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::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::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.lock_repository(acme).ok(); // Prevent repo from being re-fetched.
test(
"examples/rad-clone-partial-fail.md",
working.join("eve"),
Some(&eve.home),
[],
)
.unwrap();
}
#[test]
fn rad_clone_connect() {
let mut environment = Environment::new();
let working = environment.tmp().join("working");
let 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 acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
let ua = UserAgent::default();
let now = localtime::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();
let alice = environment.node(config::seed("alice"));
let bob = environment.node(config::seed("bob"));
let mut eve = environment.node(config::seed("eve"));
let rid = RepoId::from_urn("rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5").unwrap();
eve.policies.seed(&rid, Scope::All).unwrap();
formula(&environment.tmp(), "examples/rad-sync-without-node.md")
.unwrap()
.home(
"alice",
alice.home.path(),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
bob.home.path(),
[("RAD_HOME", bob.home.path().display())],
)
.home(
"eve",
eve.home.path(),
[("RAD_HOME", eve.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_self() {
let mut environment = Environment::new();
let alice = environment.node(Config {
external_addresses: vec!["seed.alice.acme:8776".parse().unwrap()],
..Config::test(Alias::new("alice"))
});
let working = environment.tmp().join("working");
test("examples/rad-self.md", working, Some(&alice.home), []).unwrap();
}
#[test]
fn rad_clone_unknown() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().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();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = tempfile::tempdir().unwrap();
let alice = alice.spawn();
fixtures::repository(working.path().join("alice"));
test(
"examples/rad-init-sync-not-connected.md",
working.path().join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_sync_preferred() {
let mut environment = Environment::new();
let mut alice = environment
.node(Config {
seeding_policy: DefaultSeedingPolicy::permissive(),
..Config::test(Alias::new("alice"))
})
.spawn();
let bob = environment.profile(profile::Config {
preferred_seeds: vec![alice.address()],
..config::profile("bob")
});
let mut bob = Node::new(bob).spawn();
let working = environment.tmp().join("working");
bob.connect(&alice);
alice.handle.follow(bob.id, None).unwrap();
fixtures::repository(working.join("bob"));
// Bob initializes a repo after her node has started, and after bob has connected to it.
test(
"examples/rad-init-sync-preferred.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_sync_timeout() {
let mut environment = Environment::new();
let mut alice = environment
.node(Config {
seeding_policy: DefaultSeedingPolicy::Block,
..Config::test(Alias::new("alice"))
})
.spawn();
let bob = environment.profile(profile::Config {
preferred_seeds: vec![alice.address()],
..config::profile("bob")
});
let mut bob = Node::new(bob).spawn();
let working = environment.tmp().join("working");
bob.connect(&alice);
alice.handle.follow(bob.id, None).unwrap();
fixtures::repository(working.join("bob"));
// Bob initializes a repo after her node has started, and after bob has connected to it.
test(
"examples/rad-init-sync-timeout.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_sync_and_clone() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice);
fixtures::repository(working.join("alice"));
// Alice initializes a repo after her node has started, and after bob has connected to it.
test(
"examples/rad-init-sync.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
// Wait for bob to get any updates to the routing table.
bob.converge([&alice]);
test(
"examples/rad-clone.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_fetch() {
let mut environment = Environment::new();
let working = environment.tmp().join("working");
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let mut alice = alice.spawn();
let bob = bob.spawn();
alice.connect(&bob);
fixtures::repository(working.join("alice"));
// Alice initializes a repo after her node has started, and after bob has connected to it.
test(
"examples/rad-init-sync.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
// Wait for bob to get any updates to the routing table.
bob.converge([&alice]);
test(
"examples/rad-fetch.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_fork() {
let mut environment = Environment::new();
let working = environment.tmp().join("working");
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let mut alice = alice.spawn();
let bob = bob.spawn();
alice.connect(&bob);
fixtures::repository(working.join("alice"));
// Alice initializes a repo after her node has started, and after bob has connected to it.
test(
"examples/rad-init-sync.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
// Wait for bob to get any updates to the routing table.
bob.converge([&alice]);
test(
"examples/rad-fetch.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
test(
"examples/rad-fork.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_diff() {
let working = tempfile::tempdir().unwrap();
fixtures::repository(&working);
test("examples/rad-diff.md", working, None, []).unwrap();
}
// FIXME(fintohaps): I plan on fixing this logic in the next patch clone
// should not need to fetch from the network if the repository is already
// available locally.
#[ignore]
#[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(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
let rid = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
let mut alice = alice.spawn();
let seeds = alice.handle.seeds(rid).unwrap();
let connected = seeds.connected().collect::<Vec<_>>();
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();
let working = tempfile::tempdir().unwrap();
let mut alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let rid = alice.project("heartwood", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let events = alice.handle.events();
alice.handle.follow(bob.id, None).unwrap();
alice.connect(&bob);
bob.routes_to(&[(rid, alice.id)]);
bob.fork(rid, working.path()).unwrap();
// Wait for Alice to fetch the clone refs.
events
.wait(
|e| {
matches!(
e,
Event::RefsFetched { updated, .. }
if updated.iter().any(|u| matches!(u, RefUpdate::Created { .. }))
)
.then_some(())
},
time::Duration::from_secs(6),
)
.unwrap();
let bob_repo = bob.storage.repository(rid).unwrap();
let mut bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
let mut bob_cache = radicle::cob::cache::InMemory::default();
let issue = bob_issues
.create(
"Something's fishy",
"I don't know what it is",
&[],
&[],
[],
&mut bob_cache,
&bob.signer,
)
.unwrap();
log::debug!(target: "test", "Issue {} created", issue.id());
// Make sure that Bob's issue refs announcement has a different timestamp than his fork's
// announcement, otherwise Alice will consider it stale.
thread::sleep(time::Duration::from_millis(3));
bob.handle.announce_refs(rid).unwrap();
// Wait for Alice to fetch the issue refs.
events
.iter()
.find(|e| matches!(e, Event::RefsFetched { .. }))
.unwrap();
let alice_repo = alice.storage.repository(rid).unwrap();
let alice_issues = radicle::cob::issue::Issues::open(&alice_repo).unwrap();
let alice_issue = alice_issues.get(issue.id()).unwrap().unwrap();
assert_eq!(alice_issue.title(), "Something's fishy");
}
#[test]
fn test_cob_deletion() {
let mut environment = Environment::new();
let working = tempfile::tempdir().unwrap();
let mut alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let rid = alice.project("heartwood", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
alice.handle.seed(rid, Scope::All).unwrap();
bob.handle.seed(rid, Scope::All).unwrap();
alice.connect(&bob);
bob.routes_to(&[(rid, alice.id)]);
let alice_repo = alice.storage.repository(rid).unwrap();
let mut alice_issues = radicle::cob::issue::Cache::no_cache(&alice_repo).unwrap();
let issue = alice_issues
.create(
"Something's fishy",
"I don't know what it is",
&[],
&[],
[],
&alice.signer,
)
.unwrap();
let issue_id = issue.id();
log::debug!(target: "test", "Issue {} created", issue_id);
bob.rad("clone", &[rid.to_string().as_str()], working.path())
.unwrap();
let bob_repo = bob.storage.repository(rid).unwrap();
let bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
assert!(bob_issues.get(issue_id).unwrap().is_some());
let mut alice_issues = radicle::cob::issue::Cache::no_cache(&alice_repo).unwrap();
alice_issues.remove(issue_id, &alice.signer).unwrap();
log::debug!(target: "test", "Removing issue..");
radicle::assert_matches!(
bob.handle.fetch(rid, alice.id, DEFAULT_TIMEOUT).unwrap(),
radicle::node::FetchResult::Success { .. }
);
let bob_repo = bob.storage.repository(rid).unwrap();
let bob_issues = radicle::cob::issue::Issues::open(&bob_repo).unwrap();
assert!(bob_issues.get(issue_id).unwrap().is_none());
}
#[test]
fn rad_sync() {
let mut environment = Environment::new();
let working = environment.tmp().join("working");
let alice = environment.node(config::seed("alice"));
let bob = environment.node(config::seed("bob"));
let eve = environment.node(config::seed("eve"));
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
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 mut eve = eve.spawn();
bob.handle.seed(acme, Scope::All).unwrap();
eve.handle.seed(acme, Scope::All).unwrap();
alice.connect(&bob);
eve.connect(&alice);
bob.routes_to(&[(acme, alice.id)]);
eve.routes_to(&[(acme, alice.id)]);
alice.routes_to(&[(acme, alice.id), (acme, eve.id), (acme, bob.id)]);
alice.is_synced_with(&acme, &eve.id);
alice.is_synced_with(&acme, &bob.id);
test(
"examples/rad-sync.md",
working.join("acme"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
//
// alice -- seed -- bob
//
fn test_replication_via_seed() {
let mut environment = Environment::new();
let alice = environment.node(config::relay("alice"));
let bob = environment.node(config::relay("bob"));
let seed = environment.node(Config {
seeding_policy: DefaultSeedingPolicy::permissive(),
..config::relay("seed")
});
let working = environment.tmp().join("working");
let rid = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let seed = seed.spawn();
alice.connect(&seed);
bob.connect(&seed);
// Enough time for the next inventory from Seed to not be considered stale by Bob.
thread::sleep(time::Duration::from_millis(3));
alice.routes_to(&[]);
seed.routes_to(&[]);
bob.routes_to(&[]);
// Initialize a repo as Alice.
fixtures::repository(working.join("alice"));
alice
.rad(
"init",
&[
"--name",
"heartwood",
"--description",
"Radicle Heartwood Protocol & Stack",
"--default-branch",
"master",
"--public",
],
working.join("alice"),
)
.unwrap();
alice
.rad("follow", &[&bob.id.to_human()], working.join("alice"))
.unwrap();
alice.routes_to(&[(rid, alice.id), (rid, seed.id)]);
seed.routes_to(&[(rid, alice.id), (rid, seed.id)]);
bob.routes_to(&[(rid, alice.id), (rid, seed.id)]);
let seed_events = seed.handle.events();
let alice_events = alice.handle.events();
bob.fork(rid, working.join("bob")).unwrap();
alice.routes_to(&[(rid, alice.id), (rid, seed.id), (rid, bob.id)]);
seed.routes_to(&[(rid, alice.id), (rid, seed.id), (rid, bob.id)]);
bob.routes_to(&[(rid, alice.id), (rid, seed.id), (rid, bob.id)]);
seed_events.iter().any(|e| {
matches!(
e, Event::RefsFetched { updated, remote, .. }
if remote == bob.id && updated.iter().any(|u| u.is_created())
)
});
alice_events.iter().any(|e| {
matches!(
e, Event::RefsFetched { updated, remote, .. }
if remote == seed.id && updated.iter().any(|u| u.is_created())
)
});
seed.storage
.repository(rid)
.unwrap()
.remote(&bob.id)
.unwrap();
// Seed should send Bob's ref announcement to Alice, after the fetch.
alice
.storage
.repository(rid)
.unwrap()
.remote(&bob.id)
.unwrap();
}
#[test]
fn rad_remote() {
let mut environment = Environment::new();
let alice = environment.node(config::relay("alice"));
let bob = environment.node(config::relay("bob"));
let eve = environment.node(config::relay("eve"));
let working = environment.tmp().join("working");
let home = alice.home.clone();
let rid = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
// Setup a test repository.
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&home),
[],
)
.unwrap();
let mut alice = alice.spawn();
let mut bob = bob.spawn();
let mut eve = eve.spawn();
alice
.handle
.follow(bob.id, Some(Alias::new("bob")))
.unwrap();
bob.connect(&alice);
bob.routes_to(&[(rid, alice.id)]);
bob.fork(rid, bob.home.path()).unwrap();
bob.announce(rid, 2, bob.home.path()).unwrap();
alice.has_remote_of(&rid, &bob.id);
eve.connect(&bob);
eve.routes_to(&[(rid, alice.id)]);
eve.fork(rid, eve.home.path()).unwrap();
eve.announce(rid, 2, eve.home.path()).unwrap();
alice.has_remote_of(&rid, &eve.id);
test(
"examples/rad-remote.md",
working.join("alice"),
Some(&home),
[],
)
.unwrap();
}
#[test]
fn rad_merge_via_push() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
test(
"examples/rad-merge-via-push.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_merge_after_update() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
test(
"examples/rad-merge-after-update.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_merge_no_ff() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/rad-merge-no-ff.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_patch_pull_update() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
formula(&environment.tmp(), "examples/rad-patch-pull-update.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
bob.home.path(),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_patch_open_explore() {
logger::init(log::Level::Debug);
let mut environment = Environment::new();
let seed = environment
.node(Config {
seeding_policy: DefaultSeedingPolicy::permissive(),
..config::seed("seed")
})
.spawn();
let bob = environment.profile(profile::Config {
preferred_seeds: vec![seed.address()],
..config::profile("bob")
});
let mut bob = Node::new(bob).spawn();
let working = environment.tmp().join("working");
fixtures::repository(&working);
bob.connect(&seed);
bob.init("heartwood", "", &working).unwrap();
bob.converge([&seed]);
test(
"examples/rad-patch-open-explore.md",
&working,
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_private() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_private_no_seed() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init-private-no-seed.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn rad_init_private_seed() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
let alice = alice.spawn();
let mut bob = bob.spawn();
test(
"examples/rad-init-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
bob.connect(&alice).converge([&alice]);
formula(&environment.tmp(), "examples/rad-init-private-seed.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
bob.home.path(),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_init_private_clone() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
let alice = alice.spawn();
let mut bob = bob.spawn();
test(
"examples/rad-init-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
bob.connect(&alice).converge([&alice]);
formula(&environment.tmp(), "examples/rad-init-private-clone.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
bob.home.path(),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_init_private_clone_seed() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
let alice = alice.spawn();
let mut bob = bob.spawn();
test(
"examples/rad-init-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
bob.connect(&alice).converge([&alice]);
formula(
&environment.tmp(),
"examples/rad-init-private-clone-seed.md",
)
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
bob.home.path(),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_publish() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init-private.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/rad-publish.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn framework_home() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
formula(&environment.tmp(), "examples/framework/home.md")
.unwrap()
.home(
"alice",
alice.home.path(),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
bob.home.path(),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn git_push_diverge() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.fork(acme, working.join("bob")).unwrap();
alice.has_remote_of(&acme, &bob.id);
formula(&environment.tmp(), "examples/git/git-push-diverge.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn git_push_converge() {
use std::fs;
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let eve = environment.node(Config::test(Alias::new("eve")));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
let mut eve = eve.spawn();
bob.connect(&alice).connect(&eve).converge([&alice]);
eve.connect(&alice).converge([&alice]);
bob.fork(acme, working.join("bob")).unwrap();
eve.fork(acme, working.join("eve")).unwrap();
alice.has_remote_of(&acme, &bob.id);
alice.has_remote_of(&acme, &eve.id);
fs::write(
working.join("bob").join("heartwood").join("README"),
"Hello\n",
)
.unwrap();
fs::write(
working.join("eve").join("heartwood").join("README"),
"Hello, world!\n",
)
.unwrap();
formula(&environment.tmp(), "examples/git/git-push-converge.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.home(
"eve",
working.join("eve").join("heartwood"),
[("RAD_HOME", eve.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn git_push_amend() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.fork(acme, working.join("bob")).unwrap();
alice.has_remote_of(&acme, &bob.id);
formula(&environment.tmp(), "examples/git/git-push-amend.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn git_push_rollback() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.fork(acme, working.join("bob")).unwrap();
alice.has_remote_of(&acme, &bob.id);
formula(&environment.tmp(), "examples/git/git-push-rollback.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_push_and_pull_patches() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.fork(acme, working.join("bob")).unwrap();
alice.has_remote_of(&acme, &bob.id);
formula(&environment.tmp(), "examples/rad-push-and-pull-patches.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_patch_fetch_1() {
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 working = environment.tmp().join("working");
let (repo, _) = fixtures::repository(working.join("alice"));
let rid = alice.project_from("heartwood", "Radicle Heartwood Protocol & Stack", &repo);
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.clone(rid, working.join("bob")).unwrap();
formula(&environment.tmp(), "examples/rad-patch-fetch-1.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_watch() {
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 working = environment.tmp().join("working");
let (repo, _) = fixtures::repository(working.join("alice"));
let rid = alice.project_from("heartwood", "Radicle Heartwood Protocol & Stack", &repo);
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.clone(rid, working.join("bob")).unwrap();
formula(&environment.tmp(), "examples/rad-watch.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob").join("heartwood"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_inbox() {
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 working = environment.tmp().join("working");
let (repo1, _) = fixtures::repository(working.join("alice").join("heartwood"));
let (repo2, _) = fixtures::repository(working.join("alice").join("radicle-git"));
let rid1 = alice.project_from("heartwood", "Radicle Heartwood Protocol & Stack", &repo1);
let rid2 = alice.project_from("radicle-git", "Radicle Git", &repo2);
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
bob.clone(rid1, working.join("bob")).unwrap();
bob.clone(rid2, working.join("bob")).unwrap();
formula(&environment.tmp(), "examples/rad-inbox.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_patch_fetch_2() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/rad-patch-fetch-2.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn git_push_and_fetch() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
test(
"examples/rad-clone.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
test(
"examples/git/git-push.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/git/git-fetch.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
test(
"examples/git/git-push-delete.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
}
#[test]
fn git_tag() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/rad-init.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
test(
"examples/rad-clone.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
formula(&environment.tmp(), "examples/git/git-tag.md")
.unwrap()
.home(
"alice",
working.join("alice"),
[("RAD_HOME", alice.home.path().display())],
)
.home(
"bob",
working.join("bob"),
[("RAD_HOME", bob.home.path().display())],
)
.run()
.unwrap();
}
#[test]
fn rad_workflow() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let working = environment.tmp().join("working");
fixtures::repository(working.join("alice"));
test(
"examples/workflow/1-new-project.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
let alice = alice.spawn();
let mut bob = bob.spawn();
bob.connect(&alice).converge([&alice]);
test(
"examples/workflow/2-cloning.md",
working.join("bob"),
Some(&bob.home),
[],
)
.unwrap();
test(
"examples/workflow/3-issues.md",
working.join("bob").join("heartwood"),
Some(&bob.home),
[],
)
.unwrap();
test(
"examples/workflow/4-patching-contributor.md",
working.join("bob").join("heartwood"),
Some(&bob.home),
[],
)
.unwrap();
test(
"examples/workflow/5-patching-maintainer.md",
working.join("alice"),
Some(&alice.home),
[],
)
.unwrap();
test(
"examples/workflow/6-pulling-contributor.md",
working.join("bob").join("heartwood"),
Some(&bob.home),
[],
)
.unwrap();
}
#[test]
fn rad_job() {
let mut environment = Environment::new();
let profile = environment.profile(config::profile("alice"));
let home = &profile.home;
let working = environment.tmp().join("working");
// Setup a test repository.
fixtures::repository(&working);
test("examples/rad-init.md", &working, Some(home), []).unwrap();
test("examples/rad-job.md", &working, Some(home), []).unwrap();
}