node: Create shared test environment
We move and combine some of the test code, so that the CLI tests and node tests use shared code.
This commit is contained in:
parent
abb288e742
commit
b542ddcd6c
|
|
@ -1852,6 +1852,7 @@ dependencies = [
|
|||
"radicle",
|
||||
"radicle-cob",
|
||||
"radicle-crypto",
|
||||
"radicle-node",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
|
|
|
|||
|
|
@ -43,5 +43,6 @@ path = "../radicle-crypto"
|
|||
pretty_assertions = { version = "1.3.0" }
|
||||
tempfile = { version = "3.3.0" }
|
||||
radicle = { path = "../radicle", features = ["test"] }
|
||||
radicle-node = { path = "../radicle-node", features = ["test"] }
|
||||
shlex = { version = "1.1.0" }
|
||||
snapbox = { version = "0.4.3" }
|
||||
|
|
|
|||
|
|
@ -1,56 +1,14 @@
|
|||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
use radicle::crypto::ssh::Keystore;
|
||||
use radicle::crypto::KeyPair;
|
||||
use radicle::git;
|
||||
use radicle::profile::{Home, Profile};
|
||||
use radicle::storage::git::transport;
|
||||
use radicle::storage::git::Storage;
|
||||
use radicle::profile::Home;
|
||||
use radicle::test::fixtures;
|
||||
|
||||
use radicle_node::test::environment::Environment;
|
||||
|
||||
mod framework;
|
||||
use framework::TestFormula;
|
||||
use radicle_crypto::Seed;
|
||||
|
||||
/// Test environment.
|
||||
pub struct Environment {
|
||||
tempdir: tempfile::TempDir,
|
||||
users: usize,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
/// Create a new test environment.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
tempdir: tempfile::tempdir().unwrap(),
|
||||
users: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new profile in this environment.
|
||||
fn profile(&mut self, name: &str) -> Profile {
|
||||
let home = Home::new(self.tempdir.path().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]));
|
||||
|
||||
transport::local::register(storage.clone());
|
||||
keystore
|
||||
.store(keypair.clone(), "radicle", "radicle".to_owned())
|
||||
.unwrap();
|
||||
|
||||
// Ensures that each user has a unique but deterministic public key.
|
||||
self.users += 1;
|
||||
|
||||
Profile {
|
||||
home,
|
||||
storage,
|
||||
keystore,
|
||||
public_key: keypair.pk.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a CLI test file.
|
||||
fn test<'a>(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod arbitrary;
|
||||
pub mod environment;
|
||||
pub mod gossip;
|
||||
pub mod handle;
|
||||
pub mod logger;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,264 @@
|
|||
use std::mem::ManuallyDrop;
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
iter, net, thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
|
||||
use radicle::crypto::test::signer::MockSigner;
|
||||
use radicle::crypto::{KeyPair, Seed, Signature, Signer};
|
||||
use radicle::git::refname;
|
||||
use radicle::identity::Id;
|
||||
use radicle::node::Handle as _;
|
||||
use radicle::profile::Home;
|
||||
use radicle::profile::Profile;
|
||||
use radicle::rad;
|
||||
use radicle::storage::ReadStorage;
|
||||
use radicle::test::fixtures;
|
||||
use radicle::Storage;
|
||||
|
||||
use crate::node::NodeId;
|
||||
use crate::storage::git::transport;
|
||||
use crate::{runtime, runtime::Handle, service, Runtime};
|
||||
|
||||
/// Test environment.
|
||||
pub struct Environment {
|
||||
tempdir: tempfile::TempDir,
|
||||
users: usize,
|
||||
}
|
||||
|
||||
impl Default for Environment {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tempdir: tempfile::tempdir().unwrap(),
|
||||
users: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
/// Create a new test environment.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let profile = self.profile(name);
|
||||
let signer = MemorySigner::load(&profile.keystore, "radicle".to_owned().into()).unwrap();
|
||||
|
||||
Node {
|
||||
home: profile.home,
|
||||
signer,
|
||||
storage: profile.storage,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 storage = Storage::open(home.storage()).unwrap();
|
||||
let keystore = Keystore::new(&home.keys());
|
||||
let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
|
||||
|
||||
transport::local::register(storage.clone());
|
||||
keystore
|
||||
.store(keypair.clone(), "radicle", "radicle".to_owned())
|
||||
.unwrap();
|
||||
|
||||
// Ensures that each user has a unique but deterministic public key.
|
||||
self.users += 1;
|
||||
|
||||
Profile {
|
||||
home,
|
||||
storage,
|
||||
keystore,
|
||||
public_key: keypair.pk.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A node that can be run.
|
||||
pub struct Node<G> {
|
||||
pub home: Home,
|
||||
pub signer: G,
|
||||
pub storage: Storage,
|
||||
}
|
||||
|
||||
/// Handle to a running node.
|
||||
pub struct NodeHandle<G: Signer + cyphernet::EcSign + 'static> {
|
||||
pub id: NodeId,
|
||||
pub storage: Storage,
|
||||
pub signer: G,
|
||||
pub addr: net::SocketAddr,
|
||||
pub thread: ManuallyDrop<thread::JoinHandle<Result<(), runtime::Error>>>,
|
||||
pub handle: ManuallyDrop<Handle<G>>,
|
||||
}
|
||||
|
||||
impl<G: Signer + cyphernet::EcSign + 'static> Drop for NodeHandle<G> {
|
||||
fn drop(&mut self) {
|
||||
log::debug!(target: "test", "Node {} shutting down..", self.id);
|
||||
|
||||
unsafe { ManuallyDrop::take(&mut self.handle) }
|
||||
.shutdown()
|
||||
.unwrap();
|
||||
unsafe { ManuallyDrop::take(&mut self.thread) }
|
||||
.join()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
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>) {
|
||||
self.handle.connect(remote.id, remote.addr.into()).unwrap();
|
||||
|
||||
loop {
|
||||
let local_sessions = self.handle.sessions().unwrap();
|
||||
let remote_sessions = remote.handle.sessions().unwrap();
|
||||
|
||||
let local_sessions = local_sessions
|
||||
.negotiated()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<BTreeSet<_>>();
|
||||
let remote_sessions = remote_sessions
|
||||
.negotiated()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
if local_sessions.contains(&remote.id) && remote_sessions.contains(&self.id) {
|
||||
log::debug!(target: "test", "Connection between {} and {} established", self.id, remote.id);
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<MockSigner> {
|
||||
/// Create a new node.
|
||||
pub fn init(base: &Path) -> Self {
|
||||
let home = base.join(
|
||||
iter::repeat_with(fastrand::alphanumeric)
|
||||
.take(8)
|
||||
.collect::<String>(),
|
||||
);
|
||||
let home = Home::new(home).init().unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let storage = Storage::open(home.storage()).unwrap();
|
||||
|
||||
Self {
|
||||
home,
|
||||
signer,
|
||||
storage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: cyphernet::EcSign<Pk = NodeId, Sig = Signature> + Signer + Clone> Node<G> {
|
||||
/// Spawn a node in its own thread.
|
||||
pub fn spawn(self, config: service::Config) -> NodeHandle<G> {
|
||||
let listen = vec![([0, 0, 0, 0], 0).into()];
|
||||
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,
|
||||
config,
|
||||
listen,
|
||||
proxy,
|
||||
daemon,
|
||||
self.signer.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let addr = *rt.local_addrs.first().unwrap();
|
||||
let id = *self.signer.public_key();
|
||||
let handle = ManuallyDrop::new(rt.handle.clone());
|
||||
let thread = ManuallyDrop::new(
|
||||
thread::Builder::new()
|
||||
.name(id.to_string())
|
||||
.spawn(move || rt.run())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
NodeHandle {
|
||||
id,
|
||||
storage: self.storage,
|
||||
signer: self.signer,
|
||||
addr,
|
||||
handle,
|
||||
thread,
|
||||
}
|
||||
}
|
||||
|
||||
/// Populate a storage instance with a project.
|
||||
pub fn project(&mut self, name: &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,
|
||||
refname!("master"),
|
||||
&self.signer,
|
||||
&self.storage,
|
||||
)
|
||||
.map(|(id, _, _)| id)
|
||||
.unwrap();
|
||||
|
||||
log::debug!(
|
||||
target: "test",
|
||||
"Initialized project {id} for node {}", self.signer.public_key()
|
||||
);
|
||||
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the nodes have converged in their routing tables.
|
||||
#[track_caller]
|
||||
pub fn converge<'a, G: Signer + cyphernet::EcSign + 'static>(
|
||||
nodes: impl IntoIterator<Item = &'a NodeHandle<G>>,
|
||||
) -> BTreeSet<(Id, NodeId)> {
|
||||
let nodes = nodes.into_iter().collect::<Vec<_>>();
|
||||
|
||||
let mut all_routes = BTreeSet::<(Id, NodeId)>::new();
|
||||
let mut remaining = BTreeMap::from_iter(nodes.iter().map(|node| (node.id, node)));
|
||||
|
||||
// First build the set of all routes.
|
||||
for node in &nodes {
|
||||
let inv = node.storage.inventory().unwrap();
|
||||
|
||||
for rid in inv {
|
||||
all_routes.insert((rid, node.id));
|
||||
}
|
||||
}
|
||||
|
||||
// Then, while there are nodes remaining to converge, check each node to see if
|
||||
// its routing table has all routes. If so, remove it from the remaining nodes.
|
||||
while !remaining.is_empty() {
|
||||
remaining.retain(|_, node| {
|
||||
let routing = node.handle.routing().unwrap();
|
||||
let routes = BTreeSet::from_iter(routing.try_iter());
|
||||
|
||||
if routes == all_routes {
|
||||
log::debug!(target: "test", "Node {} has converged", node.id);
|
||||
return false;
|
||||
} else {
|
||||
log::debug!(target: "test", "Node {} has {:?}", node.id, routes);
|
||||
}
|
||||
true
|
||||
});
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
all_routes
|
||||
}
|
||||
|
|
@ -1,203 +1,12 @@
|
|||
use std::mem::ManuallyDrop;
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
iter, net, thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use radicle::crypto::test::signer::MockSigner;
|
||||
use radicle::crypto::Signer;
|
||||
use radicle::git::refname;
|
||||
use radicle::identity::Id;
|
||||
use radicle::node::{FetchResult, Handle as _};
|
||||
use radicle::profile::Home;
|
||||
use radicle::storage::{ReadRepository, ReadStorage, WriteStorage};
|
||||
use radicle::test::fixtures;
|
||||
use radicle::Storage;
|
||||
use radicle::storage::{ReadRepository, WriteStorage};
|
||||
use radicle::{assert_matches, rad};
|
||||
|
||||
use crate::node::NodeId;
|
||||
use crate::service;
|
||||
use crate::storage::git::transport;
|
||||
use crate::test::environment::{converge, Node};
|
||||
use crate::test::logger;
|
||||
use crate::{runtime, runtime::Handle, service, Runtime};
|
||||
|
||||
/// A node that can be run.
|
||||
struct Node {
|
||||
home: Home,
|
||||
signer: MockSigner,
|
||||
storage: Storage,
|
||||
}
|
||||
|
||||
/// Handle to a running node.
|
||||
struct NodeHandle {
|
||||
id: NodeId,
|
||||
storage: Storage,
|
||||
signer: MockSigner,
|
||||
addr: net::SocketAddr,
|
||||
thread: ManuallyDrop<thread::JoinHandle<Result<(), runtime::Error>>>,
|
||||
handle: ManuallyDrop<Handle<MockSigner>>,
|
||||
}
|
||||
|
||||
impl Drop for NodeHandle {
|
||||
fn drop(&mut self) {
|
||||
log::debug!(target: "test", "Node {} shutting down..", self.id);
|
||||
|
||||
unsafe { ManuallyDrop::take(&mut self.handle) }
|
||||
.shutdown()
|
||||
.unwrap();
|
||||
unsafe { ManuallyDrop::take(&mut self.thread) }
|
||||
.join()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeHandle {
|
||||
/// Connect this node to another node, and wait for the connection to be established both ways.
|
||||
fn connect(&mut self, remote: &NodeHandle) {
|
||||
self.handle.connect(remote.id, remote.addr.into()).unwrap();
|
||||
|
||||
loop {
|
||||
let local_sessions = self.handle.sessions().unwrap();
|
||||
let remote_sessions = remote.handle.sessions().unwrap();
|
||||
|
||||
let local_sessions = local_sessions
|
||||
.negotiated()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<BTreeSet<_>>();
|
||||
let remote_sessions = remote_sessions
|
||||
.negotiated()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
if local_sessions.contains(&remote.id) && remote_sessions.contains(&self.id) {
|
||||
log::debug!(target: "test", "Connection between {} and {} established", self.id, remote.id);
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
/// Create a new node.
|
||||
fn new(base: &Path) -> Self {
|
||||
let home = base.join(
|
||||
iter::repeat_with(fastrand::alphanumeric)
|
||||
.take(8)
|
||||
.collect::<String>(),
|
||||
);
|
||||
let home = Home::new(home).init().unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let storage = Storage::open(home.storage()).unwrap();
|
||||
|
||||
Self {
|
||||
home,
|
||||
signer,
|
||||
storage,
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a node in its own thread.
|
||||
fn spawn(self, config: service::Config) -> NodeHandle {
|
||||
let listen = vec![([0, 0, 0, 0], 0).into()];
|
||||
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,
|
||||
config,
|
||||
listen,
|
||||
proxy,
|
||||
daemon,
|
||||
self.signer.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let addr = *rt.local_addrs.first().unwrap();
|
||||
let id = *self.signer.public_key();
|
||||
let handle = ManuallyDrop::new(rt.handle.clone());
|
||||
let thread = ManuallyDrop::new(
|
||||
thread::Builder::new()
|
||||
.name(id.to_string())
|
||||
.spawn(move || rt.run())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
NodeHandle {
|
||||
id,
|
||||
storage: self.storage,
|
||||
signer: self.signer,
|
||||
addr,
|
||||
handle,
|
||||
thread,
|
||||
}
|
||||
}
|
||||
|
||||
/// Populate a storage instance with a project.
|
||||
fn project(&mut self, name: &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,
|
||||
refname!("master"),
|
||||
&self.signer,
|
||||
&self.storage,
|
||||
)
|
||||
.map(|(id, _, _)| id)
|
||||
.unwrap();
|
||||
|
||||
log::debug!(
|
||||
target: "test",
|
||||
"Initialized project {id} for node {}", self.signer.public_key()
|
||||
);
|
||||
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the nodes have converged in their routing tables.
|
||||
#[track_caller]
|
||||
fn converge<'a>(nodes: impl IntoIterator<Item = &'a NodeHandle>) -> BTreeSet<(Id, NodeId)> {
|
||||
let nodes = nodes.into_iter().collect::<Vec<_>>();
|
||||
|
||||
let mut all_routes = BTreeSet::<(Id, NodeId)>::new();
|
||||
let mut remaining = BTreeMap::from_iter(nodes.iter().map(|node| (node.id, node)));
|
||||
|
||||
// First build the set of all routes.
|
||||
for node in &nodes {
|
||||
let inv = node.storage.inventory().unwrap();
|
||||
|
||||
for rid in inv {
|
||||
all_routes.insert((rid, node.id));
|
||||
}
|
||||
}
|
||||
|
||||
// Then, while there are nodes remaining to converge, check each node to see if
|
||||
// its routing table has all routes. If so, remove it from the remaining nodes.
|
||||
while !remaining.is_empty() {
|
||||
remaining.retain(|_, node| {
|
||||
let routing = node.handle.routing().unwrap();
|
||||
let routes = BTreeSet::from_iter(routing.try_iter());
|
||||
|
||||
if routes == all_routes {
|
||||
log::debug!(target: "test", "Node {} has converged", node.id);
|
||||
return false;
|
||||
} else {
|
||||
log::debug!(target: "test", "Node {} has {:?}", node.id, routes);
|
||||
}
|
||||
true
|
||||
});
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
all_routes
|
||||
}
|
||||
|
||||
#[test]
|
||||
//
|
||||
|
|
@ -208,8 +17,8 @@ fn test_inventory_sync_basic() {
|
|||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
let mut alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let mut alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
|
||||
alice.project("alice");
|
||||
bob.project("bob");
|
||||
|
|
@ -232,9 +41,9 @@ fn test_inventory_sync_bridge() {
|
|||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
let mut alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let mut eve = Node::new(tmp.path());
|
||||
let mut alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
let mut eve = Node::init(tmp.path());
|
||||
|
||||
alice.project("alice");
|
||||
bob.project("bob");
|
||||
|
|
@ -262,10 +71,10 @@ fn test_inventory_sync_ring() {
|
|||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
let mut alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let mut eve = Node::new(tmp.path());
|
||||
let mut carol = Node::new(tmp.path());
|
||||
let mut alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
let mut eve = Node::init(tmp.path());
|
||||
let mut carol = Node::init(tmp.path());
|
||||
|
||||
alice.project("alice");
|
||||
bob.project("bob");
|
||||
|
|
@ -299,11 +108,11 @@ fn test_inventory_sync_star() {
|
|||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
let mut alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let mut eve = Node::new(tmp.path());
|
||||
let mut carol = Node::new(tmp.path());
|
||||
let mut dave = Node::new(tmp.path());
|
||||
let mut alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
let mut eve = Node::init(tmp.path());
|
||||
let mut carol = Node::init(tmp.path());
|
||||
let mut dave = Node::init(tmp.path());
|
||||
|
||||
alice.project("alice");
|
||||
bob.project("bob");
|
||||
|
|
@ -331,8 +140,8 @@ fn test_replication() {
|
|||
logger::init(log::Level::Debug);
|
||||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
let acme = bob.project("acme");
|
||||
|
||||
let mut alice = alice.spawn(service::Config::default());
|
||||
|
|
@ -388,8 +197,8 @@ fn test_clone() {
|
|||
logger::init(log::Level::Debug);
|
||||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
let acme = bob.project("acme");
|
||||
|
||||
let mut alice = alice.spawn(service::Config::default());
|
||||
|
|
@ -438,8 +247,8 @@ fn test_fetch_up_to_date() {
|
|||
logger::init(log::Level::Debug);
|
||||
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let alice = Node::new(tmp.path());
|
||||
let mut bob = Node::new(tmp.path());
|
||||
let alice = Node::init(tmp.path());
|
||||
let mut bob = Node::init(tmp.path());
|
||||
let acme = bob.project("acme");
|
||||
|
||||
let mut alice = alice.spawn(service::Config::default());
|
||||
|
|
|
|||
Loading…
Reference in New Issue