#![cfg(feature = "ha")]
use remdb::config::{LogMode, TimeSeriesConfig, WALConfig};
use remdb::ha::heartbeat::HeartbeatMonitor;
use remdb::ha::manager::HAManager;
use remdb::ha::replication::ReplicationManager;
use remdb::ha::role::RoleManager;
use remdb::ha::HAError;
use remdb::ha::{HAConfig, HARole, ReplicationMode};
use remdb::*;
struct TestPlatform;
impl crate::platform::Platform for TestPlatform {
fn get_timestamp(&self) -> u64 {
123456
}
fn get_timestamp_us(&self) -> u64 {
123456789
}
fn spin_lock(&self, _lock: &mut u32) {
}
fn spin_unlock(&self, _lock: &mut u32) {
}
fn memcpy(&self, dst: *mut u8, src: *const u8, size: usize) {
unsafe {
std::ptr::copy(src, dst, size);
}
}
fn memset(&self, ptr: *mut u8, value: u8, size: usize) {
unsafe {
std::ptr::write_bytes(ptr, value, size);
}
}
fn compiler_barrier(&self) {
}
fn full_memory_barrier(&self) {
}
fn delay_ms(&self, _ms: u32) {
}
fn delay_us(&self, _us: u32) {
}
fn file_open(
&self,
_path: &str,
_mode: crate::platform::FileMode,
) -> std::result::Result<*const u8, ()> {
Ok(std::ptr::null())
}
fn file_close(&self, _handle: *const u8) -> std::result::Result<(), ()> {
Ok(())
}
fn file_write(
&self,
_handle: *const u8,
_data: *const u8,
_size: usize,
) -> std::result::Result<usize, ()> {
Ok(0)
}
fn file_read(
&self,
_handle: *const u8,
_data: *mut u8,
_size: usize,
) -> std::result::Result<usize, ()> {
Ok(0)
}
fn file_seek(
&self,
_handle: *const u8,
_offset: i64,
_whence: crate::platform::SeekWhence,
) -> std::result::Result<u64, ()> {
Ok(0)
}
fn file_remove(&self, _path: &str) -> std::result::Result<(), ()> {
Ok(())
}
fn file_size(&self, _path: &str) -> std::result::Result<usize, ()> {
Ok(0)
}
fn crc32(&self, _data: *const u8, _size: usize) -> u32 {
0
}
}
static TEST_PLATFORM: TestPlatform = TestPlatform;
static EMPTY_TABLES: &[crate::types::TableDef] = &[];
fn init_test_platform() {
crate::platform::init_platform(&TEST_PLATFORM);
crate::transaction::init_tx_manager();
let _ = crate::pubsub::shutdown();
}
fn init_test_platform_with_pubsub() {
init_test_platform();
let config = crate::pubsub::PubSubConfig::default();
let _ = crate::pubsub::init(config);
}
#[test]
fn test_role_manager() {
init_test_platform();
let role_manager = RoleManager::new(HARole::Master).expect("Failed to create RoleManager");
role_manager
.init()
.expect("Failed to initialize RoleManager");
assert_eq!(role_manager.get_role(), HARole::Master);
role_manager
.set_role(HARole::Slave)
.expect("Failed to set role to Slave");
assert_eq!(role_manager.get_role(), HARole::Slave);
role_manager
.set_role(HARole::Auto)
.expect("Failed to set role to Auto");
assert_eq!(role_manager.get_role(), HARole::Auto);
assert!(!(role_manager.get_role() == HARole::Master));
assert!(!(role_manager.get_role() == HARole::Slave));
assert!(role_manager.get_role() == HARole::Auto);
role_manager
.shutdown()
.expect("Failed to shutdown RoleManager");
}
#[test]
fn test_heartbeat_monitor() {
init_test_platform();
let heartbeat_monitor =
HeartbeatMonitor::new(1000, 3000).expect("Failed to create HeartbeatMonitor");
heartbeat_monitor
.init()
.expect("Failed to initialize HeartbeatMonitor");
assert!(heartbeat_monitor.is_master_alive());
assert_eq!(heartbeat_monitor.get_last_heartbeat_time(), 123456);
heartbeat_monitor
.shutdown()
.expect("Failed to shutdown HeartbeatMonitor");
}
#[test]
fn test_heartbeat_packet_crc() {
init_test_platform();
println!(
"Size of HeartbeatPacket: {}",
core::mem::size_of::<remdb::ha::heartbeat::HeartbeatPacket>()
);
println!("Expected size without padding: {}", 8 + 8 + 1 + 4);
let packet = remdb::ha::heartbeat::HeartbeatPacket::new(123, HARole::Master);
assert!(packet.verify_crc());
assert_eq!(packet.node_id(), 123);
assert_eq!(packet.role(), HARole::Master);
let bytes = packet.to_bytes();
let parsed_packet = remdb::ha::heartbeat::HeartbeatPacket::from_bytes(&bytes);
assert!(parsed_packet.is_some());
let parsed = parsed_packet.unwrap();
assert_eq!(parsed.node_id(), 123);
assert_eq!(parsed.role(), HARole::Master);
assert!(parsed.verify_crc());
}
#[test]
fn test_heartbeat_status_check() {
init_test_platform();
let heartbeat_monitor =
HeartbeatMonitor::new(1000, 3000).expect("Failed to create HeartbeatMonitor");
heartbeat_monitor
.init()
.expect("Failed to initialize HeartbeatMonitor");
assert!(heartbeat_monitor.is_master_alive());
let result = heartbeat_monitor.check_status();
assert!(result.is_ok());
heartbeat_monitor
.shutdown()
.expect("Failed to shutdown HeartbeatMonitor");
}
#[test]
fn test_ha_manager_failover() {
init_test_platform();
static SLAVE_CONFIG: std::sync::LazyLock<config::DbConfig> =
std::sync::LazyLock::new(|| config::DbConfig {
tables: EMPTY_TABLES.to_vec(),
total_memory: 8 * 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &config::DefaultMemoryAllocator,
wal_config: WALConfig {
log_path: "./wal",
log_mode: LogMode::Async,
checkpoint_interval_ms: 60000,
log_file_size_limit: 1 * 1024 * 1024,
log_prealloc_size: 0,
log_segment_size: 1 * 1024 * 1024,
retained_checkpoints: 1,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: remdb::config::WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: config::TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: Some(HAConfig {
node_id: 2, ha_role: HARole::Slave,
replication_mode: ReplicationMode::Sync,
heartbeat_interval_ms: 1000,
failure_detection_ms: 3000,
sync_timeout_ms: 2000,
master_address: None,
master_port: None,
replication_port: 5556,
}),
model_worker_config: Default::default(),
});
let mut ha_manager = HAManager::new(&SLAVE_CONFIG).expect("Failed to create HAManager");
ha_manager.init().expect("Failed to initialize HAManager");
assert_eq!(ha_manager.get_role(), HARole::Slave);
ha_manager
.promote_to_master()
.expect("Failed to promote to master");
assert_eq!(ha_manager.get_role(), HARole::Master);
ha_manager
.demote_to_slave()
.expect("Failed to demote to slave");
assert_eq!(ha_manager.get_role(), HARole::Slave);
ha_manager.shutdown().expect("Failed to shutdown HAManager");
}
#[test]
fn test_replication_manager() {
init_test_platform();
let mut replication_manager = ReplicationManager::new(ReplicationMode::Sync)
.expect("Failed to create ReplicationManager");
replication_manager
.init()
.expect("Failed to initialize ReplicationManager");
assert_eq!(
replication_manager.get_replication_mode(),
ReplicationMode::Sync
);
replication_manager
.init_master()
.expect("Failed to initialize master");
replication_manager
.init_slave()
.expect("Failed to initialize slave");
replication_manager
.check_status()
.expect("Failed to check status");
replication_manager
.shutdown()
.expect("Failed to shutdown ReplicationManager");
}
#[test]
fn test_ha_manager() {
init_test_platform();
static EMPTY_TABLES: &[crate::types::TableDef] = &[];
static CONFIG: std::sync::LazyLock<config::DbConfig> =
std::sync::LazyLock::new(|| config::DbConfig {
tables: EMPTY_TABLES.to_vec(),
total_memory: 8 * 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &config::DefaultMemoryAllocator,
wal_config: WALConfig {
log_path: "./wal",
log_mode: LogMode::Async,
checkpoint_interval_ms: 60000,
log_file_size_limit: 1 * 1024 * 1024,
log_prealloc_size: 0,
log_segment_size: 1 * 1024 * 1024,
retained_checkpoints: 1,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: remdb::config::WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: config::TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: Some(HAConfig {
node_id: 1, ha_role: HARole::Master,
replication_mode: ReplicationMode::Sync,
heartbeat_interval_ms: 1000,
failure_detection_ms: 3000,
sync_timeout_ms: 2000,
master_address: None,
master_port: None,
replication_port: 5556,
}),
model_worker_config: Default::default(),
});
let mut ha_manager = HAManager::new(&CONFIG).expect("Failed to create HAManager");
ha_manager.init().expect("Failed to initialize HAManager");
assert_eq!(ha_manager.get_role(), HARole::Master);
assert_eq!(ha_manager.get_replication_mode(), ReplicationMode::Sync);
ha_manager
.check_status()
.expect("Failed to check HA status");
ha_manager.shutdown().expect("Failed to shutdown HAManager");
}
#[test]
fn test_ha_manager_role_switch() {
init_test_platform();
static CONFIG: std::sync::LazyLock<config::DbConfig> =
std::sync::LazyLock::new(|| config::DbConfig {
tables: EMPTY_TABLES.to_vec(),
total_memory: 8 * 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &config::DefaultMemoryAllocator,
wal_config: WALConfig {
log_path: "./wal",
log_mode: LogMode::Async,
checkpoint_interval_ms: 60000,
log_file_size_limit: 1 * 1024 * 1024,
log_prealloc_size: 0,
log_segment_size: 1 * 1024 * 1024,
retained_checkpoints: 1,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: remdb::config::WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: config::TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: Some(HAConfig {
node_id: 1, ha_role: HARole::Master,
replication_mode: ReplicationMode::Sync,
heartbeat_interval_ms: 1000,
failure_detection_ms: 3000,
sync_timeout_ms: 2000,
master_address: None,
master_port: None,
replication_port: 5556,
}),
model_worker_config: Default::default(),
});
let mut ha_manager = HAManager::new(&CONFIG).expect("Failed to create HAManager");
ha_manager.init().expect("Failed to initialize HAManager");
assert_eq!(ha_manager.get_role(), HARole::Master);
ha_manager
.demote_to_slave()
.expect("Failed to demote to slave");
assert_eq!(ha_manager.get_role(), HARole::Slave);
ha_manager
.promote_to_master()
.expect("Failed to promote to master");
assert_eq!(ha_manager.get_role(), HARole::Master);
ha_manager.shutdown().expect("Failed to shutdown HAManager");
}
#[test]
fn test_ha_errors() {
assert_eq!(
format!("{}", HAError::InitFailed),
"HA initialization failed"
);
assert_eq!(format!("{}", HAError::NetworkError), "Network error");
assert_eq!(
format!("{}", HAError::InvalidParameter),
"Invalid parameter"
);
assert_eq!(format!("{}", HAError::RoleConflict), "Role conflict");
assert_eq!(format!("{}", HAError::SyncFailed), "Sync failed");
assert_eq!(
format!("{}", HAError::HeartbeatTimeout),
"Heartbeat timeout"
);
assert_eq!(
format!("{}", HAError::ReplicationError),
"Replication error"
);
assert_eq!(
format!("{}", HAError::UnsupportedOperation),
"Unsupported operation"
);
}
#[test]
fn test_ha_config_validation() {
init_test_platform();
let invalid_config = config::DbConfig {
tables: EMPTY_TABLES.to_vec(),
total_memory: 8 * 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &config::DefaultMemoryAllocator,
wal_config: WALConfig {
log_path: "./wal",
log_mode: LogMode::Async,
checkpoint_interval_ms: 60000,
log_file_size_limit: 1 * 1024 * 1024,
log_prealloc_size: 0,
log_segment_size: 1 * 1024 * 1024,
retained_checkpoints: 1,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: remdb::config::WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: config::TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: Some(HAConfig {
node_id: 1, ha_role: HARole::Master,
replication_mode: ReplicationMode::Sync,
heartbeat_interval_ms: 50, failure_detection_ms: 3000,
sync_timeout_ms: 2000,
master_address: None,
master_port: None,
replication_port: 5556,
}),
model_worker_config: Default::default(),
};
assert!(!config::validate_config(&invalid_config));
let valid_config = config::DbConfig {
tables: EMPTY_TABLES.to_vec(),
total_memory: 8 * 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &config::DefaultMemoryAllocator,
wal_config: WALConfig {
log_path: "./wal",
log_mode: LogMode::Async,
checkpoint_interval_ms: 60000,
log_file_size_limit: 1 * 1024 * 1024,
log_prealloc_size: 0,
log_segment_size: 1 * 1024 * 1024,
retained_checkpoints: 1,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: remdb::config::WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: config::TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: Some(HAConfig {
node_id: 1, ha_role: HARole::Master,
replication_mode: ReplicationMode::Sync,
heartbeat_interval_ms: 1000, failure_detection_ms: 3000, sync_timeout_ms: 2000, master_address: None,
master_port: None,
replication_port: 5556,
}),
model_worker_config: Default::default(),
};
assert!(config::validate_config(&valid_config));
}
#[test]
fn test_sync_request_encode_decode_full() {
let request = remdb::ha::protocol::SyncRequest::new_full(42);
let encoded = request.encode();
let decoded = remdb::ha::protocol::SyncRequest::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.slave_id, 42);
assert_eq!(decoded.sync_type, remdb::ha::protocol::SyncType::Full);
assert_eq!(decoded.last_log_index, 0);
assert_eq!(encoded.len(), 2);
}
#[test]
fn test_sync_request_encode_decode_incremental() {
let request = remdb::ha::protocol::SyncRequest::new_incremental(7, 123456);
let encoded = request.encode();
let decoded = remdb::ha::protocol::SyncRequest::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.slave_id, 7);
assert_eq!(
decoded.sync_type,
remdb::ha::protocol::SyncType::Incremental
);
assert_eq!(decoded.last_log_index, 123456);
assert_eq!(encoded.len(), 6);
}
#[test]
fn test_sync_request_decode_invalid() {
let empty_data: &[u8] = &[];
assert!(remdb::ha::protocol::SyncRequest::decode(empty_data).is_none());
let short_data: &[u8] = &[1];
assert!(remdb::ha::protocol::SyncRequest::decode(short_data).is_none());
let incremental_short: &[u8] = &[1, 1];
assert!(remdb::ha::protocol::SyncRequest::decode(incremental_short).is_none());
}
#[test]
fn test_sync_data_begin_encode_decode_snapshot() {
let begin = remdb::ha::protocol::SyncDataBegin::new_snapshot(1024 * 1024 * 10, 200, 8);
let encoded = begin.encode();
let decoded = remdb::ha::protocol::SyncDataBegin::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.sync_type, remdb::ha::protocol::SyncType::Full);
assert_eq!(decoded.total_size, 1024 * 1024 * 10);
assert_eq!(decoded.chunk_count, 200);
assert_eq!(decoded.table_count, 8);
assert_eq!(decoded.log_count, 0);
}
#[test]
fn test_sync_data_begin_encode_decode_wal() {
let begin = remdb::ha::protocol::SyncDataBegin::new_wal(50000, 10, 100);
let encoded = begin.encode();
let decoded = remdb::ha::protocol::SyncDataBegin::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(
decoded.sync_type,
remdb::ha::protocol::SyncType::Incremental
);
assert_eq!(decoded.total_size, 50000);
assert_eq!(decoded.chunk_count, 10);
assert_eq!(decoded.table_count, 0);
assert_eq!(decoded.log_count, 100);
}
#[test]
fn test_sync_data_begin_decode_invalid() {
let empty_data: &[u8] = &[];
assert!(remdb::ha::protocol::SyncDataBegin::decode(empty_data).is_none());
let short_data: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17];
assert!(remdb::ha::protocol::SyncDataBegin::decode(short_data).is_none());
}
#[test]
fn test_sync_data_chunk_encode_decode() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let chunk = remdb::ha::protocol::SyncDataChunk::new(100, &data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.chunk_index, 100);
assert_eq!(decoded.data_size, 10);
assert_eq!(decoded.data, data);
}
#[test]
fn test_sync_data_chunk_large_data() {
let data: Vec<u8> = (0..=255).cycle().take(50000).collect();
let chunk = remdb::ha::protocol::SyncDataChunk::new(0, &data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.data.len(), 50000);
assert_eq!(decoded.data, data);
}
#[test]
fn test_sync_data_chunk_decode_invalid() {
let empty_data: &[u8] = &[];
assert!(remdb::ha::protocol::SyncDataChunk::decode(empty_data).is_none());
let short_data: &[u8] = &[0, 1, 2, 3, 4];
assert!(remdb::ha::protocol::SyncDataChunk::decode(short_data).is_none());
}
#[test]
fn test_sync_data_end_encode_decode() {
let end = remdb::ha::protocol::SyncDataEnd::new(150, 0xABCD1234);
let encoded = end.encode();
let decoded = remdb::ha::protocol::SyncDataEnd::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.total_chunks, 150);
assert_eq!(decoded.checksum, 0xABCD1234);
}
#[test]
fn test_sync_data_end_decode_invalid() {
let empty_data: &[u8] = &[];
assert!(remdb::ha::protocol::SyncDataEnd::decode(empty_data).is_none());
let short_data: &[u8] = &[0, 1, 2, 3, 4, 5, 7];
assert!(remdb::ha::protocol::SyncDataEnd::decode(short_data).is_none());
}
#[test]
fn test_sync_ack_encode_decode_success() {
let ack = remdb::ha::protocol::SyncAck::new(5, true, 100);
let encoded = ack.encode();
let decoded = remdb::ha::protocol::SyncAck::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.slave_id, 5);
assert!(decoded.success);
assert_eq!(decoded.chunks_received, 100);
}
#[test]
fn test_sync_ack_encode_decode_failure() {
let ack = remdb::ha::protocol::SyncAck::new(10, false, 50);
let encoded = ack.encode();
let decoded = remdb::ha::protocol::SyncAck::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.slave_id, 10);
assert!(!decoded.success);
assert_eq!(decoded.chunks_received, 50);
}
#[test]
fn test_sync_ack_decode_invalid() {
let empty_data: &[u8] = &[];
assert!(remdb::ha::protocol::SyncAck::decode(empty_data).is_none());
let short_data: &[u8] = &[0, 1, 2, 3, 5];
assert!(remdb::ha::protocol::SyncAck::decode(short_data).is_none());
}
#[test]
fn test_sync_type_from_u8() {
assert_eq!(
remdb::ha::protocol::SyncType::from(0),
remdb::ha::protocol::SyncType::Full
);
assert_eq!(
remdb::ha::protocol::SyncType::from(1),
remdb::ha::protocol::SyncType::Incremental
);
assert_eq!(
remdb::ha::protocol::SyncType::from(255),
remdb::ha::protocol::SyncType::Full
);
}
#[test]
fn test_sync_handler_creation() {
init_test_platform();
let handler = remdb::ha::sync_handler::SyncHandler::new();
assert_eq!(
handler.get_state(),
remdb::ha::sync_handler::SyncHandlerState::Idle
);
}
#[test]
fn test_sync_handler_default() {
init_test_platform();
let handler = remdb::ha::sync_handler::SyncHandler::default();
assert_eq!(
handler.get_state(),
remdb::ha::sync_handler::SyncHandlerState::Idle
);
}
#[test]
fn test_sync_handler_shutdown() {
init_test_platform();
let mut handler = remdb::ha::sync_handler::SyncHandler::new();
let result = handler.shutdown();
assert!(result.is_ok());
assert_eq!(
handler.get_state(),
remdb::ha::sync_handler::SyncHandlerState::Idle
);
}
#[test]
fn test_sync_receiver_creation() {
init_test_platform();
let receiver = remdb::ha::sync_receiver::SyncReceiver::new(1);
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Idle);
}
#[test]
fn test_sync_receiver_default() {
init_test_platform();
let receiver = remdb::ha::sync_receiver::SyncReceiver::default();
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Idle);
}
#[test]
fn test_sync_receiver_start_sync() {
init_test_platform();
let mut receiver = remdb::ha::sync_receiver::SyncReceiver::new(1);
let result = receiver.start_sync();
assert!(result.is_ok());
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Syncing);
}
#[test]
fn test_sync_receiver_shutdown() {
init_test_platform();
let mut receiver = remdb::ha::sync_receiver::SyncReceiver::new(1);
let result = receiver.shutdown();
assert!(result.is_ok());
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Idle);
}
#[test]
fn test_sync_handler_state_conversion() {
use remdb::ha::sync_handler::SyncHandlerState;
use remdb::ha::SyncState;
assert_eq!(SyncState::from(SyncHandlerState::Idle), SyncState::Idle);
assert_eq!(
SyncState::from(SyncHandlerState::Syncing),
SyncState::Syncing
);
assert_eq!(
SyncState::from(SyncHandlerState::Completed),
SyncState::Synced
);
assert_eq!(SyncState::from(SyncHandlerState::Failed), SyncState::Failed);
}
#[test]
fn test_master_slave_sync_request_flow() {
init_test_platform_with_pubsub();
let mut replication_manager = ReplicationManager::new(ReplicationMode::Sync)
.expect("Failed to create ReplicationManager");
replication_manager.init().expect("Failed to init");
let result = replication_manager.request_full_sync();
if result.is_err() {
println!(
"Note: request_full_sync failed (expected in test env without network): {:?}",
result
);
}
replication_manager.shutdown().expect("Failed to shutdown");
}
#[test]
fn test_master_slave_incremental_sync_request() {
init_test_platform_with_pubsub();
let mut replication_manager = ReplicationManager::new(ReplicationMode::Async)
.expect("Failed to create ReplicationManager");
replication_manager.init().expect("Failed to init");
let result = replication_manager.request_incremental_sync(1000);
if result.is_err() {
println!(
"Note: request_incremental_sync failed (expected in test env without network): {:?}",
result
);
}
replication_manager.shutdown().expect("Failed to shutdown");
}
#[test]
fn test_sync_state_transitions() {
init_test_platform();
let mut receiver = remdb::ha::sync_receiver::SyncReceiver::new(1);
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Idle);
receiver.start_sync().expect("Failed to start sync");
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Syncing);
receiver.shutdown().expect("Failed to shutdown");
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Idle);
}
#[test]
fn test_sync_state_from_u32() {
use remdb::ha::SyncState;
assert_eq!(SyncState::from(0), SyncState::Idle);
assert_eq!(SyncState::from(1), SyncState::Syncing);
assert_eq!(SyncState::from(2), SyncState::Synced);
assert_eq!(SyncState::from(3), SyncState::Failed);
assert_eq!(SyncState::from(999), SyncState::Idle);
}
#[test]
fn test_sync_small_data_chunk() {
let small_data: Vec<u8> = vec![1, 2, 3];
let chunk = remdb::ha::protocol::SyncDataChunk::new(0, &small_data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.data, small_data);
assert_eq!(decoded.data_size, 3);
}
#[test]
fn test_sync_medium_data_chunk() {
let medium_data: Vec<u8> = (0..=255).cycle().take(1024).collect();
let chunk = remdb::ha::protocol::SyncDataChunk::new(0, &medium_data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.data.len(), 1024);
assert_eq!(decoded.data, medium_data);
}
#[test]
fn test_sync_large_data_chunk() {
let large_data: Vec<u8> = (0..=255).cycle().take(60000).collect();
let chunk = remdb::ha::protocol::SyncDataChunk::new(0, &large_data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.data.len(), 60000);
assert_eq!(decoded.data, large_data);
}
#[test]
fn test_sync_chunk_sequence() {
let total_size = 150000usize;
let chunk_size = remdb::ha::protocol::MAX_CHUNK_DATA_SIZE;
let expected_chunks = (total_size + chunk_size - 1) / chunk_size;
let data: Vec<u8> = (0..=255).cycle().take(total_size).collect();
for i in 0..expected_chunks {
let start = i * chunk_size;
let end = std::cmp::min(start + chunk_size, total_size);
let chunk_data = &data[start..end];
let chunk = remdb::ha::protocol::SyncDataChunk::new(i as u32, chunk_data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.chunk_index, i as u32);
assert_eq!(decoded.data, chunk_data);
}
}
#[test]
fn test_sync_data_begin_large_values() {
let large_size = u64::MAX;
let large_chunk_count = u32::MAX;
let large_table_count = u8::MAX;
let begin = remdb::ha::protocol::SyncDataBegin::new_snapshot(
large_size,
large_chunk_count,
large_table_count,
);
let encoded = begin.encode();
let decoded = remdb::ha::protocol::SyncDataBegin::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.total_size, large_size);
assert_eq!(decoded.chunk_count, large_chunk_count);
assert_eq!(decoded.table_count, large_table_count);
}
#[test]
fn test_sync_data_end_large_values() {
let end = remdb::ha::protocol::SyncDataEnd::new(u32::MAX, u32::MAX);
let encoded = end.encode();
let decoded = remdb::ha::protocol::SyncDataEnd::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.total_chunks, u32::MAX);
assert_eq!(decoded.checksum, u32::MAX);
}
#[test]
fn test_sync_request_malformed_data() {
let malformed: &[u8] = &[0xFF, 0xFF];
let result = remdb::ha::protocol::SyncRequest::decode(malformed);
assert!(result.is_some());
let decoded = result.unwrap();
assert_eq!(decoded.slave_id, 0xFF);
assert_eq!(decoded.sync_type, remdb::ha::protocol::SyncType::Full);
}
#[test]
fn test_sync_data_chunk_truncated_data() {
let data = vec![1, 2, 3, 4, 5];
let chunk = remdb::ha::protocol::SyncDataChunk::new(0, &data);
let mut encoded = chunk.encode();
encoded.truncate(encoded.len() - 2);
let result = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(result.is_none());
}
#[test]
fn test_sync_receiver_timeout() {
init_test_platform();
let mut receiver = remdb::ha::sync_receiver::SyncReceiver::new(1);
receiver.start_sync().expect("Failed to start sync");
let result = receiver.wait_for_completion(100);
assert!(result.is_err());
assert_eq!(receiver.get_state(), remdb::ha::SyncState::Failed);
}
#[test]
fn test_sync_data_begin_zero_values() {
let begin = remdb::ha::protocol::SyncDataBegin::new_snapshot(0, 0, 0);
let encoded = begin.encode();
let decoded = remdb::ha::protocol::SyncDataBegin::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.total_size, 0);
assert_eq!(decoded.chunk_count, 0);
assert_eq!(decoded.table_count, 0);
}
#[test]
fn test_sync_data_chunk_empty_data() {
let empty_data: Vec<u8> = vec![];
let chunk = remdb::ha::protocol::SyncDataChunk::new(0, &empty_data);
let encoded = chunk.encode();
let decoded = remdb::ha::protocol::SyncDataChunk::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.data.len(), 0);
assert_eq!(decoded.data_size, 0);
}
#[test]
fn test_replication_manager_sync_failure_handling() {
init_test_platform();
let mut replication_manager = ReplicationManager::new(ReplicationMode::Sync)
.expect("Failed to create ReplicationManager");
replication_manager.init().expect("Failed to init");
let result = replication_manager.check_status();
assert!(result.is_ok());
replication_manager.shutdown().expect("Failed to shutdown");
}
#[test]
fn test_sync_ack_failure_response() {
let ack = remdb::ha::protocol::SyncAck::new(1, false, 0);
let encoded = ack.encode();
let decoded = remdb::ha::protocol::SyncAck::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert!(!decoded.success);
assert_eq!(decoded.chunks_received, 0);
}
#[test]
fn test_sync_data_end_zero_checksum() {
let end = remdb::ha::protocol::SyncDataEnd::new(100, 0);
let encoded = end.encode();
let decoded = remdb::ha::protocol::SyncDataEnd::decode(&encoded);
assert!(decoded.is_some());
let decoded = decoded.unwrap();
assert_eq!(decoded.checksum, 0);
}
#[test]
fn test_max_chunk_data_size_constant() {
assert_eq!(remdb::ha::protocol::MAX_CHUNK_DATA_SIZE, 60000);
}
#[test]
fn test_sync_handler_state_equality() {
use remdb::ha::sync_handler::SyncHandlerState;
assert_eq!(SyncHandlerState::Idle, SyncHandlerState::Idle);
assert_eq!(SyncHandlerState::Syncing, SyncHandlerState::Syncing);
assert_eq!(SyncHandlerState::Completed, SyncHandlerState::Completed);
assert_eq!(SyncHandlerState::Failed, SyncHandlerState::Failed);
assert_ne!(SyncHandlerState::Idle, SyncHandlerState::Syncing);
assert_ne!(SyncHandlerState::Syncing, SyncHandlerState::Completed);
}
#[test]
fn test_sync_state_equality() {
use remdb::ha::SyncState;
assert_eq!(SyncState::Idle, SyncState::Idle);
assert_eq!(SyncState::Syncing, SyncState::Syncing);
assert_eq!(SyncState::Synced, SyncState::Synced);
assert_eq!(SyncState::Failed, SyncState::Failed);
assert_ne!(SyncState::Idle, SyncState::Syncing);
assert_ne!(SyncState::Syncing, SyncState::Synced);
}