Speed up node E2E tests

By allowing the node to run using a `MockSigner`, we speed up tests
significantly, since PBKDF2 doesn't need to run.

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2023-01-16 17:58:06 +01:00
parent 72cb2fd713
commit d31968509d
No known key found for this signature in database
15 changed files with 100 additions and 57 deletions

View File

@ -80,7 +80,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
pub fn clone(id: Id, _interactive: Interactive, ctx: impl term::Context) -> anyhow::Result<()> { pub fn clone(id: Id, _interactive: Interactive, ctx: impl term::Context) -> anyhow::Result<()> {
let profile = ctx.profile()?; let profile = ctx.profile()?;
let mut node = radicle::node::connect(profile.node())?; let mut node = radicle::node::connect(profile.socket())?;
let signer = term::signer(&profile)?; let signer = term::signer(&profile)?;
// Track & fetch project. // Track & fetch project.

View File

@ -94,7 +94,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let storage = &profile.storage; let storage = &profile.storage;
let (_, rid) = radicle::rad::cwd().context("this command must be run within a project")?; let (_, rid) = radicle::rad::cwd().context("this command must be run within a project")?;
let project = storage.repository(rid)?.project_of(profile.id())?; let project = storage.repository(rid)?.project_of(profile.id())?;
let mut node = radicle::node::connect(profile.node())?; let mut node = radicle::node::connect(profile.socket())?;
term::info!( term::info!(
"Establishing 🌱 tracking relationship for {}", "Establishing 🌱 tracking relationship for {}",

View File

@ -88,6 +88,6 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
} }
pub fn untrack(id: Id, profile: &Profile) -> anyhow::Result<bool> { pub fn untrack(id: Id, profile: &Profile) -> anyhow::Result<bool> {
let mut node = radicle::node::connect(profile.node())?; let mut node = radicle::node::connect(profile.socket())?;
node.untrack_repo(id).map_err(|e| anyhow!(e)) node.untrack_repo(id).map_err(|e| anyhow!(e))
} }

View File

@ -16,7 +16,7 @@ fn test(
let base = Path::new(env!("CARGO_MANIFEST_DIR")); let base = Path::new(env!("CARGO_MANIFEST_DIR"));
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let home = if let Some(profile) = profile { let home = if let Some(profile) = profile {
profile.home.as_path().to_path_buf() profile.home().to_path_buf()
} else { } else {
tmp.path().to_path_buf() tmp.path().to_path_buf()
}; };

View File

@ -71,3 +71,28 @@ impl Signer for MockSigner {
Ok(self.sign(msg)) Ok(self.sign(msg))
} }
} }
#[cfg(feature = "cyphernet")]
impl cyphernet::EcSk for MockSigner {
type Pk = PublicKey;
fn generate_keypair() -> (Self, Self::Pk)
where
Self: Sized,
{
unimplemented! {}
}
fn to_pk(&self) -> Result<Self::Pk, cyphernet::EcSkInvalid> {
Ok(*self.public_key())
}
}
#[cfg(feature = "cyphernet")]
impl cyphernet::EcSign for MockSigner {
type Sig = Signature;
fn sign(&self, msg: impl AsRef<[u8]>) -> Self::Sig {
Signer::sign(self, msg.as_ref())
}
}

View File

@ -51,7 +51,7 @@ pub async fn run(options: Options) -> anyhow::Result<()> {
tracing::info!("{}", str::from_utf8(&git_version)?.trim()); tracing::info!("{}", str::from_utf8(&git_version)?.trim());
let profile = Arc::new(radicle::Profile::load()?); let profile = Arc::new(radicle::Profile::load()?);
tracing::info!("using radicle home at {}", profile.home.display()); tracing::info!("using radicle home at {}", profile.home().display());
let git_router = Router::new() let git_router = Router::new()
.route("/:project/*request", any(git_handler)) .route("/:project/*request", any(git_handler))

View File

@ -1,8 +1,9 @@
use std::io; use std::{io, net, thread, time};
use std::{net, thread, time};
use cyphernet::{Cert, EcSign}; use cyphernet::{Cert, EcSign};
use netservices::resource::NetAccept; use netservices::resource::NetAccept;
use radicle::profile::Paths;
use radicle::Storage;
use reactor::poller::popol; use reactor::poller::popol;
use reactor::Reactor; use reactor::Reactor;
use thiserror::Error; use thiserror::Error;
@ -14,7 +15,7 @@ use crate::node::NodeId;
use crate::service::{routing, tracking}; use crate::service::{routing, tracking};
use crate::wire::Wire; use crate::wire::Wire;
use crate::worker::{WorkerPool, WorkerReq}; use crate::worker::{WorkerPool, WorkerReq};
use crate::{crypto, profile, service, LocalTime}; use crate::{crypto, service, LocalTime};
pub mod handle; pub mod handle;
use handle::Handle; use handle::Handle;
@ -66,19 +67,19 @@ impl<G: crypto::Signer + EcSign<Pk = NodeId, Sig = Signature> + Clone + 'static>
/// ///
/// This function spawns threads. /// This function spawns threads.
pub fn with( pub fn with(
profile: profile::Profile, paths: Paths,
config: service::Config, config: service::Config,
listen: Vec<net::SocketAddr>, listen: Vec<net::SocketAddr>,
proxy: net::SocketAddr, proxy: net::SocketAddr,
signer: G, signer: G,
) -> Result<Runtime<G>, Error> { ) -> Result<Runtime<G>, Error> {
let id = *profile.id(); let id = *signer.public_key();
let node = profile.node(); let node_sock = paths.socket();
let node_dir = paths.node();
let network = config.network; let network = config.network;
let rng = fastrand::Rng::new(); let rng = fastrand::Rng::new();
let clock = LocalTime::now(); let clock = LocalTime::now();
let storage = profile.storage; let storage = Storage::open(paths.storage())?;
let node_dir = profile.home.join(NODE_DIR);
let address_db = node_dir.join(ADDRESS_DB_FILE); let address_db = node_dir.join(ADDRESS_DB_FILE);
let routing_db = node_dir.join(ROUTING_DB_FILE); let routing_db = node_dir.join(ROUTING_DB_FILE);
let tracking_db = node_dir.join(TRACKING_DB_FILE); let tracking_db = node_dir.join(TRACKING_DB_FILE);
@ -122,7 +123,7 @@ impl<G: crypto::Signer + EcSign<Pk = NodeId, Sig = Signature> + Clone + 'static>
let handle = Handle::from(reactor.controller()); let handle = Handle::from(reactor.controller());
let control = thread::spawn({ let control = thread::spawn({
let handle = handle.clone(); let handle = handle.clone();
move || control::listen(node, handle) move || control::listen(node_sock, handle)
}); });
let controller = reactor.controller(); let controller = reactor.controller();
let mut local_addrs = Vec::new(); let mut local_addrs = Vec::new();

View File

@ -6,8 +6,9 @@ use nakamoto_net::LocalDuration;
use radicle::profile; use radicle::profile;
use radicle_node::client::Runtime; use radicle_node::client::Runtime;
use radicle_node::crypto::ssh::keystore::MemorySigner; use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
use radicle_node::prelude::{Address, NodeId}; use radicle_node::prelude::{Address, NodeId};
use radicle_node::profile::Paths;
use radicle_node::{logger, service}; use radicle_node::{logger, service};
#[derive(Debug)] #[derive(Debug)]
@ -77,11 +78,13 @@ fn main() -> anyhow::Result<()> {
logger::init(log::Level::Debug)?; logger::init(log::Level::Debug)?;
let options = Options::from_env()?; let options = Options::from_env()?;
let profile = radicle::Profile::load().context("Failed to load node profile")?; let home = profile::home()?;
let paths = Paths::new(home);
let passphrase = env::var(profile::env::RAD_PASSPHRASE) let passphrase = env::var(profile::env::RAD_PASSPHRASE)
.context("`RAD_PASSPHRASE` is required to be set for the node to establish connections")? .context("`RAD_PASSPHRASE` is required to be set for the node to establish connections")?
.into(); .into();
let signer = MemorySigner::load(&profile.keystore, passphrase)?; let keystore = Keystore::new(&paths.keys());
let signer = MemorySigner::load(&keystore, passphrase)?;
let config = service::Config { let config = service::Config {
connect: options.connect.into_iter().collect(), connect: options.connect.into_iter().collect(),
external_addresses: options.external_addresses, external_addresses: options.external_addresses,
@ -89,7 +92,7 @@ fn main() -> anyhow::Result<()> {
..service::Config::default() ..service::Config::default()
}; };
let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050); let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050);
let runtime = Runtime::with(profile, config, options.listen, proxy, signer)?; let runtime = Runtime::with(paths, config, options.listen, proxy, signer)?;
runtime.run()?; runtime.run()?;

View File

@ -5,13 +5,13 @@ use std::{
time::Duration, time::Duration,
}; };
use radicle::crypto::ssh::keystore::MemorySigner; use radicle::crypto::test::signer::MockSigner;
use radicle::git::refname; use radicle::git::refname;
use radicle::identity::Id; use radicle::identity::Id;
use radicle::node::Handle; use radicle::node::Handle;
use radicle::profile::Paths;
use radicle::storage::WriteStorage; use radicle::storage::WriteStorage;
use radicle::test::fixtures; use radicle::test::fixtures;
use radicle::Profile;
use radicle::Storage; use radicle::Storage;
use radicle::{assert_matches, rad}; use radicle::{assert_matches, rad};
@ -27,8 +27,8 @@ use crate::{client, client::Runtime, service};
struct Node { struct Node {
id: NodeId, id: NodeId,
addr: net::SocketAddr, addr: net::SocketAddr,
handle: client::handle::Handle<Wire<routing::Table, address::Book, Storage, MemorySigner>>, handle: client::handle::Handle<Wire<routing::Table, address::Book, Storage, MockSigner>>,
signer: MemorySigner, signer: MockSigner,
storage: Storage, storage: Storage,
#[allow(dead_code)] #[allow(dead_code)]
thread: thread::JoinHandle<Result<(), client::Error>>, thread: thread::JoinHandle<Result<(), client::Error>>,
@ -42,15 +42,12 @@ impl Node {
.take(8) .take(8)
.collect::<String>(), .collect::<String>(),
); );
let paths = Paths::init(home).unwrap();
let profile = Profile::init(home.as_path(), "pasphrase".to_owned()).unwrap(); let signer = MockSigner::default();
let signer = MemorySigner::load(&profile.keystore, "pasphrase".to_owned().into()).unwrap();
let listen = vec![([0, 0, 0, 0], 0).into()]; let listen = vec![([0, 0, 0, 0], 0).into()];
let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050); let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050);
let storage = profile.storage.clone(); let storage = Storage::open(paths.storage()).unwrap();
let rt = Runtime::with(paths, config, listen, proxy, signer.clone()).unwrap();
let rt = Runtime::with(profile, config, listen, proxy, signer.clone()).unwrap();
let addr = *rt.local_addrs.first().unwrap(); let addr = *rt.local_addrs.first().unwrap();
let id = rt.id; let id = rt.id;
let handle = rt.handle.clone(); let handle = rt.handle.clone();

View File

@ -108,7 +108,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Box<dyn std::error::Error +
// Connect to local node and announce refs to the network. // Connect to local node and announce refs to the network.
// If our node is not running, we simply skip this step, as the // If our node is not running, we simply skip this step, as the
// refs will be announced eventually, when the node restarts. // refs will be announced eventually, when the node restarts.
if let Ok(mut conn) = radicle::node::connect(profile.node()) { if let Ok(mut conn) = radicle::node::connect(profile.socket()) {
conn.announce_refs(url.repo)?; conn.announce_refs(url.repo)?;
} }
} }

View File

@ -9,7 +9,7 @@ fn main() -> anyhow::Result<()> {
}; };
println!("id: {}", profile.id()); println!("id: {}", profile.id());
println!("home: {}", profile.home.display()); println!("home: {}", profile.home().display());
Ok(()) Ok(())
} }

View File

@ -11,7 +11,7 @@ fn main() -> anyhow::Result<()> {
if let Some(id) = env::args().nth(1) { if let Some(id) = env::args().nth(1) {
let id = Id::from_str(&id)?; let id = Id::from_str(&id)?;
let mut node = radicle::node::connect(profile.node())?; let mut node = radicle::node::connect(profile.socket())?;
let repo = radicle::rad::clone(id, &cwd, &signer, &profile.storage, &mut node)?; let repo = radicle::rad::clone(id, &cwd, &signer, &profile.storage, &mut node)?;
println!( println!(

View File

@ -16,7 +16,7 @@ fn main() -> anyhow::Result<()> {
let sigrefs = project.sign_refs(&signer)?; let sigrefs = project.sign_refs(&signer)?;
let head = project.set_head()?; let head = project.set_head()?;
radicle::node::connect(profile.node())?.announce_refs(id)?; radicle::node::connect(profile.socket())?.announce_refs(id)?;
println!("head: {}", head); println!("head: {}", head);
println!("ok: {}", sigrefs.signature); println!("ok: {}", sigrefs.signature);

View File

@ -7,7 +7,7 @@ fn main() -> anyhow::Result<()> {
"fingerprint: {}", "fingerprint: {}",
radicle::crypto::ssh::fmt::fingerprint(profile.id()) radicle::crypto::ssh::fmt::fingerprint(profile.id())
); );
println!("home: {}", profile.home.display()); println!("home: {}", profile.home().display());
Ok(()) Ok(())
} }

View File

@ -59,7 +59,7 @@ pub enum Error {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Profile { pub struct Profile {
pub home: PathBuf, pub paths: Paths,
pub storage: Storage, pub storage: Storage,
pub keystore: Keystore, pub keystore: Keystore,
pub public_key: PublicKey, pub public_key: PublicKey,
@ -68,19 +68,15 @@ pub struct Profile {
impl Profile { impl Profile {
pub fn init(home: impl AsRef<Path>, passphrase: impl Into<Passphrase>) -> Result<Self, Error> { pub fn init(home: impl AsRef<Path>, passphrase: impl Into<Passphrase>) -> Result<Self, Error> {
let home = home.as_ref().to_path_buf(); let home = home.as_ref().to_path_buf();
let paths = Paths { let paths = Paths::init(home.as_path())?;
home: home.as_path(),
};
let storage = Storage::open(paths.storage())?; let storage = Storage::open(paths.storage())?;
let keystore = Keystore::new(&paths.keys()); let keystore = Keystore::new(&paths.keys());
let public_key = keystore.init("radicle", passphrase)?; let public_key = keystore.init("radicle", passphrase)?;
fs::create_dir_all(paths.node()).ok();
transport::local::register(storage.clone()); transport::local::register(storage.clone());
Ok(Profile { Ok(Profile {
home, paths,
storage, storage,
keystore, keystore,
public_key, public_key,
@ -89,16 +85,17 @@ impl Profile {
pub fn load() -> Result<Self, Error> { pub fn load() -> Result<Self, Error> {
let home = self::home()?; let home = self::home()?;
let storage = Storage::open(home.join("storage"))?; let paths = Paths::new(home);
let keystore = Keystore::new(&home.join("keys")); let storage = Storage::open(paths.storage())?;
let keystore = Keystore::new(&paths.keys());
let public_key = keystore let public_key = keystore
.public_key()? .public_key()?
.ok_or_else(|| Error::NotFound(home.clone()))?; .ok_or_else(|| Error::NotFound(paths.home.clone()))?;
transport::local::register(storage.clone()); transport::local::register(storage.clone());
Ok(Profile { Ok(Profile {
home, paths,
storage, storage,
keystore, keystore,
public_key, public_key,
@ -130,19 +127,22 @@ impl Profile {
/// Return the path to the keys folder. /// Return the path to the keys folder.
pub fn keys(&self) -> PathBuf { pub fn keys(&self) -> PathBuf {
self.home.join("keys") self.paths.keys()
}
/// Get the profile home directory.
pub fn home(&self) -> &Path {
self.paths.home()
} }
/// Get the path to the radicle node socket. /// Get the path to the radicle node socket.
pub fn node(&self) -> PathBuf { pub fn socket(&self) -> PathBuf {
env::var_os(env::RAD_SOCKET) self.paths.socket()
.map(PathBuf::from)
.unwrap_or_else(|| self.home.join("node").join(node::DEFAULT_SOCKET_NAME))
} }
/// Get `Paths` of profile /// Get `Paths` of profile
pub fn paths(&self) -> Paths { pub fn paths(&self) -> &Paths {
Paths { home: &self.home } &self.paths
} }
} }
@ -161,13 +161,24 @@ pub fn home() -> Result<PathBuf, io::Error> {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Paths<'a> { pub struct Paths {
home: &'a Path, home: PathBuf,
} }
impl<'a> Paths<'a> { impl Paths {
pub fn new(home: &'a Path) -> Self { pub fn init(home: impl Into<PathBuf>) -> Result<Self, io::Error> {
Self { home } let paths = Self::new(home);
fs::create_dir_all(paths.node()).ok();
Ok(paths)
}
pub fn new(home: impl Into<PathBuf>) -> Self {
Self { home: home.into() }
}
pub fn home(&self) -> &Path {
self.home.as_path()
} }
pub fn storage(&self) -> PathBuf { pub fn storage(&self) -> PathBuf {
@ -181,4 +192,10 @@ impl<'a> Paths<'a> {
pub fn node(&self) -> PathBuf { pub fn node(&self) -> PathBuf {
self.home.join("node") self.home.join("node")
} }
pub fn socket(&self) -> PathBuf {
env::var_os(env::RAD_SOCKET)
.map(PathBuf::from)
.unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME))
}
} }