use communitas_core::crdt::documents::{ChannelDocument, CrdtDocument};
use communitas_core::crdt_manager::{CrdtError, CrdtManager};
use tempfile::tempdir;
use yrs::updates::decoder::Decode;
use yrs::{Doc, Map, ReadTxn, Transact, Update};
fn encode_state(doc: &Doc) -> Vec<u8> {
doc.transact()
.encode_state_as_update_v1(&yrs::StateVector::default())
}
fn apply_update_to_doc(doc: &Doc, update_bytes: &[u8]) -> Result<(), String> {
let update =
Update::decode_v1(update_bytes).map_err(|e| format!("Failed to decode update: {}", e))?;
let mut txn = doc.transact_mut();
txn.apply_update(update);
Ok(())
}
#[tokio::test]
async fn test_create_and_load_document() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let channel = ChannelDocument {
id: "channel-123".to_string(),
four_word_identity: "test-channel-main-room".to_string(),
org_id: "org-456".to_string(),
name: "Test Channel".to_string(),
description: Some("A test channel for CRDT operations".to_string()),
created_by: "creator-789".to_string(),
created_at: 1234567890,
private_disk_id: "disk-111".to_string(),
public_disk_id: "disk-222".to_string(),
website_root: None,
};
let doc = ChannelDocument::create_document(&channel.id).expect("Failed to create document");
channel
.update_document(&doc)
.expect("Failed to update document");
manager
.save_document(
&format!("channel:{}:metadata", channel.id),
"channel",
&channel.id,
&doc,
)
.await
.expect("Failed to save document");
let loaded_doc = manager
.load_document(&format!("channel:{}:metadata", channel.id))
.await
.expect("Failed to load document");
let loaded_channel =
ChannelDocument::from_document(&loaded_doc).expect("Failed to parse document");
assert_eq!(loaded_channel.id, channel.id);
assert_eq!(loaded_channel.name, channel.name);
assert_eq!(loaded_channel.org_id, channel.org_id);
assert_eq!(loaded_channel.description, channel.description);
}
#[tokio::test]
async fn test_concurrent_edits_merge_correctly() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let channel = ChannelDocument {
id: "channel-concurrent".to_string(),
four_word_identity: "test-concurrent-edit".to_string(),
org_id: "org-123".to_string(),
name: "Original Name".to_string(),
description: Some("Original description".to_string()),
created_by: "peer-a".to_string(),
created_at: 1000,
private_disk_id: "disk-a".to_string(),
public_disk_id: "disk-b".to_string(),
website_root: None,
};
let doc_id = format!("channel:{}:metadata", channel.id);
let doc_peer_a = ChannelDocument::create_document(&channel.id).expect("Create doc");
channel.update_document(&doc_peer_a).expect("Update doc");
manager
.save_document(&doc_id, "channel", &channel.id, &doc_peer_a)
.await
.expect("Save initial document");
let doc_peer_b = manager
.load_document(&doc_id)
.await
.expect("Peer B load document");
{
let root = doc_peer_a.get_or_insert_map("root");
let mut txn = doc_peer_a.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "name", "Name by Peer A");
}
{
let root = doc_peer_b.get_or_insert_map("root");
let mut txn = doc_peer_b.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "description", "Description by Peer B");
}
let update_a = encode_state(&doc_peer_a);
let update_b = encode_state(&doc_peer_b);
apply_update_to_doc(&doc_peer_a, &update_b).expect("Peer A apply update");
apply_update_to_doc(&doc_peer_b, &update_a).expect("Peer B apply update");
let channel_a = ChannelDocument::from_document(&doc_peer_a).expect("Parse A");
let channel_b = ChannelDocument::from_document(&doc_peer_b).expect("Parse B");
assert_eq!(channel_a.name, "Name by Peer A");
assert_eq!(channel_b.name, "Name by Peer A");
assert_eq!(
channel_a.description,
Some("Description by Peer B".to_string())
);
assert_eq!(
channel_b.description,
Some("Description by Peer B".to_string())
);
manager
.save_document(&doc_id, "channel", &channel.id, &doc_peer_a)
.await
.expect("Save merged document");
}
#[tokio::test]
async fn test_three_way_concurrent_merge() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let channel = ChannelDocument {
id: "channel-three-way".to_string(),
four_word_identity: "test-three-way".to_string(),
org_id: "org-999".to_string(),
name: "Initial".to_string(),
description: Some("Initial".to_string()),
created_by: "creator".to_string(),
created_at: 1000,
private_disk_id: "disk-1".to_string(),
public_disk_id: "disk-2".to_string(),
website_root: Some("https://initial.com".to_string()),
};
let doc_id = format!("channel:{}:metadata", channel.id);
let doc_initial = ChannelDocument::create_document(&channel.id).expect("Create doc");
channel.update_document(&doc_initial).expect("Update doc");
manager
.save_document(&doc_id, "channel", &channel.id, &doc_initial)
.await
.expect("Save initial");
let doc_peer_a = manager.load_document(&doc_id).await.expect("Load A");
let doc_peer_b = manager.load_document(&doc_id).await.expect("Load B");
let doc_peer_c = manager.load_document(&doc_id).await.expect("Load C");
{
let root = doc_peer_a.get_or_insert_map("root");
let mut txn = doc_peer_a.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "name", "Edited by A");
}
{
let root = doc_peer_b.get_or_insert_map("root");
let mut txn = doc_peer_b.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "description", "Edited by B");
}
{
let root = doc_peer_c.get_or_insert_map("root");
let mut txn = doc_peer_c.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "website_root", "https://peer-c.com");
}
let update_a = encode_state(&doc_peer_a);
let update_b = encode_state(&doc_peer_b);
let update_c = encode_state(&doc_peer_c);
apply_update_to_doc(&doc_peer_a, &update_b).expect("A apply B");
apply_update_to_doc(&doc_peer_a, &update_c).expect("A apply C");
apply_update_to_doc(&doc_peer_b, &update_a).expect("B apply A");
apply_update_to_doc(&doc_peer_b, &update_c).expect("B apply C");
apply_update_to_doc(&doc_peer_c, &update_a).expect("C apply A");
apply_update_to_doc(&doc_peer_c, &update_b).expect("C apply B");
let channel_a = ChannelDocument::from_document(&doc_peer_a).expect("Parse A");
let channel_b = ChannelDocument::from_document(&doc_peer_b).expect("Parse B");
let channel_c = ChannelDocument::from_document(&doc_peer_c).expect("Parse C");
assert_eq!(channel_a.name, channel_b.name);
assert_eq!(channel_b.name, channel_c.name);
assert_eq!(channel_a.description, channel_b.description);
assert_eq!(channel_b.description, channel_c.description);
assert_eq!(channel_a.website_root, channel_b.website_root);
assert_eq!(channel_b.website_root, channel_c.website_root);
assert_eq!(channel_a.name, "Edited by A");
assert_eq!(channel_a.description, Some("Edited by B".to_string()));
assert_eq!(
channel_a.website_root,
Some("https://peer-c.com".to_string())
);
}
#[tokio::test]
async fn test_document_deletion_with_tombstone() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let channel = ChannelDocument {
id: "channel-to-delete".to_string(),
four_word_identity: "test-delete".to_string(),
org_id: "org-123".to_string(),
name: "To Be Deleted".to_string(),
description: None,
created_by: "creator".to_string(),
created_at: 1000,
private_disk_id: "disk-1".to_string(),
public_disk_id: "disk-2".to_string(),
website_root: None,
};
let doc_id = format!("channel:{}:metadata", channel.id);
let doc = ChannelDocument::create_document(&channel.id).expect("Create doc");
channel.update_document(&doc).expect("Update doc");
manager
.save_document(&doc_id, "channel", &channel.id, &doc)
.await
.expect("Save document");
{
let root = doc.get_or_insert_map("root");
let mut txn = doc.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "deleted", true);
metadata_map.insert(&mut txn, "deleted_at", 2000i64);
metadata_map.insert(&mut txn, "deleted_by", "deleter-id");
}
manager
.save_document(&doc_id, "channel", &channel.id, &doc)
.await
.expect("Save tombstoned document");
let loaded_doc = manager.load_document(&doc_id).await.expect("Load doc");
let root = loaded_doc.get_or_insert_map("root");
let txn = loaded_doc.transact();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
let deleted = metadata_map
.get(&txn, "deleted")
.and_then(|v| bool::try_from(v).ok())
.unwrap_or(false);
assert!(deleted, "Document should be marked as deleted");
let deleted_at = metadata_map
.get(&txn, "deleted_at")
.and_then(|v| i64::try_from(v).ok());
assert_eq!(deleted_at, Some(2000));
}
#[tokio::test]
async fn test_tombstone_propagation() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let channel = ChannelDocument {
id: "channel-tombstone-prop".to_string(),
four_word_identity: "test-tombstone".to_string(),
org_id: "org-123".to_string(),
name: "Test Channel".to_string(),
description: None,
created_by: "creator".to_string(),
created_at: 1000,
private_disk_id: "disk-1".to_string(),
public_disk_id: "disk-2".to_string(),
website_root: None,
};
let doc_id = format!("channel:{}:metadata", channel.id);
let doc_peer_a = ChannelDocument::create_document(&channel.id).expect("Create doc");
channel.update_document(&doc_peer_a).expect("Update doc");
manager
.save_document(&doc_id, "channel", &channel.id, &doc_peer_a)
.await
.expect("Save initial");
let doc_peer_b = manager.load_document(&doc_id).await.expect("Load B");
{
let root = doc_peer_a.get_or_insert_map("root");
let mut txn = doc_peer_a.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "deleted", true);
metadata_map.insert(&mut txn, "deleted_at", 5000i64);
}
let update_a = encode_state(&doc_peer_a);
apply_update_to_doc(&doc_peer_b, &update_a).expect("B apply A's tombstone");
let root = doc_peer_b.get_or_insert_map("root");
let txn = doc_peer_b.transact();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
let deleted = metadata_map
.get(&txn, "deleted")
.and_then(|v| bool::try_from(v).ok())
.unwrap_or(false);
assert!(deleted, "Peer B should see tombstone from Peer A");
}
#[tokio::test]
async fn test_offline_online_sync() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let channel = ChannelDocument {
id: "channel-offline".to_string(),
four_word_identity: "test-offline".to_string(),
org_id: "org-123".to_string(),
name: "Initial".to_string(),
description: Some("Initial".to_string()),
created_by: "creator".to_string(),
created_at: 1000,
private_disk_id: "disk-1".to_string(),
public_disk_id: "disk-2".to_string(),
website_root: None,
};
let doc_id = format!("channel:{}:metadata", channel.id);
let doc = ChannelDocument::create_document(&channel.id).expect("Create doc");
channel.update_document(&doc).expect("Update doc");
manager
.save_document(&doc_id, "channel", &channel.id, &doc)
.await
.expect("Save initial");
let doc_peer_a = manager.load_document(&doc_id).await.expect("Load A");
{
let root = doc_peer_a.get_or_insert_map("root");
let mut txn = doc_peer_a.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "name", "Offline Edit 1");
}
{
let root = doc_peer_a.get_or_insert_map("root");
let mut txn = doc_peer_a.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "description", "Offline Edit 2");
}
let doc_peer_b = manager.load_document(&doc_id).await.expect("Load B");
{
let root = doc_peer_b.get_or_insert_map("root");
let mut txn = doc_peer_b.transact_mut();
let metadata = root.get(&txn, "metadata").unwrap();
let metadata_map = yrs::MapRef::try_from(metadata).unwrap();
metadata_map.insert(&mut txn, "website_root", "https://peer-b.com");
}
let update_a = encode_state(&doc_peer_a);
let update_b = encode_state(&doc_peer_b);
apply_update_to_doc(&doc_peer_a, &update_b).expect("A apply B");
apply_update_to_doc(&doc_peer_b, &update_a).expect("B apply A");
let channel_a = ChannelDocument::from_document(&doc_peer_a).expect("Parse A");
let channel_b = ChannelDocument::from_document(&doc_peer_b).expect("Parse B");
assert_eq!(channel_a.name, channel_b.name);
assert_eq!(channel_a.description, channel_b.description);
assert_eq!(channel_a.website_root, channel_b.website_root);
assert_eq!(channel_a.name, "Offline Edit 1");
assert_eq!(channel_a.description, Some("Offline Edit 2".to_string()));
assert_eq!(
channel_a.website_root,
Some("https://peer-b.com".to_string())
);
}
#[tokio::test]
async fn test_document_size_limit() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let large_desc = "x".repeat(11 * 1024 * 1024);
let channel = ChannelDocument {
id: "channel-large".to_string(),
four_word_identity: "test-large".to_string(),
org_id: "org-123".to_string(),
name: "Test".to_string(),
description: Some(large_desc),
created_by: "creator".to_string(),
created_at: 1000,
private_disk_id: "disk-1".to_string(),
public_disk_id: "disk-2".to_string(),
website_root: None,
};
let doc_id = format!("channel:{}:metadata", channel.id);
let doc = ChannelDocument::create_document(&channel.id).expect("Create doc");
channel.update_document(&doc).expect("Update doc");
let result = manager
.save_document(&doc_id, "channel", &channel.id, &doc)
.await;
assert!(
matches!(result, Err(CrdtError::Encoding(_))),
"Should reject document exceeding size limit"
);
}
#[tokio::test]
async fn test_entity_type_isolation() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let manager = CrdtManager::new(temp_dir.path())
.await
.expect("Failed to create manager");
let doc_channel = Doc::new();
{
let root = doc_channel.get_or_insert_map("root");
let mut txn = doc_channel.transact_mut();
root.insert(&mut txn, "type", "channel_data");
root.insert(&mut txn, "value", "Channel Value");
}
let doc_org = Doc::new();
{
let root = doc_org.get_or_insert_map("root");
let mut txn = doc_org.transact_mut();
root.insert(&mut txn, "type", "organization_data");
root.insert(&mut txn, "value", "Organization Value");
}
manager
.save_document("channel:123:metadata", "channel", "123", &doc_channel)
.await
.expect("Save channel document");
manager
.save_document("organization:123:metadata", "organization", "123", &doc_org)
.await
.expect("Save organization document");
let loaded_channel = manager
.load_document("channel:123:metadata")
.await
.expect("Load channel document");
let loaded_org = manager
.load_document("organization:123:metadata")
.await
.expect("Load organization document");
{
let root = loaded_channel.get_or_insert_map("root");
let txn = loaded_channel.transact();
let type_val = root
.get(&txn, "type")
.and_then(|v| String::try_from(v).ok())
.expect("Channel type exists");
let value_val = root
.get(&txn, "value")
.and_then(|v| String::try_from(v).ok())
.expect("Channel value exists");
assert_eq!(type_val, "channel_data");
assert_eq!(value_val, "Channel Value");
}
{
let root = loaded_org.get_or_insert_map("root");
let txn = loaded_org.transact();
let type_val = root
.get(&txn, "type")
.and_then(|v| String::try_from(v).ok())
.expect("Organization type exists");
let value_val = root
.get(&txn, "value")
.and_then(|v| String::try_from(v).ok())
.expect("Organization value exists");
assert_eq!(type_val, "organization_data");
assert_eq!(value_val, "Organization Value");
}
let channel_list = manager
.list_documents("channel")
.await
.expect("List channel docs");
let org_list = manager
.list_documents("organization")
.await
.expect("List org docs");
assert_eq!(channel_list.len(), 1);
assert_eq!(org_list.len(), 1);
assert!(channel_list.contains(&"channel:123:metadata".to_string()));
assert!(org_list.contains(&"organization:123:metadata".to_string()));
}