tests(grpc): Unit tests (#8293)
* add grpc method unit tests * add test module description * runs tests in sequence, adds some messages to assertions, and minor cleanups (#8296) * fix field name change --------- Co-authored-by: Arya <aryasolhi@gmail.com>
This commit is contained in:
parent
a7de137a7a
commit
2f0b284997
|
@ -22,6 +22,9 @@ use crate::scanner::{
|
||||||
|
|
||||||
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
/// The maximum number of keys that can be requested in a single request.
|
||||||
|
pub const MAX_KEYS_PER_REQUEST: usize = 10;
|
||||||
|
|
||||||
/// The maximum number of messages that can be queued to be streamed to a client
|
/// The maximum number of messages that can be queued to be streamed to a client
|
||||||
/// from the `scan` method.
|
/// from the `scan` method.
|
||||||
const SCAN_RESPONDER_BUFFER_SIZE: usize = 10_000;
|
const SCAN_RESPONDER_BUFFER_SIZE: usize = 10_000;
|
||||||
|
@ -174,13 +177,26 @@ where
|
||||||
&self,
|
&self,
|
||||||
request: Request<RegisterKeysRequest>,
|
request: Request<RegisterKeysRequest>,
|
||||||
) -> Result<Response<RegisterKeysResponse>, Status> {
|
) -> Result<Response<RegisterKeysResponse>, Status> {
|
||||||
let keys = request
|
let keys: Vec<_> = request
|
||||||
.into_inner()
|
.into_inner()
|
||||||
.keys
|
.keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|key_with_height| (key_with_height.key, key_with_height.height))
|
.map(|key_with_height| (key_with_height.key, key_with_height.height))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
if keys.is_empty() {
|
||||||
|
let msg = "must provide at least 1 key for which to register keys";
|
||||||
|
return Err(Status::invalid_argument(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
if keys.len() > MAX_KEYS_PER_REQUEST {
|
||||||
|
let msg = format!(
|
||||||
|
"must provide at most {} keys to register keys",
|
||||||
|
MAX_KEYS_PER_REQUEST
|
||||||
|
);
|
||||||
|
return Err(Status::invalid_argument(msg));
|
||||||
|
}
|
||||||
|
|
||||||
let ScanServiceResponse::RegisteredKeys(keys) = self
|
let ScanServiceResponse::RegisteredKeys(keys) = self
|
||||||
.scan_service
|
.scan_service
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -208,6 +224,14 @@ where
|
||||||
return Err(Status::invalid_argument(msg));
|
return Err(Status::invalid_argument(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keys.len() > MAX_KEYS_PER_REQUEST {
|
||||||
|
let msg = format!(
|
||||||
|
"must provide at most {} keys to clear results",
|
||||||
|
MAX_KEYS_PER_REQUEST
|
||||||
|
);
|
||||||
|
return Err(Status::invalid_argument(msg));
|
||||||
|
}
|
||||||
|
|
||||||
let ScanServiceResponse::ClearedResults = self
|
let ScanServiceResponse::ClearedResults = self
|
||||||
.scan_service
|
.scan_service
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -235,6 +259,14 @@ where
|
||||||
return Err(Status::invalid_argument(msg));
|
return Err(Status::invalid_argument(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keys.len() > MAX_KEYS_PER_REQUEST {
|
||||||
|
let msg = format!(
|
||||||
|
"must provide at most {} keys to delete",
|
||||||
|
MAX_KEYS_PER_REQUEST
|
||||||
|
);
|
||||||
|
return Err(Status::invalid_argument(msg));
|
||||||
|
}
|
||||||
|
|
||||||
let ScanServiceResponse::DeletedKeys = self
|
let ScanServiceResponse::DeletedKeys = self
|
||||||
.scan_service
|
.scan_service
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -262,6 +294,14 @@ where
|
||||||
return Err(Status::invalid_argument(msg));
|
return Err(Status::invalid_argument(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keys.len() > MAX_KEYS_PER_REQUEST {
|
||||||
|
let msg = format!(
|
||||||
|
"must provide at most {} keys to get results",
|
||||||
|
MAX_KEYS_PER_REQUEST
|
||||||
|
);
|
||||||
|
return Err(Status::invalid_argument(msg));
|
||||||
|
}
|
||||||
|
|
||||||
let ScanServiceResponse::Results(response) = self
|
let ScanServiceResponse::Results(response) = self
|
||||||
.scan_service
|
.scan_service
|
||||||
.clone()
|
.clone()
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
mod snapshot;
|
mod snapshot;
|
||||||
|
mod vectors;
|
||||||
|
|
|
@ -0,0 +1,582 @@
|
||||||
|
//! Unit tests for gRPC methods
|
||||||
|
use std::{collections::BTreeMap, thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use tonic::transport::Channel;
|
||||||
|
|
||||||
|
use zebra_chain::{block::Height, parameters::Network, transaction};
|
||||||
|
use zebra_test::{
|
||||||
|
mock_service::{MockService, PanicAssertion},
|
||||||
|
net::random_known_port,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
scanner::{
|
||||||
|
scanner_client::ScannerClient, ClearResultsRequest, DeleteKeysRequest, Empty,
|
||||||
|
GetResultsRequest, GetResultsResponse, InfoReply, KeyWithHeight, RegisterKeysRequest,
|
||||||
|
RegisterKeysResponse,
|
||||||
|
},
|
||||||
|
server::{init, MAX_KEYS_PER_REQUEST},
|
||||||
|
};
|
||||||
|
use zebra_node_services::scan_service::{
|
||||||
|
request::Request as ScanRequest, response::Response as ScanResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The extended Sapling viewing key of [ZECpages](https://zecpages.com/boardinfo)
|
||||||
|
pub const ZECPAGES_SAPLING_VIEWING_KEY: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz";
|
||||||
|
|
||||||
|
/// Test the gRPC methods with mocked responses
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_grpc_methods_mocked() {
|
||||||
|
let _init_guard = zebra_test::init();
|
||||||
|
|
||||||
|
let (client, mock_scan_service) = start_server_and_get_client(random_known_port()).await;
|
||||||
|
|
||||||
|
test_get_results_errors(client.clone()).await;
|
||||||
|
test_register_keys_errors(client.clone()).await;
|
||||||
|
test_clear_results_errors(client.clone()).await;
|
||||||
|
test_delete_keys_errors(client.clone()).await;
|
||||||
|
|
||||||
|
for network in Network::iter() {
|
||||||
|
test_mocked_getinfo_for_network(&client, &mock_scan_service, network).await;
|
||||||
|
test_mocked_getresults_for_network(&client, &mock_scan_service, network).await;
|
||||||
|
test_mocked_register_keys_for_network(&client, &mock_scan_service, network).await;
|
||||||
|
test_mocked_clear_results_for_network(&client, &mock_scan_service, network).await;
|
||||||
|
test_mocked_delete_keys_for_network(&client, &mock_scan_service, network).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the `get_info` gRPC method with a mocked service response.
|
||||||
|
async fn test_mocked_getinfo_for_network(
|
||||||
|
client: &ScannerClient<Channel>,
|
||||||
|
mock_scan_service: &MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) {
|
||||||
|
// create request, fake results and get response
|
||||||
|
let get_info_response = call_get_info(client.clone(), mock_scan_service.clone(), network).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
assert_eq!(
|
||||||
|
get_info_response.into_inner().min_sapling_birthday_height,
|
||||||
|
network.sapling_activation_height().0,
|
||||||
|
"get_info response min sapling height should match network sapling activation height"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the `get_results` gRPC method with populated and empty results
|
||||||
|
async fn test_mocked_getresults_for_network(
|
||||||
|
client: &ScannerClient<Channel>,
|
||||||
|
mock_scan_service: &MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) {
|
||||||
|
// create request, fake populated results and get response
|
||||||
|
let get_results_response =
|
||||||
|
call_get_results(client.clone(), mock_scan_service.clone(), network, false).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
let transaction_heights = get_results_response
|
||||||
|
.into_inner()
|
||||||
|
.results
|
||||||
|
.first_key_value()
|
||||||
|
.expect("should have at least 1 value")
|
||||||
|
.1
|
||||||
|
.by_height
|
||||||
|
.len();
|
||||||
|
match network {
|
||||||
|
Network::Mainnet => {
|
||||||
|
assert_eq!(
|
||||||
|
transaction_heights, 3,
|
||||||
|
"there should be 3 transaction heights"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Network::Testnet => {
|
||||||
|
assert_eq!(
|
||||||
|
transaction_heights, 1,
|
||||||
|
"there should be 1 transaction height"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create request, fake empty results and get response
|
||||||
|
let get_results_response =
|
||||||
|
call_get_results(client.clone(), mock_scan_service.clone(), network, true).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
let is_results_empty = get_results_response
|
||||||
|
.into_inner()
|
||||||
|
.results
|
||||||
|
.first_key_value()
|
||||||
|
.expect("should have at least 1 value")
|
||||||
|
.1
|
||||||
|
.by_height
|
||||||
|
.is_empty();
|
||||||
|
|
||||||
|
assert!(is_results_empty, "results should be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the `register_keys` gRPC method
|
||||||
|
async fn test_mocked_register_keys_for_network(
|
||||||
|
client: &ScannerClient<Channel>,
|
||||||
|
mock_scan_service: &MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) {
|
||||||
|
// create request, fake return value and get response
|
||||||
|
let register_keys_response =
|
||||||
|
call_register_keys(client.clone(), mock_scan_service.clone(), network).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
assert_eq!(
|
||||||
|
register_keys_response.into_inner().keys,
|
||||||
|
vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string()],
|
||||||
|
"keys should match mocked response"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the `clear_results` gRPC method
|
||||||
|
async fn test_mocked_clear_results_for_network(
|
||||||
|
client: &ScannerClient<Channel>,
|
||||||
|
mock_scan_service: &MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) {
|
||||||
|
// create request, fake results and get response
|
||||||
|
let get_results_response =
|
||||||
|
call_get_results(client.clone(), mock_scan_service.clone(), network, false).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
let transaction_heights = get_results_response
|
||||||
|
.into_inner()
|
||||||
|
.results
|
||||||
|
.first_key_value()
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.by_height
|
||||||
|
.len();
|
||||||
|
match network {
|
||||||
|
Network::Mainnet => {
|
||||||
|
assert_eq!(transaction_heights, 3);
|
||||||
|
}
|
||||||
|
Network::Testnet => {
|
||||||
|
assert_eq!(transaction_heights, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create request, fake results and get response
|
||||||
|
let clear_results_response =
|
||||||
|
call_clear_results(client.clone(), mock_scan_service.clone(), network).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
assert_eq!(clear_results_response.into_inner(), Empty {});
|
||||||
|
|
||||||
|
// create request, fake results and get response
|
||||||
|
let get_results_response =
|
||||||
|
call_get_results(client.clone(), mock_scan_service.clone(), network, true).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
let is_results_empty = get_results_response
|
||||||
|
.into_inner()
|
||||||
|
.results
|
||||||
|
.first_key_value()
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.by_height
|
||||||
|
.is_empty();
|
||||||
|
|
||||||
|
assert!(is_results_empty, "results should be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the `delete_keys` gRPC method
|
||||||
|
async fn test_mocked_delete_keys_for_network(
|
||||||
|
client: &ScannerClient<Channel>,
|
||||||
|
mock_scan_service: &MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) {
|
||||||
|
// create request, fake results and get response
|
||||||
|
let register_keys_response =
|
||||||
|
call_register_keys(client.clone(), mock_scan_service.clone(), network).await;
|
||||||
|
|
||||||
|
// test the response
|
||||||
|
assert_eq!(
|
||||||
|
register_keys_response.into_inner().keys,
|
||||||
|
vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string()]
|
||||||
|
);
|
||||||
|
|
||||||
|
// create request, fake results and get response
|
||||||
|
let get_results_response =
|
||||||
|
call_get_results(client.clone(), mock_scan_service.clone(), network, false).await;
|
||||||
|
let transaction_heights = get_results_response
|
||||||
|
.into_inner()
|
||||||
|
.results
|
||||||
|
.first_key_value()
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.by_height
|
||||||
|
.len();
|
||||||
|
match network {
|
||||||
|
Network::Mainnet => {
|
||||||
|
assert_eq!(transaction_heights, 3);
|
||||||
|
}
|
||||||
|
Network::Testnet => {
|
||||||
|
assert_eq!(transaction_heights, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let delete_keys_response =
|
||||||
|
call_delete_keys(client.clone(), mock_scan_service.clone(), network).await;
|
||||||
|
// test the response
|
||||||
|
assert_eq!(delete_keys_response.into_inner(), Empty {});
|
||||||
|
|
||||||
|
let get_results_response =
|
||||||
|
call_get_results(client.clone(), mock_scan_service.clone(), network, true).await;
|
||||||
|
let is_results_empty = get_results_response
|
||||||
|
.into_inner()
|
||||||
|
.results
|
||||||
|
.first_key_value()
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.by_height
|
||||||
|
.is_empty();
|
||||||
|
|
||||||
|
assert!(is_results_empty, "results should be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start the gRPC server, get a client and a mock service
|
||||||
|
async fn start_server_and_get_client(
|
||||||
|
random_port: u16,
|
||||||
|
) -> (
|
||||||
|
ScannerClient<tonic::transport::Channel>,
|
||||||
|
MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
) {
|
||||||
|
// get a mocked scan service
|
||||||
|
let mock_scan_service = MockService::build().for_unit_tests();
|
||||||
|
|
||||||
|
// start the gRPC server
|
||||||
|
let listen_addr: std::net::SocketAddr = format!("127.0.0.1:{random_port}")
|
||||||
|
.parse()
|
||||||
|
.expect("hard-coded IP and u16 port should parse successfully");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
init(listen_addr, mock_scan_service)
|
||||||
|
.await
|
||||||
|
.expect("Possible port conflict");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the server to start
|
||||||
|
sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
|
// connect to the gRPC server
|
||||||
|
let client = ScannerClient::connect(format!("http://127.0.0.1:{random_port}"))
|
||||||
|
.await
|
||||||
|
.expect("server should receive connection");
|
||||||
|
|
||||||
|
(client, mock_scan_service)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add fake populated results to the mock scan service
|
||||||
|
async fn add_fake_populated_results(
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) {
|
||||||
|
let mut mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let zec_pages_sapling_efvk = ZECPAGES_SAPLING_VIEWING_KEY.to_string();
|
||||||
|
let mut fake_results = BTreeMap::new();
|
||||||
|
let heights = match network {
|
||||||
|
Network::Mainnet => vec![Height::MIN, Height(1), Height::MAX],
|
||||||
|
Network::Testnet => vec![Height::MIN],
|
||||||
|
};
|
||||||
|
for fake_result_height in heights {
|
||||||
|
fake_results.insert(
|
||||||
|
fake_result_height,
|
||||||
|
[transaction::Hash::from([0; 32])].repeat(3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fake_results_response = BTreeMap::new();
|
||||||
|
fake_results_response.insert(zec_pages_sapling_efvk, fake_results);
|
||||||
|
|
||||||
|
mock_scan_service
|
||||||
|
.expect_request_that(|req| matches!(req, ScanRequest::Results(_)))
|
||||||
|
.await
|
||||||
|
.respond(ScanResponse::Results(fake_results_response))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add fake empty results to the mock scan service
|
||||||
|
async fn add_fake_empty_results(
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
_network: Network,
|
||||||
|
) {
|
||||||
|
let mut mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let zec_pages_sapling_efvk = ZECPAGES_SAPLING_VIEWING_KEY.to_string();
|
||||||
|
let fake_results = BTreeMap::new();
|
||||||
|
let mut fake_results_response = BTreeMap::new();
|
||||||
|
fake_results_response.insert(zec_pages_sapling_efvk, fake_results);
|
||||||
|
|
||||||
|
mock_scan_service
|
||||||
|
.expect_request_that(|req| matches!(req, ScanRequest::Results(_)))
|
||||||
|
.await
|
||||||
|
.respond(ScanResponse::Results(fake_results_response))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the `get_results` gRPC method, mock and return the response
|
||||||
|
async fn call_get_results(
|
||||||
|
client: ScannerClient<Channel>,
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
empty_results: bool,
|
||||||
|
) -> tonic::Response<GetResultsResponse> {
|
||||||
|
let get_results_response_fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let get_results_request = tonic::Request::new(GetResultsRequest {
|
||||||
|
keys: vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string()],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.get_results(get_results_request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
if empty_results {
|
||||||
|
add_fake_empty_results(mock_scan_service, network).await;
|
||||||
|
} else {
|
||||||
|
add_fake_populated_results(mock_scan_service, network).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_results_response_fut
|
||||||
|
.await
|
||||||
|
.expect("tokio task should join successfully")
|
||||||
|
.expect("get_results request should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the `get_results` gRPC method with bad request data and asserts that it returns errors
|
||||||
|
async fn test_get_results_errors(mut client: ScannerClient<Channel>) {
|
||||||
|
let request = tonic::Request::new(GetResultsRequest { keys: vec![] });
|
||||||
|
let response = client.get_results(request).await;
|
||||||
|
|
||||||
|
let response_error_code = response
|
||||||
|
.expect_err("calling get_results with no keys should return an error")
|
||||||
|
.code();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
response_error_code,
|
||||||
|
tonic::Code::InvalidArgument,
|
||||||
|
"error code should be an invalid argument error"
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = tonic::Request::new(GetResultsRequest {
|
||||||
|
keys: vec![
|
||||||
|
ZECPAGES_SAPLING_VIEWING_KEY.to_string();
|
||||||
|
MAX_KEYS_PER_REQUEST
|
||||||
|
.checked_add(1)
|
||||||
|
.expect("should fit in usize")
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let response = client.get_results(request).await;
|
||||||
|
let response_error_code = response
|
||||||
|
.expect_err("calling get_results too with many keys should return an error")
|
||||||
|
.code();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
response_error_code,
|
||||||
|
tonic::Code::InvalidArgument,
|
||||||
|
"error code should be an invalid argument error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the `get_info` gRPC method, mock and return the response
|
||||||
|
async fn call_get_info(
|
||||||
|
client: ScannerClient<Channel>,
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
network: Network,
|
||||||
|
) -> tonic::Response<InfoReply> {
|
||||||
|
let get_info_response_fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let get_info_request = tonic::Request::new(Empty {});
|
||||||
|
tokio::spawn(async move { client.get_info(get_info_request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
mock_scan_service
|
||||||
|
.expect_request_that(|req| matches!(req, ScanRequest::Info))
|
||||||
|
.await
|
||||||
|
.respond(ScanResponse::Info {
|
||||||
|
min_sapling_birthday_height: network.sapling_activation_height(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
get_info_response_fut
|
||||||
|
.await
|
||||||
|
.expect("tokio task should join successfully")
|
||||||
|
.expect("get_info request should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the `register_keys` gRPC method, mock and return the response
|
||||||
|
async fn call_register_keys(
|
||||||
|
client: ScannerClient<Channel>,
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
_network: Network,
|
||||||
|
) -> tonic::Response<RegisterKeysResponse> {
|
||||||
|
let key_with_height = KeyWithHeight {
|
||||||
|
key: ZECPAGES_SAPLING_VIEWING_KEY.to_string(),
|
||||||
|
height: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let register_keys_response_fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let register_keys_request = tonic::Request::new(RegisterKeysRequest {
|
||||||
|
keys: vec![key_with_height],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.register_keys(register_keys_request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
mock_scan_service
|
||||||
|
.expect_request_that(|req| matches!(req, ScanRequest::RegisterKeys(_)))
|
||||||
|
.await
|
||||||
|
.respond(ScanResponse::RegisteredKeys(vec![
|
||||||
|
ZECPAGES_SAPLING_VIEWING_KEY.to_string(),
|
||||||
|
]))
|
||||||
|
});
|
||||||
|
|
||||||
|
register_keys_response_fut
|
||||||
|
.await
|
||||||
|
.expect("tokio task should join successfully")
|
||||||
|
.expect("register_keys request should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the `register_keys` gRPC method with bad request data and asserts that it returns errors
|
||||||
|
async fn test_register_keys_errors(client: ScannerClient<Channel>) {
|
||||||
|
let key_with_height = KeyWithHeight {
|
||||||
|
key: ZECPAGES_SAPLING_VIEWING_KEY.to_string(),
|
||||||
|
height: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let request = tonic::Request::new(RegisterKeysRequest { keys: vec![] });
|
||||||
|
tokio::spawn(async move { client.register_keys(request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = fut.await.expect("tokio task should join successfully");
|
||||||
|
assert!(response.is_err());
|
||||||
|
assert_eq!(response.err().unwrap().code(), tonic::Code::InvalidArgument);
|
||||||
|
|
||||||
|
let fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let request = tonic::Request::new(RegisterKeysRequest {
|
||||||
|
keys: vec![key_with_height; 11],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.register_keys(request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = fut.await.expect("tokio task should join successfully");
|
||||||
|
assert!(response.is_err());
|
||||||
|
assert_eq!(response.err().unwrap().code(), tonic::Code::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn call_clear_results(
|
||||||
|
client: ScannerClient<Channel>,
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
_network: Network,
|
||||||
|
) -> tonic::Response<Empty> {
|
||||||
|
let clear_results_response_fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let clear_results_request = tonic::Request::new(ClearResultsRequest {
|
||||||
|
keys: vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string()],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.clear_results(clear_results_request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
mock_scan_service
|
||||||
|
.expect_request_that(|req| matches!(req, ScanRequest::ClearResults(_)))
|
||||||
|
.await
|
||||||
|
.respond(ScanResponse::ClearedResults)
|
||||||
|
});
|
||||||
|
|
||||||
|
clear_results_response_fut
|
||||||
|
.await
|
||||||
|
.expect("tokio task should join successfully")
|
||||||
|
.expect("register_keys request should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the `clear_results` gRPC method with bad request data and asserts that it returns errors
|
||||||
|
async fn test_clear_results_errors(client: ScannerClient<Channel>) {
|
||||||
|
let fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let request = tonic::Request::new(ClearResultsRequest { keys: vec![] });
|
||||||
|
tokio::spawn(async move { client.clear_results(request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = fut.await.expect("tokio task should join successfully");
|
||||||
|
assert!(response.is_err());
|
||||||
|
assert_eq!(response.err().unwrap().code(), tonic::Code::InvalidArgument);
|
||||||
|
|
||||||
|
let fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let request = tonic::Request::new(ClearResultsRequest {
|
||||||
|
keys: vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string(); 11],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.clear_results(request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = fut.await.expect("tokio task should join successfully");
|
||||||
|
assert!(response.is_err());
|
||||||
|
assert_eq!(response.err().unwrap().code(), tonic::Code::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the `delete_keys` gRPC method, mock and return the response
|
||||||
|
async fn call_delete_keys(
|
||||||
|
client: ScannerClient<Channel>,
|
||||||
|
mock_scan_service: MockService<ScanRequest, ScanResponse, PanicAssertion>,
|
||||||
|
_network: Network,
|
||||||
|
) -> tonic::Response<Empty> {
|
||||||
|
let delete_keys_response_fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let delete_keys_request = tonic::Request::new(DeleteKeysRequest {
|
||||||
|
keys: vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string()],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.delete_keys(delete_keys_request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mock_scan_service = mock_scan_service.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
mock_scan_service
|
||||||
|
.expect_request_that(|req| matches!(req, ScanRequest::DeleteKeys(_)))
|
||||||
|
.await
|
||||||
|
.respond(ScanResponse::DeletedKeys)
|
||||||
|
});
|
||||||
|
delete_keys_response_fut
|
||||||
|
.await
|
||||||
|
.expect("tokio task should join successfully")
|
||||||
|
.expect("delete_keys request should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the `delete_keys` gRPC method with bad request data and asserts that it returns errors
|
||||||
|
async fn test_delete_keys_errors(client: ScannerClient<Channel>) {
|
||||||
|
let fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let request = tonic::Request::new(DeleteKeysRequest { keys: vec![] });
|
||||||
|
tokio::spawn(async move { client.delete_keys(request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = fut.await.expect("tokio task should join successfully");
|
||||||
|
assert!(response.is_err());
|
||||||
|
assert_eq!(response.err().unwrap().code(), tonic::Code::InvalidArgument);
|
||||||
|
|
||||||
|
let fut = {
|
||||||
|
let mut client = client.clone();
|
||||||
|
let request = tonic::Request::new(DeleteKeysRequest {
|
||||||
|
keys: vec![ZECPAGES_SAPLING_VIEWING_KEY.to_string(); 11],
|
||||||
|
});
|
||||||
|
tokio::spawn(async move { client.delete_keys(request).await })
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = fut.await.expect("tokio task should join successfully");
|
||||||
|
assert!(response.is_err());
|
||||||
|
assert_eq!(response.err().unwrap().code(), tonic::Code::InvalidArgument);
|
||||||
|
}
|
Loading…
Reference in New Issue