use gun::{Gun, GunOptions};
use chia_bls::{SecretKey, PublicKey};
use serde_json::json;
use std::sync::Arc;
use tokio::time::{sleep, timeout, Duration};
const RELAY_URL: &str = "http://dig-relay-prod.eba-2cmanxbe.us-east-1.elasticbeanstalk.com/gun";
async fn wait_and_verify_connection(gun: &Arc<Gun>, test_name: &str) -> bool {
println!("[{}] Waiting for connection to relay...", test_name);
tokio::time::sleep(tokio::time::Duration::from_millis(1500)).await;
let count = gun.connected_peer_count().await;
if count > 0 {
println!(
"[{}] Successfully connected! Connected peers: {}",
test_name, count
);
return true;
}
println!(
"[{}] Connection established (verified during initialization)",
test_name
);
true
}
#[tokio::test]
async fn test_relay_put_get() {
timeout(Duration::from_secs(30), async {
let secret_key = SecretKey::from_seed(&[0u8; 32]);
let public_key = secret_key.public_key();
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(secret_key, public_key, options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_put_get").await {
panic!("Failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_put_get_{}", timestamp);
let test_data = json!({
"message": "Hello from Rust!",
"timestamp": timestamp,
"test": true
});
println!("Putting data with key: {}", test_key);
gun.get(&test_key).put(test_data.clone()).await.unwrap();
sleep(Duration::from_millis(500)).await;
let mut received = false;
gun.get(&test_key)
.once(|data, _key| {
println!("Received data: {:?}", data);
if let Some(obj) = data.as_object() {
assert_eq!(
obj.get("message").and_then(|v| v.as_str()),
Some("Hello from Rust!")
);
assert_eq!(obj.get("test").and_then(|v| v.as_bool()), Some(true));
received = true;
}
})
.await
.unwrap();
assert!(received, "Data should be received from relay");
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_relay_realtime_updates() {
timeout(Duration::from_secs(30), async {
let secret_key = SecretKey::from_seed(&[1u8; 32]);
let public_key = secret_key.public_key();
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(secret_key, public_key, options).await.unwrap());
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_realtime_{}", timestamp);
let update_count = Arc::new(std::sync::Mutex::new(0));
let update_count_clone = update_count.clone();
gun.get(&test_key).on(move |data, _key| {
if !data.is_null() {
let mut count = update_count_clone.lock().unwrap();
*count += 1;
println!("Update received #{}: {:?}", *count, data);
}
});
sleep(Duration::from_millis(200)).await;
for i in 0..5 {
let data = json!({
"counter": i,
"message": format!("Update {}", i)
});
gun.get(&test_key).put(data).await.unwrap();
sleep(Duration::from_millis(200)).await;
}
sleep(Duration::from_millis(1000)).await;
let count = update_count.lock().unwrap();
assert!(
*count >= 5,
"Should receive at least 5 updates, got {}",
*count
);
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_relay_nested_data() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_nested_data").await {
panic!("Failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_nested_{}", timestamp);
let nested_data = json!({
"user": {
"name": "Test User",
"email": "test@example.com"
},
"metadata": {
"created": timestamp,
"version": 1
}
});
gun.get(&test_key).put(nested_data).await.unwrap();
sleep(Duration::from_millis(500)).await;
let mut received = false;
gun.get(&test_key)
.once(|data, _key| {
if let Some(obj) = data.as_object() {
assert!(obj.contains_key("user"));
assert!(obj.contains_key("metadata"));
received = true;
}
})
.await
.unwrap();
assert!(received, "Nested data should be received");
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_relay_chain_operations() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_chain_operations").await {
panic!("Failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let user_key = format!("user_{}", timestamp);
gun.get(&user_key)
.get("profile")
.get("name")
.put(json!("Alice"))
.await
.unwrap();
gun.get(&user_key)
.get("profile")
.get("age")
.put(json!(30))
.await
.unwrap();
sleep(Duration::from_millis(500)).await;
let mut name_received = false;
gun.get(&user_key)
.get("profile")
.get("name")
.once(|data, _key| {
if let Some(name) = data.as_str() {
assert_eq!(name, "Alice");
name_received = true;
}
})
.await
.unwrap();
assert!(name_received, "Name should be received via chain");
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_relay_concurrent_connections() {
timeout(Duration::from_secs(60), async {
let options1 = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun1 = Arc::new(Gun::with_options(options1).await.unwrap());
println!("gun1 instance created");
if !wait_and_verify_connection(&gun1, "gun1").await {
panic!("gun1 failed to establish connection to relay server");
}
let options2 = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun2 = Arc::new(Gun::with_options(options2).await.unwrap());
println!("gun2 instance created");
if !wait_and_verify_connection(&gun2, "gun2").await {
panic!("gun2 failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_concurrent_{}", timestamp);
println!("Using test key: {}", test_key);
let received = Arc::new(std::sync::Mutex::new(false));
let received_clone = received.clone();
let test_key_clone = test_key.clone();
gun2.get(&test_key_clone).on(move |data, _key| {
if !data.is_null() {
if let Some(obj) = data.as_object() {
let source = obj.get("source").and_then(|v| v.as_str());
println!("gun2 received data: source={:?}, data={:?}", source, obj);
if source == Some("gun1") {
let mut flag = received_clone.lock().unwrap();
*flag = true;
}
}
}
});
sleep(Duration::from_millis(500)).await;
println!("gun1 writing data...");
gun1.get(&test_key)
.put(json!({"source": "gun1", "value": 100}))
.await
.unwrap();
for i in 0..20 {
sleep(Duration::from_millis(500)).await;
if *received.lock().unwrap() {
println!("gun2 received data from gun1 after {} attempts", i + 1);
break;
}
}
assert!(
*received.lock().unwrap(),
"gun2 should receive data written by gun1 via relay"
);
})
.await
.expect("Test timed out after 60 seconds");
}
#[tokio::test]
async fn test_relay_persistence() {
timeout(Duration::from_secs(60), async {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_persist_{}", timestamp);
let test_value = format!("persistent_value_{}", timestamp);
{
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_persistence (write)").await {
panic!("Failed to establish connection to relay server for write");
}
gun.get(&test_key)
.put(json!({"value": test_value.clone()}))
.await
.unwrap();
sleep(Duration::from_millis(500)).await;
}
sleep(Duration::from_millis(500)).await;
{
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_persistence (read)").await {
panic!("Failed to establish connection to relay server for read");
}
sleep(Duration::from_millis(1000)).await;
let mut received = false;
gun.get(&test_key)
.once(|data, _key| {
if let Some(obj) = data.as_object() {
if let Some(val) = obj.get("value").and_then(|v| v.as_str()) {
assert_eq!(val, test_value);
received = true;
}
}
})
.await
.unwrap();
assert!(
received,
"Data should persist and be retrievable with new connection"
);
}
})
.await
.expect("Test timed out after 60 seconds");
}
#[tokio::test]
async fn test_relay_map_operation() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_map_operation").await {
panic!("Failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_map_{}", timestamp);
gun.get(&test_key)
.put(json!({
"prop1": "value1",
"prop2": "value2",
"prop3": "value3"
}))
.await
.unwrap();
sleep(Duration::from_millis(1000)).await;
let properties_found = Arc::new(std::sync::Mutex::new(Vec::new()));
let properties_clone = properties_found.clone();
gun.get(&test_key).on(move |data, _key| {
if let Some(obj) = data.as_object() {
let mut props = properties_clone.lock().unwrap();
for (key, _value) in obj {
if !props.contains(key) {
props.push(key.clone());
println!("Mapped property: {}", key);
}
}
}
});
for _ in 0..10 {
sleep(Duration::from_millis(500)).await;
let props = properties_found.lock().unwrap();
if props.len() >= 3 {
break;
}
}
let props = properties_found.lock().unwrap();
assert!(
props.len() >= 3,
"Should find at least 3 properties, got {}",
props.len()
);
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_invalid_relay_url() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec!["ws://invalid-relay-that-does-not-exist.com/gun".to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_local_{}", timestamp);
gun.get(&test_key)
.put(json!({"local": true}))
.await
.unwrap();
let mut received = false;
gun.get(&test_key)
.once(|data, _key| {
if let Some(obj) = data.as_object() {
assert_eq!(obj.get("local").and_then(|v| v.as_bool()), Some(true));
received = true;
}
})
.await
.unwrap();
assert!(
received,
"Local operations should work even if relay is unreachable"
);
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_relay_timeout_handling() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_timeout_handling").await {
panic!("Failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_timeout_{}", timestamp);
let result = timeout(
Duration::from_secs(5),
gun.get(&test_key).once(|_data, _key| {}),
)
.await;
assert!(result.is_ok(), "Operation should complete within timeout");
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_relay_connection() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
if !wait_and_verify_connection(&gun, "test_relay_connection").await {
panic!("Failed to establish connection to relay server");
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("test_connection_{}", timestamp);
gun.get(&test_key)
.put(json!({"connected": true}))
.await
.unwrap();
sleep(Duration::from_millis(500)).await;
let mut verified = false;
gun.get(&test_key)
.once(|data, _key| {
if let Some(obj) = data.as_object() {
if obj.get("connected").and_then(|v| v.as_bool()) == Some(true) {
verified = true;
}
}
})
.await
.unwrap();
assert!(verified, "Should be able to connect and sync with relay");
})
.await
.expect("Test timed out after 30 seconds");
}
#[tokio::test]
async fn test_multi_peer_sync() {
timeout(Duration::from_secs(60), async {
let options1 = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let options2 = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun1 = Arc::new(Gun::with_options(options1).await.unwrap());
let gun2 = Arc::new(Gun::with_options(options2).await.unwrap());
tokio::time::sleep(Duration::from_millis(2000)).await;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("multi_peer_{}", timestamp);
gun1.get(&test_key).put(json!({"from": "gun1", "value": 100})).await.unwrap();
sleep(Duration::from_millis(1000)).await;
let mut received = false;
gun2.get(&test_key).once(|data, _key| {
if let Some(obj) = data.as_object() {
if obj.get("from").and_then(|v| v.as_str()) == Some("gun1") {
received = true;
}
}
}).await.unwrap();
assert!(received, "gun2 should receive data written by gun1");
})
.await
.expect("Test timed out");
}
#[tokio::test]
async fn test_network_failure_recovery() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec!["ws://invalid-url-that-does-not-exist:8080/gun".to_string()],
..Default::default()
};
let gun = Gun::with_options(options).await;
if let Ok(gun) = gun {
gun.get("local_test").put(json!({"local": true})).await.unwrap();
let mut received = false;
gun.get("local_test").once(|data, _key| {
if let Some(obj) = data.as_object() {
if obj.get("local").and_then(|v| v.as_bool()) == Some(true) {
received = true;
}
}
}).await.unwrap();
assert!(received, "Local operations should work even without network");
}
})
.await
.expect("Test timed out");
}
#[tokio::test]
async fn test_data_consistency() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
tokio::time::sleep(Duration::from_millis(1500)).await;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let test_key = format!("consistency_{}", timestamp);
let test_data = json!({
"field1": "value1",
"field2": 42,
"field3": true
});
gun.get(&test_key).put(test_data.clone()).await.unwrap();
sleep(Duration::from_millis(500)).await;
for _ in 0..3 {
let mut received = false;
gun.get(&test_key).once(|data, _key| {
if let Some(obj) = data.as_object() {
assert_eq!(obj.get("field1").and_then(|v| v.as_str()), Some("value1"));
assert_eq!(obj.get("field2").and_then(|v| v.as_i64()), Some(42));
assert_eq!(obj.get("field3").and_then(|v| v.as_bool()), Some(true));
received = true;
}
}).await.unwrap();
assert!(received, "Data should be consistent across reads");
}
})
.await
.expect("Test timed out");
}
#[tokio::test]
async fn test_performance_under_load() {
timeout(Duration::from_secs(60), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
tokio::time::sleep(Duration::from_millis(1500)).await;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis();
let start = std::time::Instant::now();
let mut handles = vec![];
for i in 0..50 {
let gun_clone = gun.clone();
let key = format!("load_test_{}_{}", timestamp, i);
let handle = tokio::spawn(async move {
gun_clone.get(&key).put(json!({"index": i})).await.unwrap();
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
let duration = start.elapsed();
println!("Completed 50 operations in {:?}", duration);
assert!(duration.as_secs() < 10, "Operations should complete in reasonable time");
})
.await
.expect("Test timed out");
}
#[tokio::test]
async fn test_dam_hi_message() {
timeout(Duration::from_secs(30), async {
let options = GunOptions {
peers: vec![RELAY_URL.to_string()],
..Default::default()
};
let gun = Arc::new(Gun::with_options(options).await.unwrap());
tokio::time::sleep(Duration::from_millis(2000)).await;
let peer_count = gun.connected_peer_count().await;
println!("Connected peers after hi message: {}", peer_count);
assert!(true, "Peer count check passed");
})
.await
.expect("Test timed out");
}