use eidetica::store::DocStore;
use super::helpers::*;
use std::time::Duration;
#[tokio::test]
async fn test_bootstrap_sync_from_zero_state() {
let (
server_instance,
mut _server_user,
_server_key_id,
server_database,
test_tree_id,
server_sync,
) = setup_public_sync_enabled_server("server_user", "server_key", "test_database");
let test_entry_id = {
let tx = server_database.new_transaction().unwrap();
let store = tx
.get_store::<eidetica::store::DocStore>("messages")
.unwrap();
store.set_string("msg1", "Hello from server!").unwrap();
store.set_string("msg2", "Second message").unwrap();
tx.commit().unwrap()
};
let server_tips = server_instance.backend().get_tips(&test_tree_id).unwrap();
println!("๐งช DEBUG: Server tips: {:?}", server_tips);
let server_addr = start_sync_server(&server_sync).await;
let (client_instance, _client_user, _client_key_id, client_sync) =
setup_sync_enabled_client("client_user", "client_key");
assert!(
client_instance.load_database(&test_tree_id).is_err(),
"Client should not have the database initially"
);
client_sync.enable_http_transport().unwrap();
println!("๐งช TEST: Attempting bootstrap sync from server...");
client_sync
.sync_with_peer(&server_addr, Some(&test_tree_id))
.await
.expect("Bootstrap sync should succeed");
tokio::time::sleep(Duration::from_millis(100)).await;
let root_client = client_instance
.backend()
.get(&test_tree_id)
.expect("Client should have the root entry");
assert_eq!(root_client.id(), test_tree_id, "Root entry should match");
let entry_result = client_instance.backend().get(&test_entry_id);
println!(
"๐งช DEBUG: Client has test entry: {:?}",
entry_result.is_ok()
);
let tips = client_instance.backend().get_tips(&test_tree_id);
println!("๐งช DEBUG: Client tips result: {:?}", tips);
match tips {
Ok(tip_vec) => {
println!(
"โ
Client successfully bootstrapped tree with {} tips: {:?}",
tip_vec.len(),
tip_vec
);
assert!(
!tip_vec.is_empty(),
"Client should have tips for the synced tree"
);
}
Err(e) => {
panic!("Failed to get tips: {:?}", e);
}
}
println!("โ
TEST: Bootstrap sync completed successfully");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_incremental_sync_after_bootstrap() {
let (
_server_instance,
_server_user,
_server_key_id,
server_database,
test_tree_id,
server_sync,
) = setup_public_sync_enabled_server("server_user", "server_key", "test_database");
let server_addr = start_sync_server(&server_sync).await;
let (client_instance, _client_user, _client_key_id, client_sync) =
setup_sync_enabled_client("client_user", "client_key");
client_sync.enable_http_transport().unwrap();
client_sync
.sync_with_peer(&server_addr, Some(&test_tree_id))
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(100)).await;
assert!(
client_instance.backend().get(&test_tree_id).is_ok(),
"Client should have the tree"
);
let entry2_id = {
let tx = server_database.new_transaction().unwrap();
let store = tx.get_store::<DocStore>("messages").unwrap();
store
.set_string("post_bootstrap", "After bootstrap message")
.unwrap();
tx.commit().unwrap()
};
println!("๐งช TEST: Attempting incremental sync...");
client_sync
.sync_with_peer(&server_addr, Some(&test_tree_id))
.await
.expect("Incremental sync should succeed");
tokio::time::sleep(Duration::from_millis(100)).await;
let entry2_client_result = client_instance.backend().get(&entry2_id);
assert!(
entry2_client_result.is_ok(),
"Client should have received the new entry"
);
let tips = client_instance.backend().get_tips(&test_tree_id).unwrap();
assert!(
tips.contains(&entry2_id),
"Client tips should include the new entry"
);
println!("โ
TEST: Incremental sync completed successfully");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_bootstrap_nonexistent_tree() {
let (_server_instance, server_sync) = setup();
let (_client_instance, client_sync) = setup();
server_sync.enable_http_transport().unwrap();
server_sync.start_server_async("127.0.0.1:0").await.unwrap();
let server_addr = server_sync.get_server_address_async().await.unwrap();
client_sync.enable_http_transport().unwrap();
let fake_tree_id = eidetica::entry::ID::from("fake_tree_id_that_doesnt_exist");
let result = client_sync
.sync_with_peer(&server_addr, Some(&fake_tree_id))
.await;
assert!(result.is_err(), "Syncing non-existent tree should fail");
println!(
"โ
TEST: Non-existent tree sync failed as expected: {:?}",
result.err()
);
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_discover_peer_trees_placeholder() {
let (_server_instance, server_sync) = setup();
let (_client_instance, client_sync) = setup();
let server_addr = start_sync_server(&server_sync).await;
client_sync.enable_http_transport().unwrap();
let trees = client_sync.discover_peer_trees(&server_addr).await.unwrap();
assert!(
trees.is_empty(),
"discover_peer_trees currently returns empty list"
);
println!("โ
TEST: Tree discovery placeholder works (returns empty list)");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_bootstrap_malformed_request_data() {
let (_server_instance, server_sync) = setup();
let (_client_instance, client_sync) = setup();
let root_entry = create_test_tree_entry();
let test_tree_id = root_entry.id().clone();
server_sync
.backend()
.expect("Failed to get backend")
.put_verified(root_entry)
.unwrap();
let server_addr = start_sync_server(&server_sync).await;
client_sync.enable_http_transport().unwrap();
let malformed_tree_id = eidetica::entry::ID::from("invalid_tree_format");
let result = client_sync
.sync_with_peer_for_bootstrap(
&server_addr,
&malformed_tree_id,
"client_key",
eidetica::auth::Permission::Write(5),
)
.await;
assert!(
result.is_err(),
"Bootstrap should fail with invalid tree ID"
);
println!("โ
Bootstrap correctly rejected malformed tree ID");
let result = client_sync
.sync_with_peer_for_bootstrap(
&server_addr,
&test_tree_id,
"", eidetica::auth::Permission::Write(5),
)
.await;
assert!(result.is_err(), "Bootstrap should fail with empty key name");
println!("โ
Bootstrap correctly rejected empty key name");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_bootstrap_conflicting_tree_ids() {
let (_server_instance, server_sync) = setup();
let (_client_instance, client_sync) = setup();
let root_entry = create_test_tree_entry();
let _actual_tree_id = root_entry.id().clone();
server_sync
.backend()
.expect("Failed to get backend")
.put_verified(root_entry)
.unwrap();
let server_addr = start_sync_server(&server_sync).await;
client_sync.enable_http_transport().unwrap();
let different_tree_id = eidetica::entry::ID::from("different_tree_that_doesnt_exist");
let result = client_sync
.sync_with_peer_for_bootstrap(
&server_addr,
&different_tree_id,
"client_key",
eidetica::auth::Permission::Write(5),
)
.await;
assert!(
result.is_err(),
"Bootstrap should fail for non-existent tree"
);
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("not found") || error_msg.contains("exist"),
"Error should indicate tree not found: {}",
error_msg
);
println!("โ
Bootstrap correctly rejected request for non-existent tree");
server_sync.stop_server_async().await.unwrap();
}