Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration tests for unstable-reconnecting-rpc-client #1711

Merged
merged 8 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,12 @@ jobs:
command: test
args: --release --package integration-tests --features unstable-light-client

- name: Run tests
uses: actions-rs/cargo@v1.0.3
with:
command: test
args: --release --package integration-tests --no-default-features --features unstable-reconnecting-rpc-client

- if: "failure()"
uses: "andymckay/cancel-action@a955d435292c0d409d104b57d8e78435a93a6ef1" # v0.5

Expand Down
8 changes: 6 additions & 2 deletions testing/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ homepage.workspace = true
description = "Subxt integration tests that rely on the Substrate binary"

[features]
default = []
default = [ "subxt/jsonrpsee" ]

# Enable to run the tests with Light Client support.
unstable-light-client = ["subxt/unstable-light-client"]
Expand All @@ -25,6 +25,10 @@ unstable-light-client-long-running = ["subxt/unstable-light-client"]
# the default one which relies on the "old" RPC methods.
unstable-backend-client = []

# Enable the unstable reconnecting rpc-client
unstable-reconnecting-rpc-client = ["subxt/unstable-reconnecting-rpc-client"]


[dev-dependencies]
assert_matches = { workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
Expand All @@ -36,7 +40,7 @@ serde = { workspace = true }
scale-info = { workspace = true, features = ["bit-vec"] }
sp-core = { workspace = true }
syn = { workspace = true }
subxt = { workspace = true, features = ["unstable-metadata", "native", "jsonrpsee", "substrate-compat"] }
subxt = { workspace = true, features = ["unstable-metadata", "native", "substrate-compat"] }
subxt-signer = { workspace = true, features = ["default"] }
subxt-codegen = { workspace = true }
subxt-metadata = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions testing/integration-tests/src/full_client/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ async fn transaction_validation() {

wait_for_blocks(&api).await;

#[cfg(feature = "unstable-reconnecting-rpc-client")]
let _ctx = ctx.restart().await;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather create a separate test for unstable-reconnecting stuff than injecting it here.

Also this would remove all feature-gated stuff in this PR and just feature-gate the test itself

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed all the feature gating stuff and moved this to a separate test


let tx = node_runtime::tx()
.balances()
.transfer_allow_death(bob.public_key().into(), 10_000);
Expand Down
5 changes: 5 additions & 0 deletions testing/integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ compile_error!(
"The features 'unstable-light-client' and 'unstable-backend-client' cannot be used together"
);

#[cfg(all(feature = "unstable-reconnecting-rpc-client", feature = "default"))]
compile_error!(
"The features 'unstable-reconnecting-rpc-client' and 'default' cannot be used together"
);

#[cfg(test)]
pub mod utils;

Expand Down
38 changes: 37 additions & 1 deletion testing/integration-tests/src/utils/node_proc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ where
TestNodeProcessBuilder::new(paths)
}

pub async fn restart(mut self) -> Self {
tokio::task::spawn_blocking(move || {
if let Some(ref mut proc) = &mut self.proc {
let _ = proc.restart().unwrap();
}
self
})
.await
.expect("to succeed")
}

/// Hand back an RPC client connected to the test node which exposes the legacy RPC methods.
pub async fn legacy_rpc_methods(&self) -> legacy::LegacyRpcMethods<R> {
let rpc_client = self.rpc_client().await;
Expand All @@ -70,14 +81,28 @@ where
unstable::UnstableRpcMethods::new(rpc_client)
}

#[cfg(feature = "default")]
/// Hand back an RPC client connected to the test node.
pub async fn rpc_client(&self) -> rpc::RpcClient {
let url = get_url(self.proc.as_ref().map(|p| p.ws_port()));
let url = self.proc.as_ref().map(|p| p.ws_port());
let url = get_url(url);
rpc::RpcClient::from_url(url)
.await
.expect("Unable to connect RPC client to test node")
}

#[cfg(feature = "unstable-reconnecting-rpc-client")]
/// Hand back an RPC client connected to the test node.
pub async fn rpc_client(&self) -> rpc::RpcClient {
let url = self.proc.as_ref().map(|p| p.ws_port());
let url = get_url(url);
let client = subxt::backend::rpc::reconnecting_rpc_client::Client::builder()
.build(url)
.await
.expect("unable to connect RPC client to test node");
rpc::RpcClient::new(client)
}

/// Always return a client using the unstable backend.
/// Only use for comparing backends; use [`TestNodeProcess::client()`] normally,
/// which enables us to run each test against both backends.
Expand Down Expand Up @@ -198,6 +223,7 @@ impl TestNodeProcessBuilder {
}
}

#[cfg(feature = "default")]
async fn build_rpc_client(ws_url: &str) -> Result<rpc::RpcClient, String> {
let rpc_client = rpc::RpcClient::from_insecure_url(ws_url)
.await
Expand All @@ -206,6 +232,16 @@ async fn build_rpc_client(ws_url: &str) -> Result<rpc::RpcClient, String> {
Ok(rpc_client)
}

#[cfg(feature = "unstable-reconnecting-rpc-client")]
async fn build_rpc_client(ws_url: &str) -> Result<rpc::RpcClient, String> {
let client = subxt::backend::rpc::reconnecting_rpc_client::Client::builder()
.build(ws_url.to_string())
.await
.map_err(|e| format!("Cannot construct RPC client: {e}"))?;

Ok(rpc::RpcClient::new(client))
}

async fn build_legacy_client<T: Config>(
rpc_client: rpc::RpcClient,
) -> Result<OnlineClient<T>, String> {
Expand Down
69 changes: 68 additions & 1 deletion testing/substrate-runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,27 @@ impl SubstrateNodeBuilder {
}

/// Spawn the node, handing back an object which, when dropped, will stop it.
pub fn spawn(self) -> Result<SubstrateNode, Error> {
pub fn spawn(mut self) -> Result<SubstrateNode, Error> {
// Try to spawn the binary at each path, returning the
// first "ok" or last error that we encountered.
let mut res = Err(io::Error::new(
io::ErrorKind::Other,
"No binary path provided",
));

let path = Command::new("mktemp")
.arg("-d")
.output()
.expect("failed to create base dir");
let path = String::from_utf8(path.stdout).expect("bad path");
let mut bin_path = OsString::new();
for binary_path in &self.binary_paths {
self.custom_flags
.insert("base-path".into(), Some(path.clone().into()));

res = SubstrateNodeBuilder::try_spawn(binary_path, &self.custom_flags);
if res.is_ok() {
bin_path = binary_path.clone();
break;
}
}
Expand All @@ -98,10 +109,13 @@ impl SubstrateNodeBuilder {
let p2p_port = p2p_port.ok_or(Error::CouldNotExtractP2pPort)?;

Ok(SubstrateNode {
binary_path: bin_path,
custom_flags: self.custom_flags,
proc,
ws_port,
p2p_address,
p2p_port,
base_path: path,
})
}

Expand Down Expand Up @@ -131,10 +145,13 @@ impl SubstrateNodeBuilder {
}

pub struct SubstrateNode {
binary_path: OsString,
custom_flags: HashMap<CowStr, Option<CowStr>>,
proc: process::Child,
ws_port: u16,
p2p_address: String,
p2p_port: u32,
base_path: String,
}

impl SubstrateNode {
Expand Down Expand Up @@ -167,11 +184,61 @@ impl SubstrateNode {
pub fn kill(&mut self) -> std::io::Result<()> {
self.proc.kill()
}

/// restart the node, handing back an object which, when dropped, will stop it.
pub fn restart(&mut self) -> Result<(), std::io::Error> {
let res = self.kill();

match res {
Ok(_) => (),
Err(e) => {
self.cleanup();
return Err(e);
}
}

let proc = self.try_spawn()?;

self.proc = proc;
// Wait for RPC port to be logged (it's logged to stderr).

Ok(())
}

// Attempt to spawn a binary with the path/flags given.
fn try_spawn(&mut self) -> Result<Child, std::io::Error> {
let mut cmd = Command::new(&self.binary_path);

cmd.env("RUST_LOG", "info,libp2p_tcp=debug")
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.arg("--dev");

for (key, val) in &self.custom_flags {
let arg = match val {
Some(val) => format!("--{key}={val}"),
None => format!("--{key}"),
};
cmd.arg(arg);
}

cmd.arg(format!("--rpc-port={}", self.ws_port));
cmd.arg(format!("--port={}", self.p2p_port));
cmd.spawn()
}

fn cleanup(&self) {
let _ = Command::new("rm")
.args(["-rf", &self.base_path])
.output()
.expect("success");
}
}

impl Drop for SubstrateNode {
fn drop(&mut self) {
let _ = self.kill();
self.cleanup()
}
}

Expand Down
Loading