Boot Zcash topologies (validators, indexers, wallets) on Kubernetes and hand typed RPC handles back to test code.
kind create cluster
ztest cluster add kind --kind kind --set-default
ztest cluster setup
# Run integration tests against validator/indexer pods
ztest run
# Launch long-running background syncs
ztest sync start zaino-index-constructionuse ztest::prelude::*;
#[tokio::test(flavor = "multi_thread")]
async fn zaino_indexes_to_validator_tip() {
let mut t = TestEnv::builder();
let zebra = t.add_validator(Validator::zebrad("6.2.3").regtest());
let zaino = t.add_indexer(Indexer::zainod("0.7.0").regtest());
t.build().await.unwrap();
// Typed RPC sugar on the validator handle:
zebra.generate_blocks(10).await.unwrap();
zaino.poll_block_height(zebra.chain_height().await.unwrap())
.await
.unwrap();
}most operations are defined by generic traits (ValidatorBackend,
IndexerBackend,WalletBackend) making ztest backend-agnostic.
Wallets are primarily run in-process, and most do not ship a daemon docker container, so test-runner images are used and sized w/ more CPU.
use ztest::prelude::*;
#[rstest::case]
#[tokio::test(flavor = "multi_thread")]
#[ztest::qos::wallet]
async fn ironwood_fetch_parity(wallet) {
let mut t = TestEnv::builder();
let zebra = t.add_validator(Validator::zebrad("6.2.3").regtest());
let zaino = t.add_indexer(Indexer::zainod("0.7.0").regtest());
let _wallet = t.add_wallet(wallet);
t.build().await.unwrap();
let faucet = w.funded_faucet(&zeb, &zai).await.unwrap();
let recipient = w.recipient(&zeb, &zai).await.unwrap();
let to = recipient.address(Pool::Orchard).await.unwrap();
faucet.send(&to, 100_000).await.unwrap();
let zebra = t.add_validator(
Validator::zebrad("6.2.3")
.mount(mount_config! ("tests/assets/zebrad.toml", "/etc/zebrad/zebrad.toml"))
.mount(mount_archive!("tests/assets/zebrad-100blocks.tar.zst", "/data")),
);
);
// Ztest also currently has zebra chain archives for mainnet and testnet (will add more later)
let zebra = t.add_validator(
Validator::zebrad("6.2.3").mainnet("orchard")
);When iterating on a component locally, swap the published constructor for
a dev!(...) pointed at a Dockerfile (resolved relative to the test
crate; compile fails if missing):
let zai = t.add_indexer(dev!(Indexer::Zainod, "../Dockerfile"));Accepted variants: Validator::Zebrad, Validator::Zcashd,
Indexer::Zainod, Wallet::Zingo.
Multi-hour sync workloads run as their own pod-backed lifecycle, detached from
the terminal that started them. ztest sync status renders the report —
throughput by pool, block/tx rates, and per-component CPU, memory, disk and IO
stall — from the same series Grafana serves:
ztest sync start zaino-index-construction
# blk/s tx/s, operations/s
ztest sync status zaino-index-construction-c287448b
# Launches flamegraph viz frontend
ztest sync perf zaino-index-construction-c287448b --component zainodThe ztest binary is the developer entry point for running ztest-managed
integration tests:
cargo run --bin ztest -- --help-
Review config rendering path (Use rust structs instead of toml? How to handle version matrix w/ config migrations?) -> Put this into a trait?
- That way zaino developers can define the function to generate the config toml based on the version? Need a clear example of how this would look
-
Migrate src/backends/zainod.rs to src/backends/zainod/{config,backend,sync}.rs

