use blvm_node::rpc::blockchain::BlockchainRpc;
use blvm_node::rpc::control::ControlRpc;
use blvm_node::rpc::mining::MiningRpc;
use blvm_node::rpc::rawtx::RawTxRpc;
use serde_json::json;
use std::sync::Arc;
#[tokio::test]
async fn test_rpc_concurrent_calls() {
let blockchain = Arc::new(BlockchainRpc::new());
let mut handles = vec![];
for _ in 0..10 {
let blockchain_clone = Arc::clone(&blockchain);
handles.push(tokio::spawn(async move {
blockchain_clone.get_blockchain_info().await
}));
}
let results: Vec<_> = futures::future::join_all(handles).await;
assert_eq!(results.len(), 10);
for result in results {
let err = result.expect("task").expect_err("no chain");
assert!(err.to_string().contains("chain is not initialized"));
}
}
#[tokio::test]
async fn test_rpc_invalid_parameters() {
let blockchain = BlockchainRpc::new();
let invalid_hash = "not-a-valid-hash";
let result = blockchain.get_block(invalid_hash).await;
assert!(result.is_err());
let result = blockchain.get_block_hash(1_000_000_000).await;
let message = result.expect_err("no storage").to_string();
assert!(
message.contains("Storage not available"),
"unexpected error: {message}"
);
}
#[tokio::test]
async fn test_rpc_large_result_sets() {
let blockchain = BlockchainRpc::new();
let result = blockchain.get_blockchain_info().await;
assert!(
result
.expect_err("no chain")
.to_string()
.contains("chain is not initialized")
);
let info = blockchain.get_txoutset_info().await.expect("txoutset");
assert_eq!(info["height"], 0);
assert_eq!(info["txouts"], 0);
}
#[tokio::test]
async fn test_rpc_rate_limiting_edge_cases() {
let control = Arc::new(ControlRpc::new());
let mut handles = vec![];
for _ in 0..20 {
let control_clone = Arc::clone(&control);
handles.push(tokio::spawn(async move {
control_clone.uptime(&json!([])).await
}));
}
let results: Vec<_> = futures::future::join_all(handles).await;
assert_eq!(results.len(), 20);
}
#[tokio::test]
async fn test_rpc_mining_edge_cases() {
let mining = MiningRpc::new();
let params_empty = json!([]);
let result1 = mining.get_block_template(¶ms_empty).await;
let message = result1.expect_err("no storage").to_string();
assert!(
message.contains("getblocktemplate requires storage"),
"unexpected error: {message}"
);
let params_invalid = json!([{"invalid": "params"}]);
let result2 = mining.get_block_template(¶ms_invalid).await;
let message = result2.expect_err("no storage").to_string();
assert!(
message.contains("getblocktemplate requires storage"),
"unexpected error: {message}"
);
}
#[tokio::test]
async fn test_rpc_rawtx_edge_cases() {
let rawtx = RawTxRpc::new();
let params_invalid_hex = json!(["not-hex"]);
let result = rawtx.sendrawtransaction(¶ms_invalid_hex).await;
assert!(result.is_err());
let params_empty = json!([""]);
let result = rawtx.sendrawtransaction(¶ms_empty).await;
assert!(result.is_err());
let large_hex = "00".repeat(1000000); let params_large = json!([large_hex]);
let result = rawtx.sendrawtransaction(¶ms_large).await;
assert!(result.is_err(), "zero-filled hex was accepted: {result:?}");
}
#[tokio::test]
async fn test_rpc_control_edge_cases() {
let control = ControlRpc::new();
let params_invalid = json!(["nonexistent_command"]);
let result = control.help(¶ms_invalid).await;
let message = result.expect_err("unknown command").to_string();
assert!(
message.contains("Unknown command"),
"unexpected error: {message}"
);
let params_invalid = json!([{"invalid": "params"}]);
let logging = control.logging(¶ms_invalid).await.expect("logging");
let filter = logging["current_filter"].as_str().expect("filter");
let expect_active = filter.contains("debug") || filter.contains("trace");
assert_eq!(logging["active"], expect_active);
}
#[tokio::test]
async fn test_rpc_error_recovery() {
let blockchain = BlockchainRpc::new();
let result1 = blockchain.get_block("invalid").await;
assert!(result1.is_err());
let result2 = blockchain.get_blockchain_info().await;
assert!(
result2
.expect_err("no chain")
.to_string()
.contains("chain is not initialized")
);
}
#[tokio::test]
async fn test_rpc_concurrent_different_methods() {
let blockchain = Arc::new(BlockchainRpc::new());
let control = Arc::new(ControlRpc::new());
let mut blockchain_handles = vec![];
for _ in 0..5 {
let blockchain_clone = Arc::clone(&blockchain);
blockchain_handles.push(tokio::spawn(async move {
blockchain_clone.get_blockchain_info().await
}));
}
let mut control_handles = vec![];
for _ in 0..5 {
let control_clone = Arc::clone(&control);
control_handles.push(tokio::spawn(async move {
control_clone.uptime(&json!([])).await
}));
}
let bc_results: Vec<_> = futures::future::join_all(blockchain_handles).await;
let ctl_results: Vec<_> = futures::future::join_all(control_handles).await;
assert_eq!(bc_results.len() + ctl_results.len(), 10);
for result in bc_results {
assert!(result.is_ok());
assert!(
result
.unwrap()
.expect_err("no chain")
.to_string()
.contains("chain is not initialized")
);
}
for result in ctl_results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}
}