Skip to main content

Testing, Build, and CI

Why This Chapter Exists

No code lands without this chapter. Every PR runs the same gates locally and in CI. If you cannot reproduce a CI failure locally, you cannot fix it. The chapter is short on purpose: it lists the commands and the failure modes.

The Test Pyramid in Zebra

Tests are layered:

  • unit tests, colocated under src/**/tests/. Files commonly named vectors.rs (known-answer test vectors, usually from the spec or zcashd), prop.rs (proptest property tests), preallocate.rs (allocation-bound tests for deserializers).
  • integration tests, under each crate's top-level tests/ directory. These exercise crate-public APIs.
  • workspace-level acceptance tests in zebrad/tests/. These spawn zebrad as a subprocess and drive it.
  • network sync tests, also in zebrad/tests/, organized into nextest profiles in .config/nextest.toml.

Run them with the obvious command:

cargo test --workspace
cargo test -p zebra-chain
cargo test -p zebra-chain -- test_name

For better output and parallelism use nextest:

cargo nextest run
cargo nextest run --profile sync-large-checkpoints-empty

The nextest profiles are how CI organizes long-running sync tests. They are documented in book/src/dev/continuous-integration.md.

Property Testing

Every consensus-relevant type implements proptest::Arbitrary (gated behind the proptest-impl feature). This is what makes "for any valid transaction, serialize then deserialize is identity" tests feasible at scale. The zebra-test crate aggregates shared generators.

Read zebra-chain/src/tests.rs and any of the prop.rs files. The patterns to learn:

  • generators bounded by network upgrade. v5 transactions can only be generated under network upgrades where v5 is valid.
  • LedgerState, the proptest-helper type that lets a generator know which height, network, and upgrade it should target.

Fixed Test Vectors

For consensus parity, Zebra ports vectors from zcashd and the spec test corpus. Look for files literally named vectors.rs and the data directories under zebra-test/src/. Keep the vectors when porting a new ZIP; they are the most reliable defense against subtle serialization or hashing bugs.

Cargo.toml overrides [profile.dev.package.libzcash_script] to opt-level 3 (lines 296 to 302). The comment cites the advisory GHSA-gq4h-3grw-2rhv which only reproduces in release mode because C++ array zero-initialization in debug mode masks the buffer issue. Keep this in mind any time you debug something that "only happens in release".

Ci

CI runs via GitHub Actions; the entry point is .github/workflows/. The architecture is documented at .github/workflows/README.md and reproduced at the bottom of the top-level README.

Key workflows:

  • tests-unit.yml: unit tests across the workspace.
  • lint.yml: formatting and clippy.
  • zfnd-ci-integration-tests-gcp.yml: integration sync tests on GCP-hosted runners.
  • book.yml: builds and deploys the Zebra Book.
  • zfnd-deploy-nodes-gcp.yml: deploys canary zebrad nodes.

Release Process

Documented in book/src/dev/release-process.md and .github/workflows/. Releases are tagged on main. Versioning is documented in the same file. release.toml configures cargo-release.

The CHANGELOG.md is the authoritative user-facing change record. Per AGENTS.md, any user-visible change requires a CHANGELOG update in [Unreleased] plus a per-crate CHANGELOG.md for library-consumer-visible changes.

Docker

docker/ holds the Dockerfiles and entrypoint shell. The production images are published as zfnd/zebra on Docker Hub. Read book/src/user/docker.md and book/src/user/mining-docker.md for how operators actually run Zebra.

Benchmarks and Profiling

book/src/dev/profiling-and-benchmarking.md is the canonical guide. Tools mentioned: tracing-flame, pprof, cargo flamegraph, tokio-console. Benchmarks live under benches/ directories in the relevant crates (mostly zebra-chain for serialization and hashing).

What "Done" Looks Like for a Pr

Per the contribution gate in AGENTS.md, before opening a PR:

  1. confirm scope. Zebra is a validator node.
  2. keep the change focused.
  3. run formatting, lint, and tests locally:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
  1. prepare PR metadata: linked issue, motivation, solution, test evidence.
  2. disclose AI tooling if used.

The contribution gate is a hard requirement: there must be a maintainer-acknowledged issue before any PR is opened. The CLAUDE.md file in this repo enforces this for any AI-assisted work as well.

Suggested Exercises

  1. run the full CI sequence locally and time each step.
  2. write a proptest for one of the simpler types in zebra-chain (for example Height arithmetic) and run it under nextest.
  3. open .github/workflows/tests-unit.yml and identify the exact test commands CI runs. Reproduce them locally.
  4. read book/src/dev/state-db-upgrades.md. Sketch what a hypothetical database migration would look like for adding a new column family.

Spec Pointers

  • .github/workflows/: the CI graph the local commands mirror.
  • cargo nextest profiles in .config/nextest.toml (if present) for the integration-test matrix.

Exercises

  1. Run cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace. Confirm it passes on a clean checkout.
  2. Identify one CI workflow that runs only on tag pushes and explain what it does.
  3. Add a comment to a test, push it on a feature branch, and confirm the relevant CI job runs.