use aleo_rust_sdk::AleoHttpClient;
use wiremock::matchers::{body_json, method, path, path_regex};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn mock_client(base_url: &str) -> AleoHttpClient {
AleoHttpClient::new_with_rpc(base_url, base_url).unwrap()
}
fn json_rpc_result(value: serde_json::Value) -> serde_json::Value {
serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"result": value
})
}
fn json_rpc_error(msg: &str) -> serde_json::Value {
serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32000, "message": msg }
})
}
#[tokio::test]
async fn test_fetch_block_height() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("GET"))
.and(path("/block/height/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("12345"))
.mount(&mock_server)
.await;
let height = client.fetch_block_height().await.unwrap();
assert_eq!(height, 12345);
}
#[tokio::test]
async fn test_fetch_block_height_parse_error() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("GET"))
.and(path("/block/height/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("not_a_number"))
.mount(&mock_server)
.await;
assert!(client.fetch_block_height().await.is_err());
}
#[tokio::test]
async fn test_fetch_state_root_only() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
let root = "sr1lkr8fzg8mk69qrtycxjvtrg8rvh77puaq7fm56cjkh04xhprdqzq3a355s";
Mock::given(method("GET"))
.and(path("/stateRoot/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string(format!("\"{root}\"")))
.mount(&mock_server)
.await;
let result = client.fetch_state_root_only().await.unwrap();
assert_eq!(result.to_string(), root);
}
#[tokio::test]
async fn test_fetch_state_root_only_bad_response() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("GET"))
.and(path("/stateRoot/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("not a state root"))
.mount(&mock_server)
.await;
assert!(client.fetch_state_root_only().await.is_err());
}
#[tokio::test]
async fn test_fetch_state_root() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
let root = "sr1lkr8fzg8mk69qrtycxjvtrg8rvh77puaq7fm56cjkh04xhprdqzq3a355s";
Mock::given(method("GET"))
.and(path("/stateRoot/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string(format!("\"{root}\"")))
.mount(&mock_server)
.await;
Mock::given(method("GET"))
.and(path("/block/height/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("9876"))
.mount(&mock_server)
.await;
let (sr, height) = client.fetch_state_root().await.unwrap();
assert_eq!(sr.to_string(), root);
assert_eq!(height, 9876);
}
#[tokio::test]
async fn test_fetch_program_not_found() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("GET"))
.and(path_regex(r"/program/.+"))
.respond_with(ResponseTemplate::new(404).set_body_string("Not found"))
.mount(&mock_server)
.await;
assert!(client.fetch_program("nonexistent.aleo").await.is_err());
}
#[tokio::test]
async fn test_fetch_mapping_value_found() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
let expected_body = serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "getMappingValue",
"params": [
"credits.aleo",
"account",
"aleo1testtesttesttesttesttesttesttesttesttesttest"
]
});
Mock::given(method("POST"))
.and(body_json(&expected_body))
.respond_with(
ResponseTemplate::new(200).set_body_json(json_rpc_result(serde_json::json!("2000000"))),
)
.mount(&mock_server)
.await;
let val = client
.fetch_mapping_value(
"credits.aleo",
"account",
"aleo1testtesttesttesttesttesttesttesttesttesttest",
)
.await
.unwrap();
assert_eq!(val, Some("2000000".to_string()));
}
#[tokio::test]
async fn test_fetch_mapping_value_not_found() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("POST"))
.respond_with(
ResponseTemplate::new(200).set_body_json(json_rpc_error("Mapping key not found")),
)
.mount(&mock_server)
.await;
let val = client
.fetch_mapping_value("credits.aleo", "account", "ale1nonexistent")
.await
.unwrap();
assert_eq!(val, None);
}
#[tokio::test]
async fn test_fetch_mapping_value_non_string_result() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("POST"))
.respond_with(
ResponseTemplate::new(200).set_body_json(json_rpc_result(serde_json::json!(42))),
)
.mount(&mock_server)
.await;
let val = client.fetch_mapping_value("credits.aleo", "account", "anykey").await.unwrap();
assert_eq!(val, Some("42".to_string()));
}
#[tokio::test]
async fn test_fetch_records() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("GET"))
.and(path("/block/height/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("5000"))
.mount(&mock_server)
.await;
let records_body = serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "records/isOwner",
"params": ["viewkey123", 4000, 5000]
});
Mock::given(method("POST"))
.and(body_json(&records_body))
.respond_with(ResponseTemplate::new(200).set_body_json(json_rpc_result(
serde_json::json!([{"owner": "aleo1test", "value": "100"}]),
)))
.mount(&mock_server)
.await;
let result = client.fetch_records("viewkey123").await.unwrap();
assert!(result.contains("aleo1test"));
}
#[tokio::test]
async fn test_broadcast_transaction_success() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("POST"))
.and(path_regex(r"/transaction/broadcast.*"))
.respond_with(ResponseTemplate::new(200).set_body_string("at1mocktxid12345"))
.mount(&mock_server)
.await;
let tx_id = client.broadcast_transaction(r#"{"mock": "tx"}"#.to_string()).await.unwrap();
assert_eq!(tx_id, "at1mocktxid12345");
}
#[tokio::test]
async fn test_broadcast_transaction_rejected() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("POST"))
.and(path_regex(r"/transaction/broadcast.*"))
.respond_with(ResponseTemplate::new(400).set_body_string("Bad request"))
.mount(&mock_server)
.await;
let result = client.broadcast_transaction(r#"{"mock": "bad"}"#.to_string()).await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("400") || err.contains("rejected"));
}
#[tokio::test]
async fn test_wait_for_confirmation_timeout() {
let mock_server = MockServer::start().await;
let client = mock_client(&mock_server.uri());
Mock::given(method("GET"))
.and(path_regex(r"/transaction/.+"))
.respond_with(ResponseTemplate::new(404))
.mount(&mock_server)
.await;
let result = client.wait_for_confirmation("tx_never_confirms").await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Timed out"));
}
#[tokio::test]
async fn test_client_get_balance() {
let mock_server = MockServer::start().await;
use aleo_rust_sdk::AleoAccount;
use snarkvm::prelude::TestRng;
let mut rng = TestRng::default();
let account = AleoAccount::new_random(&mut rng).unwrap();
let addr = account.address_str();
let expected_rpc = serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "getMappingValue",
"params": ["credits.aleo", "account", addr]
});
Mock::given(method("POST"))
.and(body_json(&expected_rpc))
.respond_with(
ResponseTemplate::new(200).set_body_json(json_rpc_result(serde_json::json!("1000000"))),
)
.mount(&mock_server)
.await;
let mut client =
aleo_rust_sdk::AleoClient::new_with_rpc(&mock_server.uri(), &mock_server.uri()).unwrap();
client.set_account_from_private_key_str(&account.private_key_str()).unwrap();
let balance = client.get_balance().await.unwrap();
assert_eq!(balance, Some(1000000u64));
}
#[tokio::test]
async fn test_client_get_balance_no_account() {
let mock_server = MockServer::start().await;
let client =
aleo_rust_sdk::AleoClient::new_with_rpc(&mock_server.uri(), &mock_server.uri()).unwrap();
let result = client.get_balance().await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("No account set"));
}
#[tokio::test]
async fn test_client_get_block_height() {
let mock_server = MockServer::start().await;
let client =
aleo_rust_sdk::AleoClient::new_with_rpc(&mock_server.uri(), &mock_server.uri()).unwrap();
Mock::given(method("GET"))
.and(path("/block/height/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("7777"))
.mount(&mock_server)
.await;
let height = client.get_block_height().await.unwrap();
assert_eq!(height, 7777);
}
#[tokio::test]
async fn test_client_get_state_root() {
let mock_server = MockServer::start().await;
let client =
aleo_rust_sdk::AleoClient::new_with_rpc(&mock_server.uri(), &mock_server.uri()).unwrap();
let root = "sr1lkr8fzg8mk69qrtycxjvtrg8rvh77puaq7fm56cjkh04xhprdqzq3a355s";
Mock::given(method("GET"))
.and(path("/stateRoot/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string(format!("\"{root}\"")))
.mount(&mock_server)
.await;
let sr = client.get_state_root().await.unwrap();
assert_eq!(sr.to_string(), root);
}
#[tokio::test]
async fn test_client_fetch_unspent_records() {
let mock_server = MockServer::start().await;
use aleo_rust_sdk::AleoAccount;
use snarkvm::prelude::TestRng;
let mut rng = TestRng::default();
let account = AleoAccount::new_random(&mut rng).unwrap();
let addr = account.address_str();
Mock::given(method("GET"))
.and(path("/block/height/latest"))
.respond_with(ResponseTemplate::new(200).set_body_string("500"))
.mount(&mock_server)
.await;
Mock::given(method("POST"))
.respond_with(
ResponseTemplate::new(200)
.set_body_json(json_rpc_result(serde_json::json!([{"owner": addr}]))),
)
.mount(&mock_server)
.await;
let mut client =
aleo_rust_sdk::AleoClient::new_with_rpc(&mock_server.uri(), &mock_server.uri()).unwrap();
client.set_account_from_private_key_str(&account.private_key_str()).unwrap();
let result = client.fetch_unspent_records().await.unwrap();
assert!(result.contains(&addr));
}
#[tokio::test]
async fn test_client_execute_local_no_program() {
use aleo_rust_sdk::AleoAccount;
use snarkvm::prelude::TestRng;
let mut rng = TestRng::default();
let account = AleoAccount::new_random(&mut rng).unwrap();
let mut client =
aleo_rust_sdk::AleoClient::new("https://api.explorer.provable.com/v2/testnet").unwrap();
client.set_account_from_private_key_str(&account.private_key_str()).unwrap();
let result = client.execute_local(
"credits.aleo",
"transfer",
&["aleo1dest".to_string(), "100u64".to_string()],
);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.contains("Authorization")
|| err.contains("No program")
|| err.contains("program")
|| err.contains("load"),
"Expected an error, got: {err}"
);
}
#[tokio::test]
async fn test_client_execute_local_no_account() {
let mut client =
aleo_rust_sdk::AleoClient::new("https://api.explorer.provable.com/v2/testnet").unwrap();
client.set_program(aleo_rust_sdk::AleoProgram::credits().unwrap());
let result = client.execute_local(
"credits.aleo",
"transfer",
&["aleo1dest".to_string(), "100u64".to_string()],
);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.contains("No account set"),
"Expected 'No account set' error, got: {err}"
);
}