node: Add clone test with seed acting as relay

This commit is contained in:
Alexis Sellier 2023-02-13 10:30:04 +01:00
parent 3e7761740f
commit 3ae826dee2
No known key found for this signature in database
3 changed files with 96 additions and 7 deletions

View File

@ -1,11 +1,15 @@
use std::env;
use std::path::Path;
use std::str::FromStr;
use std::{thread, time};
use radicle::git;
use radicle::prelude::Id;
use radicle::profile::Home;
use radicle::storage::{ReadRepository, WriteStorage};
use radicle::test::fixtures;
use radicle_node::service::tracking::Policy;
use radicle_node::test::{
environment::{Config, Environment},
logger,
@ -267,3 +271,76 @@ fn rad_init_sync_and_clone() {
)
.unwrap();
}
#[test]
//
// alice -- seed -- bob
//
fn test_replication_via_seed() {
logger::init(log::Level::Debug);
let mut environment = Environment::new();
let alice = environment.node("alice");
let bob = environment.node("bob");
let seed = environment.node("seed");
let working = environment.tmp().join("working");
let rid = Id::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
let mut alice = alice.spawn(Config::default());
let mut bob = bob.spawn(Config::default());
let seed = seed.spawn(Config {
policy: Policy::Track,
..Config::default()
});
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_secs(1));
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",
],
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)]);
bob.rad("clone", &[rid.to_string().as_str()], 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)]);
// Enough time for the Seed to be able to fetch Bob's fork.
thread::sleep(time::Duration::from_secs(1));
seed.storage
.repository(rid)
.unwrap()
.remote(&bob.id)
.unwrap();
// TODO: Seed should send Bob's ref announcement to Alice, after the fetch.
// Currently, it is relayed when received from Bob, before the fetch completes,
// which means that Alice fetches too early from Seed, and nothing is fetched.
}

View File

@ -754,7 +754,7 @@ where
// Discard inventory messages we've already seen, otherwise update
// out last seen time.
if !peer.inventory_announced(timestamp) {
debug!(target: "service", "Ignoring stale inventory announcement from {announcer}");
debug!(target: "service", "Ignoring stale inventory announcement from {announcer} (t={})", self.clock.as_secs());
return Ok(false);
}

View File

@ -162,12 +162,21 @@ impl<G: Signer + cyphernet::EcSign> NodeHandle<G> {
}
/// Wait until this node's routing table contains the given routes.
#[track_caller]
pub fn routes_to(&self, routes: &[(Id, NodeId)]) {
let mut remaining: BTreeSet<_> = routes.iter().collect();
loop {
let mut remaining: BTreeSet<_> = routes.iter().collect();
while !remaining.is_empty() {
for (rid, nid) in self.handle.routing().unwrap() {
remaining.remove(&(rid, nid));
if !remaining.remove(&(rid, nid)) {
panic!(
"Node::routes_to: unexpected route for {}: ({rid}, {nid})",
self.id
);
}
}
if remaining.is_empty() {
break;
}
thread::sleep(Duration::from_millis(100));
}
@ -309,9 +318,12 @@ pub fn converge<'a, G: Signer + cyphernet::EcSign + 'static>(
// First build the set of all routes.
for node in &nodes {
let inv = node.storage.inventory().unwrap();
for rid in inv {
// Routes from the routing table.
for (rid, seed_id) in node.handle.routing().unwrap() {
all_routes.insert((rid, seed_id));
}
// Routes from the local inventory.
for rid in node.storage.inventory().unwrap() {
all_routes.insert((rid, node.id));
}
}