use gun::Gun;
use chia_bls::{SecretKey, PublicKey};
use serde_json::json;
use std::sync::Arc;
#[tokio::test]
async fn test_chain_get_navigation() {
let secret_key = SecretKey::from_seed(&[0u8; 32]);
let public_key = secret_key.public_key();
let gun = Gun::new(secret_key, public_key);
let chain = gun.get("user").get("profile").get("name");
chain.put(json!("Alice")).await.unwrap();
let mut received = false;
gun.get("user")
.get("profile")
.get("name")
.once(|data, _key| {
assert_eq!(data.as_str(), Some("Alice"));
received = true;
})
.await
.unwrap();
assert!(received);
}
#[tokio::test]
async fn test_chain_put_simple_value() {
let secret_key = SecretKey::from_seed(&[1u8; 32]);
let public_key = secret_key.public_key();
let gun = Gun::new(secret_key, public_key);
let chain = gun.get("counter");
chain.put(json!(42)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let mut received = false;
chain
.once(|data, _key| {
if data.is_i64() {
assert_eq!(data.as_i64(), Some(42));
received = true;
}
})
.await
.unwrap();
}
#[tokio::test]
async fn test_chain_put_object() {
let gun = Gun::new();
let chain = gun.get("user");
let data = json!({
"name": "Alice",
"age": 30,
"city": "NYC"
});
chain.put(data.clone()).await.unwrap();
let mut received = false;
chain
.once(|data, _key| {
assert!(data.is_object());
if let Some(obj) = data.as_object() {
assert_eq!(obj.get("name").and_then(|v| v.as_str()), Some("Alice"));
assert_eq!(obj.get("age").and_then(|v| v.as_i64()), Some(30));
}
received = true;
})
.await
.unwrap();
assert!(received);
}
#[tokio::test]
async fn test_chain_put_soul_reference() {
let gun = Gun::new();
let user1 = gun.get("user1");
user1.put(json!({"name": "Alice"})).await.unwrap();
let user1_soul = gun.get("user1");
user1_soul
.once(|_data, _key| {
})
.await
.unwrap();
}
#[tokio::test]
async fn test_chain_on_subscription() {
let gun = Gun::new();
let chain = gun.get("counter");
let count = Arc::new(std::sync::Mutex::new(0));
let count_clone = count.clone();
chain.on(move |_data, _key| {
*count_clone.lock().unwrap() += 1;
});
for i in 0..5 {
chain.put(json!(i)).await.unwrap();
}
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
assert!(*count.lock().unwrap() >= 1);
}
#[tokio::test]
async fn test_chain_once_single_read() {
let gun = Gun::new();
let chain = gun.get("value");
chain.put(json!("test")).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let mut call_count = 0;
chain
.once(|data, _key| {
if data.is_string() {
assert_eq!(data.as_str(), Some("test"));
}
call_count += 1;
})
.await
.unwrap();
chain
.once(|_data, _key| {
call_count += 1;
})
.await
.unwrap();
assert_eq!(call_count, 2);
}
#[tokio::test]
async fn test_chain_map_iteration() {
let gun = Gun::new();
let chain = gun.get("node");
chain
.put(json!({
"a": 1,
"b": 2,
"c": 3
}))
.await
.unwrap();
let properties = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
let props_clone = properties.clone();
chain.map(move |value, key| {
props_clone.lock().unwrap().push((key, value.clone()));
});
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let props = properties.lock().unwrap();
assert!(props.len() >= 3); }
#[tokio::test]
async fn test_chain_set_add_item() {
let gun = Gun::new();
let chain = gun.get("collection");
let item1 = json!({"id": 1, "name": "item1"});
chain.set(item1).await.unwrap();
let item2 = json!({"id": 2, "name": "item2"});
chain.set(item2).await.unwrap();
}
#[tokio::test]
async fn test_chain_back_navigation() {
let gun = Gun::new();
let root = gun.root();
let user = root.get("user");
let profile = user.get("profile");
let name = profile.get("name");
let back_to_profile = name.back(Some(1));
assert!(back_to_profile.is_some());
let back_to_root = profile.back(None);
assert!(back_to_root.is_some());
let back_multiple = name.back(Some(2));
assert!(back_multiple.is_some());
}
#[tokio::test]
async fn test_chain_off_remove_listeners() {
let gun = Gun::new();
let chain = gun.get("value");
let count = Arc::new(std::sync::Mutex::new(0));
let count_clone = count.clone();
chain.on(move |_data, _key| {
*count_clone.lock().unwrap() += 1;
});
chain.put(json!(1)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
let count_before = *count.lock().unwrap();
chain.off();
chain.put(json!(2)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
let count_after = *count.lock().unwrap();
assert_eq!(count_before, count_after);
}
#[tokio::test]
async fn test_chain_complex_nesting() {
let gun = Gun::new();
gun.get("a")
.get("b")
.get("c")
.get("d")
.put(json!("deep_value"))
.await
.unwrap();
let mut received = false;
gun.get("a")
.get("b")
.get("c")
.get("d")
.once(|data, _key| {
assert_eq!(data.as_str(), Some("deep_value"));
received = true;
})
.await
.unwrap();
assert!(received);
}
#[tokio::test]
async fn test_chain_multiple_listeners() {
let gun = Gun::new();
let chain = gun.get("value");
let count1 = Arc::new(std::sync::Mutex::new(0));
let count2 = Arc::new(std::sync::Mutex::new(0));
let c1 = count1.clone();
chain.on(move |_data, _key| {
*c1.lock().unwrap() += 1;
});
let c2 = count2.clone();
chain.on(move |_data, _key| {
*c2.lock().unwrap() += 1;
});
chain.put(json!(1)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
assert!(*count1.lock().unwrap() >= 1);
assert!(*count2.lock().unwrap() >= 1);
}
#[tokio::test]
async fn test_chain_once_network_request() {
let gun = Gun::new();
let chain = gun.get("network_test");
let mut called = false;
chain.once(|_data, _key| {
called = true;
}).await.unwrap();
assert!(called, "once() callback should be called even if data not found");
}
#[tokio::test]
async fn test_chain_on_change_detection() {
let gun = Gun::new();
let chain = gun.get("change_test");
let update_count = Arc::new(std::sync::Mutex::new(0));
let update_count_clone = update_count.clone();
chain.on(move |data, _key| {
if data.is_i64() {
*update_count_clone.lock().unwrap() += 1;
}
});
chain.put(json!(42)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
chain.put(json!(42)).await.unwrap(); tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
chain.put(json!(43)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
let count = *update_count.lock().unwrap();
assert!(count >= 1);
}
#[tokio::test]
async fn test_chain_on_real_time_propagation() {
let gun = Gun::new();
let chain = gun.get("realtime_test");
let received_values = Arc::new(std::sync::Mutex::new(Vec::new()));
let values_clone = received_values.clone();
chain.on(move |data, _key| {
if data.is_i64() {
values_clone.lock().unwrap().push(data.as_i64().unwrap());
}
});
for i in 0..5 {
chain.put(json!(i)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let values = received_values.lock().unwrap();
assert!(values.len() >= 1);
}
#[tokio::test]
async fn test_chain_concurrent_operations() {
let gun = Gun::new();
let mut handles = vec![];
for i in 0..10 {
let chain = gun.get(&format!("concurrent_{}", i));
let handle = tokio::spawn(async move {
chain.put(json!(i)).await.unwrap();
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
for i in 0..10 {
let mut found = false;
gun.get(&format!("concurrent_{}", i))
.once(|data, _key| {
if data.is_i64() && data.as_i64() == Some(i) {
found = true;
}
})
.await
.unwrap();
}
}
#[tokio::test]
async fn test_chain_concurrent_read_write() {
let gun = Gun::new();
let chain = gun.get("concurrent_rw");
let read_count = Arc::new(std::sync::Mutex::new(0));
let write_count = Arc::new(std::sync::Mutex::new(0));
let read_count_clone = read_count.clone();
let chain_read = chain.clone();
tokio::spawn(async move {
for _ in 0..10 {
chain_read.once(|_data, _key| {
*read_count_clone.lock().unwrap() += 1;
}).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
});
for i in 0..10 {
chain.put(json!(i)).await.unwrap();
*write_count.lock().unwrap() += 1;
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
assert_eq!(*write_count.lock().unwrap(), 10);
assert!(*read_count.lock().unwrap() >= 0);
}
#[tokio::test]
async fn test_chain_missing_data_handling() {
let gun = Gun::new();
let chain = gun.get("missing_data");
let mut called = false;
chain.once(|data, _key| {
assert!(data.is_null() || !data.is_object());
called = true;
}).await.unwrap();
assert!(called, "once() should handle missing data gracefully");
}
#[tokio::test]
async fn test_chain_on_with_initial_data() {
let gun = Gun::new();
let chain = gun.get("initial_data");
chain.put(json!({"value": 100})).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
let received_initial = Arc::new(std::sync::Mutex::new(false));
let received_update = Arc::new(std::sync::Mutex::new(false));
let initial_clone = received_initial.clone();
let update_clone = received_update.clone();
chain.on(move |data, _key| {
if let Some(obj) = data.as_object() {
if obj.get("value").and_then(|v| v.as_i64()) == Some(100) {
*initial_clone.lock().unwrap() = true;
}
if obj.get("value").and_then(|v| v.as_i64()) == Some(200) {
*update_clone.lock().unwrap() = true;
}
}
});
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
chain.put(json!({"value": 200})).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
assert!(*received_initial.lock().unwrap() || *received_update.lock().unwrap());
}