use crate::core::server::{ConflictResolver, ResourceStateManager};
use crate::core::types::{Update, Version};
use rand::prelude::*;
use rand::rngs::SmallRng;
use rand::SeedableRng;
use serde_json::json;
async fn fuzz_state_management_once(seed: u64) {
let mut rng = SmallRng::seed_from_u64(seed);
let manager = ResourceStateManager::new();
let resolver = ConflictResolver::new(manager.clone());
let resource_id = "fuzz-resource";
let agents = ["alice", "bob", "charlie"];
let _ = manager.get_or_create_resource(resource_id, "system", Some("diamond"));
let mut expected_versions = Vec::new();
for i in 0..100 {
let agent = agents.choose(&mut rng).unwrap();
let op_type = rng.random_range(0..2);
let current_version = resolver.get_resource_version(resource_id);
let parents = if rng.random_bool(0.7) && current_version.is_some() {
vec![current_version.unwrap()]
} else {
vec![]
};
let version_id = format!("v-{}-{}", seed, i);
let update = match op_type {
0 => {
let pos = rng.random_range(0..200); Update {
version: vec![Version::new(&version_id)],
parents,
body: Some(
json!({
"inserts": [{"pos": pos, "text": format!("x-{}", i)}]
})
.to_string()
.into(),
),
merge_type: Some("diamond".to_string()),
..Default::default()
}
}
_ => {
let start = rng.random_range(0..200);
Update {
version: vec![Version::new(&version_id)],
parents,
body: Some(
json!({
"deletes": [{"start": start, "end": start + 1}]
})
.to_string()
.into(),
),
merge_type: Some("diamond".to_string()),
..Default::default()
}
}
};
match resolver.resolve_update(resource_id, &update, agent).await {
Ok(_) => {
expected_versions.push(version_id);
}
Err(e) => {
}
}
if i % 10 == 0 {
if let Some(resource) = manager.get_resource(resource_id) {
let state = resource.lock();
state.crdt.dbg_check(true);
}
}
}
}
#[tokio::test]
async fn test_state_fuzz_once() {
fuzz_state_management_once(123).await;
}
#[tokio::test]
async fn test_state_convergence() {
let seed = 456;
let mut _rng = SmallRng::seed_from_u64(seed);
let manager_a = ResourceStateManager::new();
let manager_b = ResourceStateManager::new();
let resolver_a = ConflictResolver::new(manager_a.clone());
let resolver_b = ConflictResolver::new(manager_b.clone());
let resource_id = "conv-resource";
let _ = manager_a.get_or_create_resource(resource_id, "sys", Some("diamond"));
let _ = manager_b.get_or_create_resource(resource_id, "sys", Some("diamond"));
let mut updates_a = Vec::new();
let mut updates_b = Vec::new();
let initial_version_a = resolver_a.get_resource_version(resource_id);
let mut current_parents_a = initial_version_a.map(|v| vec![v]).unwrap_or_default();
for i in 0..10 {
let version_id = format!("v-a-{}", i);
let update = Update {
version: vec![Version::new(&version_id)],
parents: current_parents_a.clone(),
body: Some(
json!({"inserts": [{"pos": 0, "text": format!("a{}", i)}]})
.to_string()
.into(),
),
merge_type: Some("diamond".to_string()),
..Default::default()
};
let _ = resolver_a
.resolve_update(resource_id, &update, "alice")
.await
.unwrap();
current_parents_a = vec![Version::new(&version_id)];
updates_a.push(update);
}
let initial_version_b = resolver_b.get_resource_version(resource_id);
let mut current_parents_b = initial_version_b.map(|v| vec![v]).unwrap_or_default();
for i in 0..10 {
let version_id = format!("v-b-{}", i);
let update = Update {
version: vec![Version::new(&version_id)],
parents: current_parents_b.clone(),
body: Some(
json!({"inserts": [{"pos": 0, "text": format!("b{}", i)}]})
.to_string()
.into(),
),
merge_type: Some("diamond".to_string()),
..Default::default()
};
let _ = resolver_b
.resolve_update(resource_id, &update, "bob")
.await
.unwrap();
updates_b.push(update);
current_parents_b = vec![Version::new(&version_id)];
}
for u in &updates_a {
resolver_b
.resolve_update(resource_id, u, "alice")
.await
.unwrap();
}
for u in &updates_b {
resolver_a
.resolve_update(resource_id, u, "bob")
.await
.unwrap();
}
let content_a = resolver_a.get_resource_content(resource_id).unwrap();
let content_b = resolver_b.get_resource_content(resource_id).unwrap();
println!("Content A: {}", content_a);
println!("Content B: {}", content_b);
assert_eq!(content_a, content_b);
{
let res_a = manager_a.get_resource(resource_id).unwrap();
let state_a = res_a.lock();
state_a.crdt.dbg_check(true);
let res_b = manager_b.get_resource(resource_id).unwrap();
let state_b = res_b.lock();
state_b.crdt.dbg_check(true);
}
}