#![cfg(all(feature = "ctv", feature = "bip70-http", feature = "rest-api"))]
use blvm_node::config::PaymentConfig;
use blvm_node::payment::processor::PaymentProcessor;
use blvm_node::payment::state_machine::PaymentStateMachine;
#[cfg(feature = "rest-api")]
use blvm_node::rpc::rest::congestion::handle_congestion_request;
#[cfg(feature = "rest-api")]
use blvm_node::rpc::rest::pool::handle_pool_request;
#[cfg(feature = "rest-api")]
use blvm_node::rpc::rest::vault::handle_vault_request;
use blvm_node::storage::Storage;
use bytes::Bytes;
use http_body_util::BodyExt;
use http_body_util::Full;
use hyper::{Method, Response, StatusCode};
use serde_json::json;
use std::sync::Arc;
use tempfile::TempDir;
use uuid::Uuid;
fn create_test_state_machine() -> Arc<PaymentStateMachine> {
let config = PaymentConfig::default();
let processor =
Arc::new(PaymentProcessor::new(config).expect("Failed to create payment processor"));
Arc::new(PaymentStateMachine::new(processor))
}
fn create_test_state_machine_with_storage() -> (Arc<PaymentStateMachine>, TempDir) {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path();
let storage = Storage::new(storage_path).expect("Failed to create storage");
let storage_arc = Arc::new(storage);
let config = PaymentConfig::default();
let processor =
Arc::new(PaymentProcessor::new(config).expect("Failed to create payment processor"));
let state_machine = Arc::new(
PaymentStateMachine::with_storage(processor, Some(storage_arc.clone()))
.with_congestion_manager(
None,
Some(storage_arc),
blvm_node::payment::congestion::BatchConfig::default(),
),
);
(state_machine, temp_dir)
}
async fn extract_response_body(
response: Response<Full<Bytes>>,
) -> Result<serde_json::Value, Box<dyn std::error::Error + Send + Sync>> {
let (_, body) = response.into_parts();
let body_bytes = body.collect().await?.to_bytes();
let json: serde_json::Value = serde_json::from_slice(&body_bytes)?;
Ok(json)
}
#[tokio::test]
async fn test_rest_create_vault() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let body = Some(json!({
"vault_id": "test_vault_rest_1",
"deposit_amount": 100000,
"withdrawal_script": "5176a914000000000000000000000000000000000000000087"
}));
let response = handle_vault_request(
state_machine,
&Method::POST,
"/api/v1/vaults",
body,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["vault_id"], "test_vault_rest_1");
}
#[tokio::test]
async fn test_rest_create_vault_missing_fields() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let body = Some(json!({
"vault_id": "test_vault_rest_2"
}));
let response = handle_vault_request(
state_machine,
&Method::POST,
"/api/v1/vaults",
body,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[tokio::test]
async fn test_rest_unvault_vault() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let create_body = Some(json!({
"vault_id": "test_vault_rest_3",
"deposit_amount": 100000,
"withdrawal_script": "5176a914000000000000000000000000000000000000000087",
"config": {
"require_unvault": true
}
}));
handle_vault_request(
Arc::clone(&state_machine),
&Method::POST,
"/api/v1/vaults",
create_body,
request_id.clone(),
)
.await;
let unvault_body = Some(json!({
"unvault_script": "5276a914000000000000000000000000000000000000000087"
}));
let response = handle_vault_request(
state_machine,
&Method::POST,
"/api/v1/vaults/test_vault_rest_3/unvault",
unvault_body,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["vault_id"], "test_vault_rest_3");
}
#[tokio::test]
async fn test_rest_get_vault_state() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let create_body = Some(json!({
"vault_id": "test_vault_rest_4",
"deposit_amount": 100000,
"withdrawal_script": "5176a914000000000000000000000000000000000000000087"
}));
handle_vault_request(
Arc::clone(&state_machine),
&Method::POST,
"/api/v1/vaults",
create_body,
request_id.clone(),
)
.await;
let response = handle_vault_request(
state_machine,
&Method::GET,
"/api/v1/vaults/test_vault_rest_4",
None,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["vault_id"], "test_vault_rest_4");
}
#[tokio::test]
async fn test_rest_create_pool() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let body = Some(json!({
"pool_id": "test_pool_rest_1",
"initial_participants": [
{
"participant_id": "participant_1",
"contribution": 10000,
"script_pubkey": "5176a914000000000000000000000000000000000000000087"
},
{
"participant_id": "participant_2",
"contribution": 20000,
"script_pubkey": "5276a914000000000000000000000000000000000000000087"
}
]
}));
let response = handle_pool_request(
state_machine,
&Method::POST,
"/api/v1/pools",
body,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["pool_id"], "test_pool_rest_1");
}
#[tokio::test]
async fn test_rest_join_pool() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let create_body = Some(json!({
"pool_id": "test_pool_rest_2",
"initial_participants": [
{
"participant_id": "participant_1",
"contribution": 10000,
"script_pubkey": "5176a914000000000000000000000000000000000000000087"
}
]
}));
handle_pool_request(
Arc::clone(&state_machine),
&Method::POST,
"/api/v1/pools",
create_body,
request_id.clone(),
)
.await;
let join_body = Some(json!({
"participant_id": "participant_2",
"contribution": 20000,
"script_pubkey": "5276a914000000000000000000000000000000000000000087"
}));
let response = handle_pool_request(
state_machine,
&Method::POST,
"/api/v1/pools/test_pool_rest_2/join",
join_body,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["pool_id"], "test_pool_rest_2");
}
#[tokio::test]
async fn test_rest_get_pool_state() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let create_body = Some(json!({
"pool_id": "test_pool_rest_3",
"initial_participants": [
{
"participant_id": "participant_1",
"contribution": 10000,
"script_pubkey": "5176a914000000000000000000000000000000000000000087"
}
]
}));
handle_pool_request(
Arc::clone(&state_machine),
&Method::POST,
"/api/v1/pools",
create_body,
request_id.clone(),
)
.await;
let response = handle_pool_request(
state_machine,
&Method::GET,
"/api/v1/pools/test_pool_rest_3",
None,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["pool_id"], "test_pool_rest_3");
}
#[tokio::test]
async fn test_rest_create_batch() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let body = Some(json!({
"batch_id": "test_batch_rest_1",
"target_fee_rate": 10
}));
let response = handle_congestion_request(
state_machine,
&Method::POST,
"/api/v1/batches",
body,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["batch_id"], "test_batch_rest_1");
}
#[tokio::test]
async fn test_rest_get_batch_state() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let create_body = Some(json!({
"batch_id": "test_batch_rest_2"
}));
handle_congestion_request(
Arc::clone(&state_machine),
&Method::POST,
"/api/v1/batches",
create_body,
request_id.clone(),
)
.await;
let response = handle_congestion_request(
state_machine,
&Method::GET,
"/api/v1/batches/test_batch_rest_2",
None,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::OK);
let body_json = extract_response_body(response).await.unwrap();
assert_eq!(body_json["data"]["batch_id"], "test_batch_rest_2");
}
#[tokio::test]
async fn test_rest_get_congestion_metrics() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let request_id = Uuid::new_v4().to_string();
let response = handle_congestion_request(
state_machine,
&Method::GET,
"/api/v1/congestion",
None,
request_id.clone(),
)
.await;
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}