#[cfg(feature = "rocksdb")]
mod bitcoin_core_tests {
use blvm_node::config::StorageConfig;
use blvm_node::storage::bitcoin_core_blocks::BitcoinCoreBlockReader;
use blvm_node::storage::bitcoin_core_format::{
convert_key, get_key_prefix, parse_block_index, parse_coin,
};
use blvm_node::storage::bitcoin_core_storage::BitcoinCoreStorage;
use blvm_node::storage::bitcoin_detection::{BitcoinCoreDetection, CoreDataNetwork};
use std::fs::{create_dir_all, File};
use std::io::Write;
use tempfile::TempDir;
#[test]
fn test_bitcoin_core_detection_paths() {
let result = BitcoinCoreDetection::detect_data_dir(CoreDataNetwork::Mainnet);
assert!(result.is_ok());
}
#[test]
fn test_bitcoin_core_network_detection() {
let temp_dir = TempDir::new().unwrap();
let mainnet_path = temp_dir.path().join(".bitcoin");
create_dir_all(&mainnet_path).unwrap();
let detected = BitcoinCoreDetection::detect_network(&mainnet_path);
assert_eq!(detected, Some(CoreDataNetwork::Mainnet));
let testnet_path = temp_dir.path().join("testnet3");
create_dir_all(&testnet_path).unwrap();
let detected = BitcoinCoreDetection::detect_network(&testnet_path);
assert_eq!(detected, Some(CoreDataNetwork::Testnet));
}
#[test]
fn test_key_conversion() {
let coin_key = b"c\x01\x02\x03";
let converted = convert_key(coin_key).unwrap();
assert_eq!(converted, b"\x01\x02\x03");
let block_key = b"b\x04\x05\x06";
let converted = convert_key(block_key).unwrap();
assert_eq!(converted, b"\x04\x05\x06");
}
#[test]
fn test_get_key_prefix() {
let coin_key = b"c\x01\x02\x03";
assert_eq!(get_key_prefix(coin_key), Some(b'c'));
let block_key = b"b\x04\x05\x06";
assert_eq!(get_key_prefix(block_key), Some(b'b'));
let empty_key = b"";
assert_eq!(get_key_prefix(empty_key), None);
}
#[test]
fn test_parse_coin_simple() {
let mut data = Vec::new();
data.push(0x00);
data.push(0x06);
data.extend_from_slice(b"script");
data.extend_from_slice(&1000000u64.to_le_bytes());
data.extend_from_slice(&100u32.to_le_bytes());
data.push(0x01);
let coin = parse_coin(&data).unwrap();
assert_eq!(coin.amount, 1000000);
assert_eq!(coin.height, 100);
assert!(coin.is_coinbase);
assert_eq!(coin.script, b"script");
}
#[test]
fn test_parse_block_index() {
let mut data = vec![0u8; 104];
data[0..4].copy_from_slice(&100u32.to_le_bytes());
data[4..8].copy_from_slice(&1u32.to_le_bytes());
data[8..12].copy_from_slice(&10u32.to_le_bytes());
data[12..16].copy_from_slice(&0u32.to_le_bytes());
data[16..20].copy_from_slice(&0u32.to_le_bytes());
data[20..24].copy_from_slice(&0u32.to_le_bytes());
data[24..28].copy_from_slice(&1u32.to_le_bytes());
data[28..60].copy_from_slice(&[0u8; 32]);
data[60..92].copy_from_slice(&[1u8; 32]);
data[92..96].copy_from_slice(&1234567890u32.to_le_bytes());
data[96..100].copy_from_slice(&0x1d00ffffu32.to_le_bytes());
data[100..104].copy_from_slice(&12345u32.to_le_bytes());
let block_index = parse_block_index(&data).unwrap();
assert_eq!(block_index.height, 100);
assert_eq!(block_index.n_tx, 10);
assert_eq!(block_index.n_time, 1234567890);
assert_eq!(block_index.n_bits, 0x1d00ffff);
assert_eq!(block_index.n_nonce, 12345);
}
#[test]
fn test_block_file_reader_with_cache() {
let temp_dir = TempDir::new().unwrap();
let blocks_dir = temp_dir.path().join("blocks");
create_dir_all(&blocks_dir).unwrap();
let file_path = blocks_dir.join("blk00000.dat");
let mut file = File::create(&file_path).unwrap();
file.write_all(&[0xF9, 0xBE, 0xB4, 0xD9]).unwrap();
file.write_all(&80u32.to_le_bytes()).unwrap();
file.write_all(&[0u8; 80]).unwrap();
let cache_dir = temp_dir.path().join("cache");
let reader = BitcoinCoreBlockReader::new_with_cache(
&blocks_dir,
CoreDataNetwork::Mainnet,
Some(&cache_dir),
);
assert!(reader.is_ok());
let reader = reader.unwrap();
let count = reader.block_count().unwrap();
assert!(count > 0);
let count2 = reader.block_count().unwrap();
assert_eq!(count, count2);
let cache_file = cache_dir.join("block_index_mainnet.bin");
assert!(cache_file.exists());
}
#[test]
fn test_block_file_reader_index_persistence() {
let temp_dir = TempDir::new().unwrap();
let blocks_dir = temp_dir.path().join("blocks");
create_dir_all(&blocks_dir).unwrap();
let file_path = blocks_dir.join("blk00000.dat");
let mut file = File::create(&file_path).unwrap();
file.write_all(&[0xF9, 0xBE, 0xB4, 0xD9]).unwrap();
file.write_all(&80u32.to_le_bytes()).unwrap();
file.write_all(&[0u8; 80]).unwrap();
let cache_dir = temp_dir.path().join("cache");
let reader1 = BitcoinCoreBlockReader::new_with_cache(
&blocks_dir,
CoreDataNetwork::Mainnet,
Some(&cache_dir),
)
.unwrap();
let count1 = reader1.block_count().unwrap();
let reader2 = BitcoinCoreBlockReader::new_with_cache(
&blocks_dir,
CoreDataNetwork::Mainnet,
Some(&cache_dir),
)
.unwrap();
let count2 = reader2.block_count().unwrap();
assert_eq!(count1, count2);
}
#[test]
fn test_ensure_not_locked_rejects_live_pid_file() {
let temp_dir = TempDir::new().unwrap();
let pid = std::process::id();
std::fs::write(temp_dir.path().join("bitcoind.pid"), pid.to_string()).unwrap();
assert!(BitcoinCoreStorage::ensure_not_locked(temp_dir.path()).is_err());
}
#[test]
fn test_ensure_not_locked_ignores_stale_pid_and_lock_files() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("bitcoind.pid"), b"99999999").unwrap();
std::fs::create_dir_all(temp_dir.path().join("chainstate")).unwrap();
std::fs::write(temp_dir.path().join("chainstate/LOCK"), b"").unwrap();
assert!(BitcoinCoreStorage::ensure_not_locked(temp_dir.path()).is_ok());
}
#[test]
fn test_storage_config_auto_migrate_env_override() {
let cfg = StorageConfig {
auto_migrate_core: true,
..Default::default()
};
std::env::set_var("BLVM_NO_AUTO_MIGRATE_CORE", "1");
assert!(!cfg.auto_migrate_core_effective());
std::env::remove_var("BLVM_NO_AUTO_MIGRATE_CORE");
std::env::set_var("BLVM_AUTO_MIGRATE_CORE", "0");
assert!(!cfg.auto_migrate_core_effective());
std::env::remove_var("BLVM_AUTO_MIGRATE_CORE");
}
#[test]
fn test_storage_config_reuse_core_block_files_env() {
let cfg = StorageConfig {
reuse_core_block_files: false,
..Default::default()
};
std::env::set_var("BLVM_REUSE_CORE_BLOCK_FILES", "1");
assert!(cfg.reuse_core_block_files_effective());
std::env::remove_var("BLVM_REUSE_CORE_BLOCK_FILES");
}
#[test]
fn test_is_core_layout_requires_chainstate_and_blocks() {
let temp = TempDir::new().unwrap();
assert!(!BitcoinCoreDetection::is_core_layout_at(temp.path()));
create_dir_all(temp.path().join("blocks")).unwrap();
assert!(!BitcoinCoreDetection::is_core_layout_at(temp.path()));
}
}
#[cfg(not(feature = "rocksdb"))]
mod bitcoin_core_tests {
#[test]
fn test_bitcoin_core_not_available() {
use blvm_node::storage::bitcoin_core_detection::CoreDataNetwork;
use blvm_node::storage::bitcoin_core_storage::BitcoinCoreStorage;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let result = BitcoinCoreStorage::open_bitcoin_core_database(
temp_dir.path(),
CoreDataNetwork::Mainnet,
);
assert!(result.is_err());
}
}