Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

fix: read WS address from substrate output #11379

Merged
merged 1 commit into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions bin/node/cli/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ use nix::{
use node_primitives::Block;
use remote_externalities::rpc_api;
use std::{
io::{BufRead, BufReader, Read},
ops::{Deref, DerefMut},
path::Path,
process::{Child, Command, ExitStatus},
process::{self, Child, Command, ExitStatus},
time::Duration,
};
use tokio::time::timeout;

static LOCALHOST_WS: &str = "ws://127.0.0.1:9944/";

/// Wait for the given `child` the given number of `secs`.
///
/// Returns the `Some(exit status)` or `None` if the process did not finish in the given time.
Expand Down Expand Up @@ -63,8 +62,9 @@ pub fn wait_for(child: &mut Child, secs: u64) -> Result<ExitStatus, ()> {
pub async fn wait_n_finalized_blocks(
n: usize,
timeout_secs: u64,
url: &str,
) -> Result<(), tokio::time::error::Elapsed> {
timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, LOCALHOST_WS)).await
timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, url)).await
}

/// Wait for at least n blocks to be finalized from a specified node
Expand All @@ -85,12 +85,23 @@ pub async fn wait_n_finalized_blocks_from(n: usize, url: &str) {

/// Run the node for a while (3 blocks)
pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) {
let mut cmd = Command::new(cargo_bin("substrate"));
let mut cmd = Command::new(cargo_bin("substrate"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(args)
.arg("-d")
.arg(base_path)
.spawn()
.unwrap();

let mut child = KillChildOnDrop(cmd.args(args).arg("-d").arg(base_path).spawn().unwrap());
let stderr = cmd.stderr.take().unwrap();

let mut child = KillChildOnDrop(cmd);

let (ws_url, _) = find_ws_url_from_output(stderr);

// Let it produce some blocks.
let _ = wait_n_finalized_blocks(3, 30).await;
let _ = wait_n_finalized_blocks(3, 30, &ws_url).await;

assert!(child.try_wait().unwrap().is_none(), "the process should still be running");

Expand Down Expand Up @@ -134,3 +145,30 @@ impl DerefMut for KillChildOnDrop {
&mut self.0
}
}

/// Read the WS address from the output.
///
/// This is hack to get the actual binded sockaddr because
/// substrate assigns a random port if the specified port was already binded.
pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) {
let mut data = String::new();

let ws_url = BufReader::new(read)
.lines()
.find_map(|line| {
let line =
line.expect("failed to obtain next line from stdout for WS address discovery");
data.push_str(&line);

// does the line contain our port (we expect this specific output from substrate).
let sock_addr = match line.split_once("Running JSON-RPC WS server: addr=") {
None => return None,
Some((_, after)) => after.split_once(",").unwrap().0,
};

Some(format!("ws://{}", sock_addr))
})
.expect("We should get a WebSocket address");

(ws_url, data)
}
17 changes: 14 additions & 3 deletions bin/node/cli/tests/running_the_node_and_interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use nix::{
},
unistd::Pid,
};
use std::process::{Child, Command};
use std::process::{self, Child, Command};
use tempfile::tempdir;

pub mod common;
Expand All @@ -36,14 +36,20 @@ async fn running_the_node_works_and_can_be_interrupted() {
let base_path = tempdir().expect("could not create a temp dir");
let mut cmd = common::KillChildOnDrop(
Command::new(cargo_bin("substrate"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(&["--dev", "-d"])
.arg(base_path.path())
.arg("--no-hardware-benchmarks")
.spawn()
.unwrap(),
);

common::wait_n_finalized_blocks(3, 60)
let stderr = cmd.stderr.take().unwrap();

let (ws_url, _) = common::find_ws_url_from_output(stderr);

common::wait_n_finalized_blocks(3, 30, &ws_url)
.await
.expect("Blocks are produced in time");
assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running");
Expand All @@ -64,6 +70,8 @@ async fn running_the_node_works_and_can_be_interrupted() {
async fn running_two_nodes_with_the_same_ws_port_should_work() {
fn start_node() -> Child {
Command::new(cargo_bin("substrate"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"])
.spawn()
.unwrap()
Expand All @@ -72,7 +80,10 @@ async fn running_two_nodes_with_the_same_ws_port_should_work() {
let mut first_node = common::KillChildOnDrop(start_node());
let mut second_node = common::KillChildOnDrop(start_node());

let _ = common::wait_n_finalized_blocks(3, 30).await;
let stderr = first_node.stderr.take().unwrap();
let (ws_url, _) = common::find_ws_url_from_output(stderr);

common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap();

assert!(first_node.try_wait().unwrap().is_none(), "The first node should still be running");
assert!(second_node.try_wait().unwrap().is_none(), "The second node should still be running");
Expand Down
10 changes: 6 additions & 4 deletions bin/node/cli/tests/temp_base_path_works.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,21 @@ async fn temp_base_path_works() {
.unwrap(),
);

let mut stderr = child.stderr.take().unwrap();
let (ws_url, mut data) = common::find_ws_url_from_output(&mut stderr);

// Let it produce some blocks.
common::wait_n_finalized_blocks(3, 30).await.unwrap();
common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap();
assert!(child.try_wait().unwrap().is_none(), "the process should still be running");

// Stop the process
kill(Pid::from_raw(child.id().try_into().unwrap()), SIGINT).unwrap();
assert!(common::wait_for(&mut child, 40).map(|x| x.success()).unwrap_or_default());

// Ensure the database has been deleted
let mut stderr = String::new();
child.stderr.as_mut().unwrap().read_to_string(&mut stderr).unwrap();
stderr.read_to_string(&mut data).unwrap();
let re = Regex::new(r"Database: .+ at (\S+)").unwrap();
let db_path = PathBuf::from(re.captures(stderr.as_str()).unwrap().get(1).unwrap().as_str());
let db_path = PathBuf::from(re.captures(data.as_str()).unwrap().get(1).unwrap().as_str());

assert!(!db_path.exists());
}