This document describes the different kinds of tests that exist in the Mina codebase and how to run them.
- Overview
- Unit Tests
- Command-line / Single-node Tests
- Integration Tests (Lucy)
- End-to-end Tests
- Coverage Testing
- Running Tests in CI
The Mina codebase has several categories of tests that range from fast in-process unit tests to full end-to-end integration tests that spin up a real network of nodes. The table below gives a high-level view:
| Category | Scope | Can run locally? | Tool |
|---|---|---|---|
| Inline unit tests | Single library | ✅ | dune runtest |
| Standalone test executables | One or more libraries | ✅ | dune runtest |
| Profile-dependent tests | One or more libraries | ✅ | dune runtest |
| PPX tests | ppx_mina extension |
✅ | make test-ppx |
| ZkApps examples unit tests | zkapps_examples app |
✅ (nightly in CI) | dune runtest |
| Archive node unit tests | archive app |
✅ (needs PostgreSQL) | dune runtest |
| Command-line / single-node tests | Daemon CLI | ✅ (needs app built) | mina-command-line-tests |
| Integration tests (Lucy) | Full multi-node network | ✅ (needs Docker) | mina-test-executive |
| Rosetta tests | Rosetta API + daemon | custom scripts | |
| Replayer test | Archive replayer | mina-replayer |
|
| Fuzzy ZkApp tests | ZkApp logic | ✅ | dune exec |
| Coverage tests | Unit tests with coverage | ✅ | make test-coverage |
The most common form of testing. Many OCaml library modules include inline tests written with ppx_inline_test. These are declared with let%test, let%test_unit, or let%test_module inside the source files, and are compiled and run by dune when the (inline_tests ...) stanza is present in the library's dune file.
Run all unit tests under src/lib:
dune runtest src/lib --profile=devRun unit tests for a specific library:
dune runtest src/lib/<library-name> --profile=devRun a single inline test case (using the helper script):
./scripts/testone.sh src/lib/<library-name>/<file>.ml [<test-name>]Tips:
- The default build profile is
dev. Use--profile=devnetor--profile=mainnetwhen you need a profile that matches a deployed network. - Set
DUNE_PROFILEin the environment to avoid repeating--profile=...on every command. - Dune caches test results; use
--forceto re-run all tests even if sources have not changed. - Running tests modifies resource limits: the scripts typically call
ulimit -s 65532andulimit -n 10240. - On retry after failure, dune will only re-run the failing tests, which saves time.
Some tests are compiled as standalone executables (using the dune (tests ...) or (executable ...) stanza) and are also invoked via dune runtest. This includes tests in src/test/archive/ and src/lib/ subdirectories that have their own tests/ folder.
These are run the same way as inline tests:
dune runtest src/test/archive --profile=devTo run tests for a whole subtree:
dune runtest src/ --profile=devSome tests have expected values that vary depending on the build profile (e.g. dev, devnet, lightnet, mainnet). These tests must be run once per profile that you want to validate.
Run profile-dependent tests:
export DUNE_PROFILE=dev # or devnet, lightnet, mainnet
dune build --force src/lib/node_config # rebuild node_config for the chosen profile
dune runtest \
src/lib/blockchain_snark/tests \
src/lib/transaction_snark/test/constraint_count \
src/lib/transaction_snark/test/print_transaction_snark_vkIn CI these are run for each profile separately by buildkite/scripts/profile-dependent-tests.sh.
The ppx_mina preprocessor extension has its own test suite that verifies that the PPX behaves correctly (e.g. that [@@deriving version] attributes compile or fail in expected ways).
make test-ppxThis runs make inside src/lib/ppx_mina/tests/.
The src/app/zkapps_examples application contains runnable ZkApp examples that double as integration-style unit tests. In CI these only run during nightly builds (NIGHTLY=true), but they can be run locally at any time:
dune build --profile=dev src/app/zkapps_examples
dune runtest --profile=dev src/app/zkapps_examplesThe archive application (src/app/archive) has its own unit test suite that requires a running PostgreSQL instance.
Prerequisites: PostgreSQL must be installed and a test database must be created. The helper script buildkite/scripts/setup-database-for-archive-node.sh will configure it:
source ./buildkite/scripts/setup-database-for-archive-node.sh <user> <password> <db>This sets the MINA_TEST_POSTGRES environment variable. After setup:
dune runtest src/app/archive --profile=devIn CI this is handled by buildkite/scripts/tests/archive-node-unit-tests.sh.
The mina-command-line-tests binary (src/test/command_line_tests/) tests the Mina daemon CLI in a single-node configuration. The test runner uses mina_automation, which automatically locates the Mina daemon binary — first in _build/default/ (local build), then in system paths such as /usr/local/bin.
Build and run locally:
# Build the daemon and the test runner
dune build src/app/cli/src/mina.exe src/test/command_line_tests/command_line_tests.exe --profile=dev
# Run all tests
export MINA_LIBP2P_PASS="naughty blue worm"
export MINA_PRIVKEY_PASS="naughty blue worm"
./_build/default/src/test/command_line_tests/command_line_tests.exe test -vIn CI this is handled by buildkite/scripts/single-node-tests.sh, which installs the mina-test-suite and mina-testnet-generic-lightnet debian packages and then runs mina-command-line-tests test -v.
"Lucy" is Mina's end-to-end integration testing framework. It spins up a complete multi-node Mina testnet using Docker Swarm and runs test logic against the live network.
Lucy tests are found in src/app/test_executive/ and use a custom OCaml DSL defined in src/lib/integration_test_lib/. See src/app/test_executive/README.md for the full guide.
Prerequisites:
- Docker with Swarm mode enabled (
docker swarm init --advertise-addr 127.0.0.1) - A
mina-daemonDocker image URL - A
mina-archiveDocker image URL
Compile from source and run locally (recommended for development):
make build
dune build src/app/test_executive/test_executive.exe src/app/logproc/logproc.exeThe compiled binaries will be at:
./_build/default/src/app/test_executive/test_executive.exe./_build/default/src/app/logproc/logproc.exe
You can add optional shell aliases for convenience (in ~/.bashrc or ~/.bash_aliases):
alias test_executive=./_build/default/src/app/test_executive/test_executive.exe
alias logproc=./_build/default/src/app/logproc/logproc.exeThen run a test the same way as with the installed binary:
export TEST_NAME=<test>
export MINA_IMAGE=<url-to-mina-daemon-image>
export ARCHIVE_IMAGE=<url-to-mina-archive-image>
test_executive local "$TEST_NAME" \
--mina-image "$MINA_IMAGE" \
--archive-image "$ARCHIVE_IMAGE" \
--debug \
| tee "$TEST_NAME.local.test.log" \
| logproc -i inline -f '!(.level in ["Spam", "Debug"])'Alternatively, install the mina-test-executive debian package:
echo "deb [trusted=yes] http://packages.o1test.net $(lsb_release -cs) stable" \
> /etc/apt/sources.list.d/o1.list
apt-get update && apt-get install -y mina-test-executiveThen invoke the installed binary directly:
mina-test-executive local "$TEST_NAME" \
--mina-image "$MINA_IMAGE" \
--archive-image "$ARCHIVE_IMAGE" \
--debug \
| tee "$TEST_NAME.local.test.log" \
| mina-logproc -i inline -f '!(.level in ["Spam", "Debug"])'Available tests (as of this writing):
| Test name | Branch |
|---|---|
peers-reliability |
all |
chain-reliability |
all |
payments |
all |
delegation |
all |
archive-node |
compatible |
gossip-consis |
all |
medium-bootstrap |
all |
block-prod-prio |
all |
zkapps |
develop |
zkapps-timing |
develop |
snarkyjs |
develop |
In CI the integration tests are driven by buildkite/scripts/run-test-executive-local.sh. The --debug flag keeps the testnet alive after the test completes, which is useful for post-mortem inspection of node logs via docker logs.
The Rosetta API has multiple test suites:
-
Indexer test — Validates that the Rosetta indexer produces correct output. Requires a running PostgreSQL instance with archive data (
$PG_CONN):mina-rosetta-indexer-test --archive_uri "$PG_CONN"In CI this is handled by
buildkite/scripts/rosetta-indexer-test.sh. -
Block race test — Races the Mina daemon, archive, and Rosetta to check for block consistency under load. Requires the daemon, archive, and Rosetta binaries and a PostgreSQL connection:
./scripts/rosetta/test-block-race.sh \ --mina-exe /usr/local/bin/mina \ --archive-exe /usr/local/bin/mina-archive \ --rosetta-exe /usr/local/bin/mina-rosetta \ --postgres-uri "$PG_CONN" \ --ledger <path-to-ledger>
In CI this is handled by
buildkite/scripts/tests/rosetta-block-race-test.sh. -
Rosetta integration tests — Spins up a daemon with archive and Rosetta and runs end-to-end API validation. Requires all Mina service binaries and a PostgreSQL instance. In CI this is handled by
buildkite/scripts/tests/rosetta-integration-tests.sh.
The archive replayer (mina-replayer) replays historical blockchain transactions against an archive database and verifies that the resulting ledger state matches expectations.
Prerequisites: A PostgreSQL instance with archive data and $PG_CONN set to the connection URI.
./scripts/replayer-test.sh \
-i src/test/archive/sample_db/replayer_input_file.json \
-p "$PG_CONN" \
-a mina-replayerIn CI this is handled by buildkite/scripts/replayer-test.sh.
Property-based / fuzzy tests for ZkApp transaction logic. These run a ZkApp transaction generator with a random seed and validate the results.
dune exec <path-to-fuzzy-test-exe> --profile=<profile> \
-- --timeout <seconds> \
--individual-test-timeout <seconds> \
--seed $RANDOMThe exact path and profile are configured per CI step; see buildkite/scripts/fuzzy-zkapp-test.sh for the invocation used in CI.
To measure code coverage of the unit test suite, use the bisect_ppx-instrumented test run:
Run all unit tests with coverage instrumentation:
make test-coverageThis calls scripts/create_coverage_profiles.sh, which runs:
dune runtest --instrument-with bisect_ppx --force src/lib --profile=devRun coverage for a specific library only:
scripts/create_coverage_profiles.sh <library-name>Generate reports after running tests with coverage:
# HTML report
make coverage-html
# open _coverage/index.html in a browser
# Text summary
make coverage-summaryThe CI system (Buildkite) runs tests automatically on each pull request. The following environment variables influence test behaviour:
| Variable | Description |
|---|---|
DUNE_PROFILE |
Dune build profile (dev, devnet, lightnet, mainnet). Defaults to dev. |
NPROC |
Number of parallel jobs for dune runtest. Defaults to the number of logical CPU cores. |
ERROR_ON_PROOF |
Set to true in CI to fail if proof cache needs updating. |
NIGHTLY |
Set to true to enable nightly-only tests (e.g. ZkApps examples). |
MINA_LIBP2P_PASS |
Passphrase for libp2p keys used during tests. |
MINA_PRIVKEY_PASS |
Passphrase for private keys used during tests. |
PG_CONN |
PostgreSQL connection URI for tests that require an archive database. |
Some tests are designed to run exclusively in CI because they require large amounts of resources, cloud credentials, or specific infrastructure (e.g. Docker Swarm, pre-built Debian packages). These include the Rosetta integration tests, the archive upgrade test, and the Debian upgrade test.
Tests that can be run locally are primarily unit tests (via dune runtest) and integration tests via Lucy (provided Docker is installed and a suitable daemon image is available).