mirror of
https://github.com/logos-messaging/logos-delivery-rust-bindings.git
synced 2026-07-30 06:53:29 +00:00
Cargo runs test binaries in parallel and serial_test only serialises within one, so the channels and node suites were sharing ./data — two processes over one on-disk persistency root. Nothing here proves that caused harm, but shared mutable state across concurrent processes is not worth leaving in place. local_storage_path is added to WakuNodeConfig to make this expressible. The field is worth having regardless: it was simply missing, and its docs record that the persistency singleton refuses re-targeting, so a second node in the same process must use the same path or fail to start. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
171 lines
5.4 KiB
Rust
171 lines
5.4 KiB
Rust
use base64::Engine;
|
|
use multiaddr::Multiaddr;
|
|
use regex::Regex;
|
|
use secp256k1::SecretKey;
|
|
use serial_test::serial;
|
|
use std::str::FromStr;
|
|
use std::str::from_utf8;
|
|
use std::sync::{Arc, Mutex};
|
|
use std::time::Duration;
|
|
use tokio::time;
|
|
use tokio::time::sleep;
|
|
use waku_bindings::{
|
|
Encoding, LogosDeliveryCtx, WakuContentTopic, WakuMessage, WakuNodeConfig,
|
|
};
|
|
|
|
const ECHO_TIMEOUT: u64 = 1000;
|
|
const ECHO_MESSAGE: &str = "Hi from 🦀!";
|
|
const TEST_PUBSUBTOPIC: &str = "test";
|
|
const TIMEOUT: Duration = Duration::from_secs(30);
|
|
|
|
/// Cargo runs test binaries in parallel and serial_test only serialises within
|
|
/// one, so each binary needs its own persistency root. Every node in a binary
|
|
/// must share it: the singleton refuses to be re-targeted.
|
|
const STORAGE_PATH: &str = "./data-node-test";
|
|
|
|
fn new_node(config: WakuNodeConfig) -> Result<LogosDeliveryCtx, String> {
|
|
let config = WakuNodeConfig {
|
|
local_storage_path: Some(STORAGE_PATH.to_string()),
|
|
..config
|
|
};
|
|
let config_json = serde_json::to_string(&config).map_err(|e| e.to_string())?;
|
|
LogosDeliveryCtx::create(config_json, TIMEOUT)
|
|
}
|
|
|
|
async fn test_echo_messages(
|
|
node1: LogosDeliveryCtx,
|
|
node2: LogosDeliveryCtx,
|
|
content: &'static str,
|
|
content_topic: WakuContentTopic,
|
|
) -> Result<(), String> {
|
|
// The relay push arrives as a typed payload, so the test only has to decode
|
|
// the base64 body rather than match on an event enum.
|
|
let rx_payload: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));
|
|
|
|
let rx_payload_cloned = rx_payload.clone();
|
|
node2.add_on_received_message_listener(move |event| {
|
|
let decoded = base64::engine::general_purpose::STANDARD
|
|
.decode(&event.waku_message.payload)
|
|
.expect("event payload should be base64");
|
|
if let Ok(mut payload) = rx_payload_cloned.lock() {
|
|
*payload = decoded;
|
|
}
|
|
});
|
|
|
|
node1.start_node_async().await?;
|
|
node2.start_node_async().await?;
|
|
|
|
node1
|
|
.waku_relay_subscribe_async(TEST_PUBSUBTOPIC.to_string())
|
|
.await?;
|
|
node2
|
|
.waku_relay_subscribe_async(TEST_PUBSUBTOPIC.to_string())
|
|
.await?;
|
|
|
|
sleep(Duration::from_secs(5)).await;
|
|
|
|
// Interconnect nodes
|
|
// Replace all matches with 127.0.0.1 to avoid issue with NAT or firewall.
|
|
let addresses1 = node1.waku_listen_addresses_async().await?;
|
|
let addresses1 = addresses1
|
|
.split(',')
|
|
.next()
|
|
.ok_or("node1 should report a listen address")?;
|
|
|
|
let re = Regex::new(r"\b(?:\d{1,3}\.){3}\d{1,3}\b").unwrap();
|
|
let addresses1 = re.replace_all(addresses1, "127.0.0.1").to_string();
|
|
|
|
// Parsed only to prove the address is well formed before handing it over.
|
|
let addresses1 = addresses1
|
|
.parse::<Multiaddr>()
|
|
.expect("parse multiaddress")
|
|
.to_string();
|
|
|
|
println!("Connecting node1 to node2: {addresses1}");
|
|
node2.waku_connect_async(addresses1, 10_000).await?;
|
|
|
|
// Wait for mesh to form
|
|
sleep(Duration::from_secs(3)).await;
|
|
|
|
dbg!("Before publish");
|
|
let message = WakuMessage::new(content, content_topic, 1, Vec::new(), false);
|
|
let message = serde_json::to_string(&message).map_err(|e| e.to_string())?;
|
|
node1
|
|
.waku_relay_publish_async(TEST_PUBSUBTOPIC.to_string(), message, 10_000)
|
|
.await
|
|
.expect("send relay messages");
|
|
|
|
// Wait for the msg to arrive
|
|
for _ in 0..50 {
|
|
let message_received = if let Ok(payload) = rx_payload.lock() {
|
|
!payload.is_empty()
|
|
&& from_utf8(&payload).expect("should be valid message") == ECHO_MESSAGE
|
|
} else {
|
|
false
|
|
};
|
|
if message_received {
|
|
node1.stop_node_async().await?;
|
|
node2.stop_node_async().await?;
|
|
return Ok(());
|
|
}
|
|
sleep(Duration::from_millis(100)).await;
|
|
}
|
|
|
|
node1.stop_node_async().await?;
|
|
node2.stop_node_async().await?;
|
|
|
|
Err("Unexpected test ending".to_string())
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[serial]
|
|
async fn default_echo() -> Result<(), String> {
|
|
println!("Test default_echo");
|
|
let node1 = new_node(WakuNodeConfig {
|
|
tcp_port: Some(60010),
|
|
..Default::default()
|
|
})?;
|
|
let node2 = new_node(WakuNodeConfig {
|
|
tcp_port: Some(60020),
|
|
..Default::default()
|
|
})?;
|
|
|
|
let content_topic = WakuContentTopic::new("toychat", "2", "huilong", Encoding::Proto);
|
|
|
|
let sleep = time::sleep(Duration::from_secs(ECHO_TIMEOUT));
|
|
tokio::pin!(sleep);
|
|
|
|
// Send and receive messages. Waits until all messages received.
|
|
// The echo's Result decides the outcome: completing is not the same as
|
|
// having received the message.
|
|
let got_all = tokio::select! {
|
|
_ = sleep => Err("timed out waiting for the echo".to_string()),
|
|
result = test_echo_messages(node1, node2, ECHO_MESSAGE, content_topic) => result,
|
|
};
|
|
|
|
got_all?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[serial]
|
|
async fn node_restart() {
|
|
let config = WakuNodeConfig {
|
|
node_key: Some(
|
|
SecretKey::from_str("05f381866cc21f6c1e2e80e07fa732008e36d942dce3206ad6dcd6793c98d609")
|
|
.unwrap(),
|
|
),
|
|
..Default::default()
|
|
};
|
|
|
|
for _ in 0..3 {
|
|
let node = new_node(config.clone()).expect("default config should be valid");
|
|
node.start_node_async()
|
|
.await
|
|
.expect("node should start with valid config");
|
|
node.stop_node_async().await.expect("node should stop");
|
|
// Resources are freed by LogosDeliveryCtx's Drop impl.
|
|
}
|
|
}
|