test(grpc): Add snapshots (#8277)
* add grpc snapshot tests * replaces ScanService with MockService in snapshot tests * removes dev-dep in zebra-grpc on zebra-scan and updates snapshots * Apply suggestions from code review Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> --------- Co-authored-by: Arya <aryasolhi@gmail.com>
This commit is contained in:
parent
f20834c373
commit
fbddec3cb3
|
@ -5815,13 +5815,18 @@ version = "0.1.0-alpha.1"
|
|||
dependencies = [
|
||||
"color-eyre",
|
||||
"futures-util",
|
||||
"insta",
|
||||
"prost",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tonic 0.11.0",
|
||||
"tonic-build 0.11.0",
|
||||
"tower",
|
||||
"zcash_primitives",
|
||||
"zebra-chain",
|
||||
"zebra-node-services",
|
||||
"zebra-state",
|
||||
"zebra-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -19,6 +19,7 @@ categories = ["cryptography::cryptocurrencies"]
|
|||
futures-util = "0.3.28"
|
||||
tonic = "0.11.0"
|
||||
prost = "0.12.3"
|
||||
serde = { version = "1.0.196", features = ["serde_derive"] }
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] }
|
||||
tower = { version = "0.4.13", features = ["util", "buffer"] }
|
||||
color-eyre = "0.6.2"
|
||||
|
@ -29,3 +30,11 @@ zebra-node-services = { path = "../zebra-node-services", version = "1.0.0-beta.3
|
|||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.33.0", features = ["redactions", "json", "ron"] }
|
||||
|
||||
zebra-chain = { path = "../zebra-chain", features = ["proptest-impl"] }
|
||||
zebra-state = { path = "../zebra-state" }
|
||||
zebra-test = { path = "../zebra-test" }
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::configure()
|
||||
.btree_map(["."])
|
||||
.type_attribute(".", "#[derive(serde::Deserialize, serde::Serialize)]")
|
||||
.compile(&["proto/scanner.proto"], &[""])?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
pub mod server;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// The generated scanner proto
|
||||
pub mod scanner {
|
||||
tonic::include_proto!("scanner");
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
mod snapshot;
|
|
@ -0,0 +1,151 @@
|
|||
//! Snapshot tests for Zebra Scan gRPC responses.
|
||||
//!
|
||||
//! Currently we snapshot the `get_info` and `get_results` responses for both mainnet and testnet with a
|
||||
//! mocked scanner database. Calls that return `Empty` responses are not snapshoted in this suite.
|
||||
//!
|
||||
//! To update these snapshots, run:
|
||||
//! ```sh
|
||||
//! cargo insta test --review --delete-unreferenced-snapshots
|
||||
//! ```
|
||||
use std::{collections::BTreeMap, thread::sleep, time::Duration};
|
||||
|
||||
use zebra_chain::{block::Height, parameters::Network, transaction};
|
||||
use zebra_test::mock_service::MockService;
|
||||
|
||||
use zebra_node_services::scan_service::{
|
||||
request::Request as ScanRequest, response::Response as ScanResponse,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
scanner::{
|
||||
scanner_client::ScannerClient, Empty, GetResultsRequest, GetResultsResponse, InfoReply,
|
||||
},
|
||||
server::init,
|
||||
};
|
||||
|
||||
/// The extended Sapling viewing key of [ZECpages](https://zecpages.com/boardinfo)
|
||||
pub const ZECPAGES_SAPLING_VIEWING_KEY: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz";
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_grpc_response_data() {
|
||||
let _init_guard = zebra_test::init();
|
||||
|
||||
tokio::join!(
|
||||
test_mocked_rpc_response_data_for_network(
|
||||
Network::Mainnet,
|
||||
zebra_test::net::random_known_port()
|
||||
),
|
||||
test_mocked_rpc_response_data_for_network(
|
||||
Network::Testnet,
|
||||
zebra_test::net::random_known_port()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async fn test_mocked_rpc_response_data_for_network(network: Network, random_port: u16) {
|
||||
// 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");
|
||||
|
||||
// insta settings
|
||||
let mut settings = insta::Settings::clone_current();
|
||||
settings.set_snapshot_suffix(network.lowercase_name());
|
||||
|
||||
// snapshot the get_info grpc call
|
||||
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(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// snapshot the get_info grpc call
|
||||
|
||||
let get_info_response = get_info_response_fut
|
||||
.await
|
||||
.expect("tokio task should join successfully")
|
||||
.expect("get_info request should succeed");
|
||||
|
||||
snapshot_rpc_getinfo(get_info_response.into_inner(), &settings);
|
||||
|
||||
// snapshot the get_results grpc call
|
||||
|
||||
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 })
|
||||
};
|
||||
|
||||
{
|
||||
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();
|
||||
for fake_result_height in [Height::MIN, Height(1), Height::MAX] {
|
||||
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))
|
||||
});
|
||||
}
|
||||
|
||||
let get_results_response = get_results_response_fut
|
||||
.await
|
||||
.expect("tokio task should join successfully")
|
||||
.expect("get_results request should succeed");
|
||||
|
||||
snapshot_rpc_getresults(get_results_response.into_inner(), &settings);
|
||||
}
|
||||
|
||||
/// Snapshot `getinfo` response, using `cargo insta` and JSON serialization.
|
||||
fn snapshot_rpc_getinfo(info: InfoReply, settings: &insta::Settings) {
|
||||
settings.bind(|| insta::assert_json_snapshot!("get_info", info));
|
||||
}
|
||||
|
||||
/// Snapshot `getresults` response, using `cargo insta` and JSON serialization.
|
||||
fn snapshot_rpc_getresults(results: GetResultsResponse, settings: &insta::Settings) {
|
||||
settings.bind(|| insta::assert_json_snapshot!("get_results", results));
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: zebra-grpc/src/tests/snapshot.rs
|
||||
expression: info
|
||||
---
|
||||
{
|
||||
"min_sapling_birthday_height": 419200
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: zebra-grpc/src/tests/snapshot.rs
|
||||
expression: info
|
||||
---
|
||||
{
|
||||
"min_sapling_birthday_height": 280000
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
source: zebra-grpc/src/tests/snapshot.rs
|
||||
expression: results
|
||||
---
|
||||
{
|
||||
"results": {
|
||||
"zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz": {
|
||||
"transactions": {
|
||||
"0": {
|
||||
"hash": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
},
|
||||
"1": {
|
||||
"hash": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
},
|
||||
"2147483647": {
|
||||
"hash": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
source: zebra-grpc/src/tests/snapshot.rs
|
||||
expression: results
|
||||
---
|
||||
{
|
||||
"results": {
|
||||
"zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz": {
|
||||
"transactions": {
|
||||
"0": {
|
||||
"hash": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
},
|
||||
"1": {
|
||||
"hash": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
},
|
||||
"2147483647": {
|
||||
"hash": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -159,7 +159,7 @@ impl Storage {
|
|||
|
||||
/// Inserts a batch of scanned sapling result for a key and height.
|
||||
/// If a result already exists for that key, height, and index, it is replaced.
|
||||
pub(crate) fn insert_sapling_results(
|
||||
pub fn insert_sapling_results(
|
||||
&mut self,
|
||||
sapling_key: &SaplingScanningKey,
|
||||
height: Height,
|
||||
|
|
Loading…
Reference in New Issue