use color_eyre::eyre::{Result, WrapErr};
use zebra_chain::parameters::Network::*;
use zebra_rpc::server::OPENED_RPC_ENDPOINT_MSG;
use zebra_test::{args, prelude::*};
use crate::common::{
config::{
os_assigned_rpc_port_config, random_known_rpc_port_config, read_listen_addr_from_logs,
testdir,
},
launch::{ZebradTestDirExt, LAUNCH_DELAY},
};
#[cfg(any(feature = "prometheus", feature = "filter-reload"))]
use crate::common::config::default_test_config;
#[cfg(any(feature = "prometheus", feature = "filter-reload"))]
use zebra_test::net::random_known_port;
#[tokio::test]
#[cfg(feature = "prometheus")]
async fn metrics_endpoint() -> Result<()> {
use bytes::Bytes;
use http_body_util::BodyExt;
use http_body_util::Full;
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
use std::io::Write;
let _init_guard = zebra_test::init();
let port = random_known_port();
let endpoint = format!("127.0.0.1:{port}");
let url = format!("http://{endpoint}");
let mut config = default_test_config(&Mainnet);
config.metrics.endpoint_addr = Some(endpoint.parse().unwrap());
let dir = testdir()?.with_config(&mut config)?;
let child = dir.spawn_child(args!["start"])?;
tokio::time::sleep(LAUNCH_DELAY).await;
let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
let res = client.get(url.try_into().expect("url is valid")).await;
let (res, child) = child.kill_on_error(res)?;
assert!(res.status().is_success());
let mut body = Vec::new();
let mut body_stream = res.into_body();
while let Some(next) = body_stream.frame().await {
body.write_all(next?.data_ref().unwrap())?;
}
let (body, mut child) = child.kill_on_error::<Vec<u8>, hyper::Error>(Ok(body))?;
child.kill(false)?;
let output = child.wait_with_output()?;
let output = output.assert_failure()?;
output.any_output_line_contains(
"# TYPE zebrad_build_info counter",
&body,
"metrics exporter response",
"the metrics response header",
)?;
std::str::from_utf8(&body).expect("unexpected invalid UTF-8 in metrics exporter response");
output.stdout_line_contains(format!("Opened metrics endpoint at {endpoint}").as_str())?;
output
.assert_was_killed()
.wrap_err("Possible port conflict. Are there other zebrad tests running?")?;
Ok(())
}
#[cfg(feature = "filter-reload")]
#[tokio::test]
async fn tracing_endpoint() -> Result<()> {
use bytes::Bytes;
use http_body_util::BodyExt;
use http_body_util::Full;
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
use std::io::Write;
let _init_guard = zebra_test::init();
let port = random_known_port();
let endpoint = format!("127.0.0.1:{port}");
let url_default = format!("http://{endpoint}");
let url_filter = format!("{url_default}/filter");
let mut config = default_test_config(&Mainnet);
config.tracing.endpoint_addr = Some(endpoint.parse().unwrap());
let dir = testdir()?.with_config(&mut config)?;
let child = dir.spawn_child(args!["start"])?;
tokio::time::sleep(LAUNCH_DELAY).await;
let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
let res = client
.get(url_default.try_into().expect("url_default is valid"))
.await;
let (res, child) = child.kill_on_error(res)?;
assert!(res.status().is_success());
let mut body = Vec::new();
let mut body_stream = res.into_body();
while let Some(next) = body_stream.frame().await {
body.write_all(next?.data_ref().unwrap())?;
}
let (body, child) = child.kill_on_error::<Vec<u8>, hyper::Error>(Ok(body))?;
let request = hyper::Request::post(url_filter.clone())
.body("zebrad=debug".to_string().into())
.unwrap();
let post = client.request(request).await;
let (_post, child) = child.kill_on_error(post)?;
let tracing_res = client
.get(url_filter.try_into().expect("url_filter is valid"))
.await;
let (tracing_res, child) = child.kill_on_error(tracing_res)?;
assert!(tracing_res.status().is_success());
let mut tracing_body = Vec::new();
let mut body_stream = tracing_res.into_body();
while let Some(next) = body_stream.frame().await {
tracing_body.write_all(next?.data_ref().unwrap())?;
}
let (tracing_body, mut child) =
child.kill_on_error::<Vec<u8>, hyper::Error>(Ok(tracing_body.clone()))?;
child.kill(false)?;
let output = child.wait_with_output()?;
let output = output.assert_failure()?;
output.stdout_line_contains(format!("Opened tracing endpoint at {endpoint}").as_str())?;
output.any_output_line_contains(
"HTTP endpoint allows dynamic control of the filter",
&body,
"tracing filter endpoint response",
"the tracing response header",
)?;
output.any_output_line_contains(
"tracing events",
&body,
"tracing filter endpoint response",
"the tracing response header",
)?;
std::str::from_utf8(&tracing_body)
.expect("unexpected invalid UTF-8 in tracing filter response");
output.any_output_line_contains(
"zebrad=debug",
&tracing_body,
"tracing filter endpoint response",
"the modified tracing filter",
)?;
std::str::from_utf8(&tracing_body)
.expect("unexpected invalid UTF-8 in modified tracing filter response");
output
.assert_was_killed()
.wrap_err("Possible port conflict. Are there other zebrad tests running?")?;
Ok(())
}
#[tokio::test]
async fn rpc_endpoint_single_thread() -> Result<()> {
rpc_endpoint(false).await
}
#[tokio::test]
async fn rpc_endpoint_parallel_threads() -> Result<()> {
rpc_endpoint(true).await
}
#[tracing::instrument]
async fn rpc_endpoint(parallel_cpu_threads: bool) -> Result<()> {
use serde_json::Value;
use zebra_node_services::rpc_client::RpcRequestClient;
let _init_guard = zebra_test::init();
if zebra_test::net::zebra_skip_network_tests() {
return Ok(());
}
let mut config = os_assigned_rpc_port_config(parallel_cpu_threads, &Mainnet)?;
let dir = testdir()?.with_config(&mut config)?;
let mut child = dir.spawn_child(args!["start"])?;
let rpc_address = read_listen_addr_from_logs(&mut child, OPENED_RPC_ENDPOINT_MSG)?;
let client = RpcRequestClient::new(rpc_address);
std::thread::sleep(LAUNCH_DELAY);
let res = client.call("getinfo", "[]".to_string()).await?;
assert!(res.status().is_success());
let body = res.bytes().await;
let (body, mut child) = child.kill_on_error(body)?;
let parsed: Value = serde_json::from_slice(&body)?;
let build = parsed["result"]["build"].as_str().unwrap();
assert!(build.len() > 4, "Got {build}");
let subversion = parsed["result"]["subversion"].as_str().unwrap();
assert!(subversion.contains("Zebra"), "Got {subversion}");
child.kill(false)?;
let output = child.wait_with_output()?;
let output = output.assert_failure()?;
output
.assert_was_killed()
.wrap_err("Possible port conflict. Are there other zebrad tests running?")?;
Ok(())
}
#[tokio::test]
async fn rpc_endpoint_client_content_type() -> Result<()> {
use zebra_node_services::rpc_client::RpcRequestClient;
let _init_guard = zebra_test::init();
if zebra_test::net::zebra_skip_network_tests() {
return Ok(());
}
let mut config = random_known_rpc_port_config(true, &Mainnet)?;
let dir = testdir()?.with_config(&mut config)?;
let mut child = dir.spawn_child(args!["start"])?;
let rpc_address = read_listen_addr_from_logs(&mut child, OPENED_RPC_ENDPOINT_MSG)?;
let client = RpcRequestClient::new(rpc_address);
let res = client
.call_with_no_content_type("getinfo", "[]".to_string())
.await?;
assert!(res.status().is_success());
let res = client
.call_with_content_type("getinfo", "[]".to_string(), "text/plain".to_string())
.await?;
assert!(res.status().is_success());
let res = client
.call_with_content_type("getinfo", "[]".to_string(), "text/plain;".to_string())
.await?;
assert!(res.status().is_success());
let res = client
.call_with_content_type(
"getinfo",
"[]".to_string(),
"text/plain; other string".to_string(),
)
.await?;
assert!(res.status().is_success());
let res = client
.call_with_content_type("getinfo", "[]".to_string(), "application/json".to_string())
.await?;
assert!(res.status().is_success());
let res = client
.call_with_content_type("getinfo", "[]".to_string(), "whatever".to_string())
.await?;
assert!(res.status().is_client_error());
Ok(())
}