use super::helpers::*;
use eidetica::auth::Permission;
use eidetica::sync::RequestStatus;
use std::time::Duration;
use tracing::info;
#[tokio::test]
async fn test_client_polling_for_pending_status() {
info!("Testing client polling for pending bootstrap request status");
let (_server_instance, _database, server_sync, tree_id) = setup_manual_approval_server();
let server_addr = start_sync_server(&server_sync).await;
let (_client_instance, client_sync) = setup_simple_client();
client_sync.enable_http_transport().unwrap();
let bootstrap_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
assert!(bootstrap_result.is_err());
let error_msg = bootstrap_result.unwrap_err().to_string();
info!("Bootstrap error message: {}", error_msg);
let pending_requests = server_sync
.pending_bootstrap_requests()
.expect("Failed to list pending requests");
assert_eq!(pending_requests.len(), 1);
let (_, request) = &pending_requests[0];
assert!(matches!(request.status, RequestStatus::Pending));
assert_eq!(request.tree_id, tree_id);
info!("✅ Client correctly received pending status and request stored on server");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_client_retry_after_pending() {
info!("Testing client retry behavior after pending approval");
let (_server_instance, _database, server_sync, tree_id) = setup_manual_approval_server();
let server_addr = start_sync_server(&server_sync).await;
let (client_instance, client_sync) = setup_simple_client();
client_sync.enable_http_transport().unwrap();
let bootstrap_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
assert!(bootstrap_result.is_err());
let pending_requests = server_sync
.pending_bootstrap_requests()
.expect("Failed to list pending requests");
assert_eq!(pending_requests.len(), 1);
let (request_id, _) = &pending_requests[0];
approve_request(&server_sync, request_id, "server_admin").expect("Failed to approve request");
tokio::time::sleep(Duration::from_millis(100)).await;
let retry_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
info!("Retry result after approval: {:?}", retry_result);
let normal_sync_result = client_sync
.sync_with_peer(&server_addr, Some(&tree_id))
.await;
info!(
"Normal sync result after approval: {:?}",
normal_sync_result
);
assert!(
client_instance.load_database(&tree_id).is_ok(),
"Client should be able to load database after successful bootstrap"
);
info!("✅ Client successfully retried and bootstrapped after approval");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_duplicate_bootstrap_requests() {
info!("Testing duplicate bootstrap requests from same client");
let (_server_instance, _database, server_sync, tree_id) = setup_manual_approval_server();
let server_addr = start_sync_server(&server_sync).await;
let (_client_instance, client_sync) = setup_simple_client();
client_sync.enable_http_transport().unwrap();
let first_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
assert!(first_result.is_err());
let pending_requests = server_sync
.pending_bootstrap_requests()
.expect("Failed to list pending requests");
assert_eq!(pending_requests.len(), 1);
let second_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
assert!(second_result.is_err());
let pending_requests_after = server_sync
.pending_bootstrap_requests()
.expect("Failed to list pending requests");
info!(
"Pending requests after duplicate attempt: {}",
pending_requests_after.len()
);
info!("✅ Duplicate request handling behavior documented");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_client_behavior_after_rejection() {
info!("Testing client behavior after bootstrap request rejection");
let (_server_instance, _database, server_sync, tree_id) = setup_manual_approval_server();
let server_addr = start_sync_server(&server_sync).await;
let (client_instance, client_sync) = setup_simple_client();
client_sync.enable_http_transport().unwrap();
let bootstrap_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
assert!(bootstrap_result.is_err());
let pending_requests = server_sync
.pending_bootstrap_requests()
.expect("Failed to list pending requests");
assert_eq!(pending_requests.len(), 1);
let (request_id, _) = &pending_requests[0];
server_sync
.reject_bootstrap_request(request_id, "server_admin")
.expect("Failed to reject request");
tokio::time::sleep(Duration::from_millis(100)).await;
let retry_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, "client_key", Permission::Write(5))
.await;
assert!(retry_result.is_err(), "Retry should fail after rejection");
assert!(
client_instance.load_database(&tree_id).is_err(),
"Client should not have access to database after rejection"
);
info!("✅ Client correctly denied access after request rejection");
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_client_different_permission_requests() {
info!("Testing client bootstrap with different permission levels");
let (_server_instance, _database, server_sync, tree_id) = setup_manual_approval_server();
let server_addr = start_sync_server(&server_sync).await;
let permission_levels = [
Permission::Read,
Permission::Write(10),
Permission::Admin(5),
];
for (i, permission) in permission_levels.iter().enumerate() {
let client_key = format!("client_key_{}", i);
let (_client_instance, client_sync) = setup_bootstrap_client(&client_key);
client_sync.enable_http_transport().unwrap();
let bootstrap_result = client_sync
.sync_with_peer_for_bootstrap(&server_addr, &tree_id, &client_key, permission.clone())
.await;
assert!(bootstrap_result.is_err());
let pending_requests = server_sync
.pending_bootstrap_requests()
.expect("Failed to list pending requests");
let matching_request = pending_requests
.iter()
.find(|(_, r)| r.requesting_key_name == client_key)
.expect("Should find request for this client");
assert_eq!(matching_request.1.requested_permission, *permission);
info!(
"✅ Permission level {:?} correctly stored in request",
permission
);
}
server_sync.stop_server_async().await.unwrap();
}
#[tokio::test]
async fn test_client_connection_error_handling() {
info!("Testing client connection error handling");
let (_client_instance, client_sync) = setup_simple_client();
client_sync.enable_http_transport().unwrap();
let fake_tree_id = eidetica::entry::ID::from("fake_tree_id");
let result = client_sync
.sync_with_peer_for_bootstrap(
"127.0.0.1:65432", &fake_tree_id,
"client_key",
Permission::Write(5),
)
.await;
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
let is_connection_error = error_msg.contains("connection")
|| error_msg.contains("refused")
|| error_msg.contains("network")
|| error_msg.contains("sending request");
assert!(
is_connection_error,
"Error should indicate connection failure: {}",
error_msg
);
info!("✅ Client correctly handles connection errors");
}