Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 55 additions & 102 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -1,132 +1,85 @@
# Nextest configuration for Zebra
# This file centralizes all test execution configuration.
#
# Test categories (mapped to module paths in zebrad/tests/):
# unit:: Fast CLI, config, basic functionality tests (<1 min)
# integration:: Tests requiring zebrad running, no cached state (5-15 min)
# stateful:: Tests requiring cached blockchain state (30 min - days)
#
# Profiles:
# default Local dev: runs unit + integration (excludes stateful)
# ci PR CI: same scope, CI-tuned output
# ci-stateful GCP: runs stateful tests selected by --filter-expr
# check-no-git-dependencies Special CI release check

[profile.default]
fail-fast = true
status-level = "pass"
default-filter = 'not test(/^stateful::/) and not test(check_no_git_dependencies)'

# --- Platform-specific overrides ---
# --- Platform overrides ---

# Skip Windows-incompatible tests
[[profile.default.overrides]]
platform = 'cfg(target_os = "windows")'
filter = "not test(trusted_chain_sync_handles_forks_correctly) and not test(delete_old_databases)"

filter = "not test(=trusted_chain_sync_handles_forks_correctly) and not test(=delete_old_databases)"

# --- All Tests profile ---
# CI-friendly test selection.
#
# Runs fast unit/integration tests and explicitly excludes:
# - 'check_no_git_dependencies' (it has its own profile)
# - stateful or long-running tests that require a local cache dir or external
# services (lightwalletd RPC/GRPC, full sync, checkpoint generation, mining
# RPCs, etc.).
#
# Notes:
# - CI now provides a default cache directory, so stateful tests are no longer
# auto-skipped when the cache dir is undefined. To keep this profile fast and
# deterministic, we exclude those tests here and offer dedicated profiles
# below for running them when needed.
# TODO: We need a better test architecture to run all non-stateful
[profile.all-tests]
failure-output = "immediate"
default-filter = "not test(check_no_git_dependencies) and not test(=fully_synced_rpc_z_getsubtreesbyindex_snapshot_test) and not test(=lwd_rpc_test) and not test(=lwd_rpc_send_tx) and not test(=lwd_grpc_wallet) and not test(=lwd_integration) and not test(=lwd_sync_full) and not test(=lwd_sync_update) and not test(=lightwalletd_test_suite) and not test(=rpc_get_block_template) and not test(=rpc_submit_block) and not test(=get_peer_info) and not test(~generate_checkpoints_) and not test(=sync_update_mainnet) and not test(=activate_mempool_mainnet)"

# --- Individual Test Profiles ---

[profile.check-no-git-dependencies]
default-filter = 'test(check_no_git_dependencies)'

[profile.sync-large-checkpoints-empty]
# Slow integration test
[[profile.default.overrides]]
filter = 'test(sync_large_checkpoints_empty)'
slow-timeout = { period = "60m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=sync_large_checkpoints_empty)'

[profile.sync-full-mainnet]
slow-timeout = { period = "30000m", terminate-after = 1 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=sync_full_mainnet)'

[profile.sync-full-testnet]
slow-timeout = { period = "1500m", terminate-after = 1 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=sync_full_testnet)'

[profile.sync-to-mandatory-checkpoint-mainnet]
slow-timeout = { period = "1500m", terminate-after = 1 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=sync_to_mandatory_checkpoint_mainnet)'

[profile.sync-to-mandatory-checkpoint-testnet]
slow-timeout = { period = "1500m", terminate-after = 1 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=sync_to_mandatory_checkpoint_testnet)'
# --- CI Profile (PRs) ---

[profile.sync-update-mainnet]
slow-timeout = { period = "30m", terminate-after = 2 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=sync_update_mainnet)'
[profile.ci]
fail-fast = false
failure-output = "immediate"
default-filter = 'not test(/^stateful::/) and not test(check_no_git_dependencies)'

[profile.sync-past-mandatory-checkpoint-mainnet]
slow-timeout = { period = "60m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=sync_past_mandatory_checkpoint_mainnet)'
[[profile.ci.overrides]]
platform = 'cfg(target_os = "windows")'
filter = "not test(trusted_chain_sync_handles_forks_correctly) and not test(delete_old_databases)"

[profile.sync-past-mandatory-checkpoint-testnet]
[[profile.ci.overrides]]
filter = 'test(sync_large_checkpoints_empty)'
slow-timeout = { period = "60m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=sync_past_mandatory_checkpoint_testnet)'

[profile.generate-checkpoints-mainnet]
slow-timeout = { period = "90m", terminate-after = 1 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=generate_checkpoints_mainnet)'
# --- CI Stateful Profile (GCP VMs) ---
# No default-filter: CI selects specific tests via --filter-expr / NEXTEST_FILTER

[profile.generate-checkpoints-testnet]
slow-timeout = { period = "90m", terminate-after = 1 }
[profile.ci-stateful]
fail-fast = false
failure-output = "immediate"
success-output = "immediate"
default-filter = 'package(zebrad) and test(=generate_checkpoints_testnet)'
default-filter = 'all()'

[profile.lwd-rpc-test]
test-threads = 1
slow-timeout = { period = "60m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=lwd_rpc_test)'
# Stateful tests run serially to avoid state conflicts
[test-groups]
serial-state = { max-threads = 1 }

[profile.lwd-integration]
[[profile.ci-stateful.overrides]]
filter = 'test(/^stateful::/)'
test-group = 'serial-state'
slow-timeout = { period = "30m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=lwd_integration)'

[profile.lwd-sync-full]
# Per-test timeout overrides for long-running tests
[[profile.ci-stateful.overrides]]
filter = 'test(sync_full_mainnet) or test(lwd_sync_full)'
slow-timeout = { period = "30000m", terminate-after = 1 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=lwd_sync_full)'

[profile.lwd-sync-update]
slow-timeout = { period = "30m", terminate-after = 2 }
success-output = "immediate"
default-filter = 'package(zebrad) and test(=lwd_sync_update)'

[profile.lwd-grpc-wallet]
slow-timeout = { period = "60m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=lwd_grpc_wallet)'

[profile.lwd-rpc-send-tx]
slow-timeout = { period = "30m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=lwd_rpc_send_tx)'
[[profile.ci-stateful.overrides]]
filter = 'test(sync_full_testnet) or test(sync_to_mandatory_checkpoint_mainnet) or test(sync_to_mandatory_checkpoint_testnet)'
slow-timeout = { period = "1500m", terminate-after = 1 }

[profile.rpc-get-block-template]
slow-timeout = { period = "30m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=rpc_get_block_template)'
[[profile.ci-stateful.overrides]]
filter = 'test(generate_checkpoints_mainnet) or test(generate_checkpoints_testnet)'
slow-timeout = { period = "90m", terminate-after = 1 }

[profile.rpc-submit-block]
slow-timeout = { period = "30m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=rpc_submit_block)'
[[profile.ci-stateful.overrides]]
filter = 'test(sync_past_mandatory_checkpoint_mainnet) or test(sync_past_mandatory_checkpoint_testnet) or test(lwd_rpc_test) or test(lightwalletd_test_suite) or test(lwd_grpc_wallet)'
slow-timeout = { period = "60m", terminate-after = 2 }

[profile.indexer-has-spending-transaction-ids]
slow-timeout = { period = "30m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=has_spending_transaction_ids)'
# lwd_rpc_test runs single-threaded via the serial-state test group

[profile.lightwalletd-test-suite]
slow-timeout = { period = "60m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=lightwalletd_test_suite)'
# --- Special Profile ---

[profile.rpc-z-getsubtreesbyindex-snapshot]
slow-timeout = { period = "30m", terminate-after = 2 }
default-filter = 'package(zebrad) and test(=fully_synced_rpc_z_getsubtreesbyindex_snapshot_test)'
[profile.check-no-git-dependencies]
default-filter = 'test(check_no_git_dependencies)'
20 changes: 9 additions & 11 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,24 +172,22 @@ Our test execution is centralized through our Docker [entrypoint script](../../d
We use `nextest` profiles defined in [`.config/nextest.toml`](../../.config/nextest.toml) to manage test suites. A single environment variable, `NEXTEST_PROFILE`, selects the profile to run.

```bash
# Run the full test suite using the 'all-tests' profile
docker run --rm -e NEXTEST_PROFILE=all-tests zebra-tests
# Run unit + integration tests using the 'ci' profile
docker run --rm -e NEXTEST_PROFILE=ci zebra-tests

# Run a specific test suite, like the lightwalletd integration tests
docker run --rm -e NEXTEST_PROFILE=lwd-integration zebra-tests
# Run a specific stateful test on GCP
docker run --rm -e NEXTEST_PROFILE=ci-stateful -e "NEXTEST_FILTER=test(sync_full_mainnet)" zebra-tests
```

#### Test Categories

Our tests are organized into different categories:
Tests are organized into module-based tiers in `zebrad/tests/`:

- **Unit & Integration Tests**: Basic functionality and component testing
- **Network Sync Tests**: Testing blockchain synchronization from various states
- **Lightwalletd Tests**: Integration with the lightwalletd service
- **RPC Tests**: JSON-RPC endpoint functionality
- **Checkpoint Tests**: Blockchain checkpoint generation and validation
- **`unit::`**: CLI, config, end-of-support (<1 min)
- **`integration::`**: Launches zebrad, no cached state (5-15 min)
- **`stateful::`**: Requires cached blockchain state, runs on GCP (30 min - days)

Each test category has specific profiles that can be run individually using the `NEXTEST_PROFILE` environment variable.
The `ci` profile runs `unit::` and `integration::` tests on every PR. The `ci-stateful` profile is used on GCP VMs with `NEXTEST_FILTER` selecting specific tests. Adding a new test to `unit::` or `integration::` automatically includes it in PR CI.

### Pull Request Testing

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ jobs:
# TODO: Do we need --locked --release --features "default-release-binaries" here?
cargo llvm-cov report --lcov --output-path lcov.info
env:
TEST_LARGE_CHECKPOINTS: 1
# We set cases to 1, because some tests already run 1 case by default.
# We set maximum shrink iterations to 0, because we don't expect failures in coverage tests.
# Coverage tests are much slower than other tests, particularly in hot loops.
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/tests-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ jobs:
echo "PROPTEST_MAX_SHRINK_ITERS=1024" >> $GITHUB_ENV

- name: Run unit tests
run: cargo nextest run --profile all-tests --locked --release --features "${{ matrix.features }}" --run-ignored=all
env:
TEST_LARGE_CHECKPOINTS: 1
run: cargo nextest run --profile ci --locked --release --features "${{ matrix.features }}" --run-ignored=all

check-no-git-dependencies:
name: Check no git dependencies
Expand Down
28 changes: 14 additions & 14 deletions .github/workflows/zfnd-ci-integration-tests-gcp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ jobs:
app_name: zebrad
test_id: sync-to-mandatory-checkpoint
test_description: Test sync up to mandatory checkpoint
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=sync-to-mandatory-checkpoint-${{ (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && 'mainnet' || 'testnet' }},TEST_SYNC_TO_CHECKPOINT=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(sync_to_mandatory_checkpoint_${{ (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && 'mainnet' || 'testnet' }})"
network: ${{ inputs.network || vars.ZCASH_NETWORK }}
# This test commonly less than 3 hours by October 2024, but now it takes longer
is_long_test: true
Expand Down Expand Up @@ -231,7 +231,7 @@ jobs:
app_name: zebrad
test_id: sync-past-mandatory-checkpoint
test_description: Test full validation sync from a cached state
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=sync-past-mandatory-checkpoint-${{ (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && 'mainnet' || 'testnet' }},TEST_SYNC_PAST_CHECKPOINT=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(sync_past_mandatory_checkpoint_${{ (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && 'mainnet' || 'testnet' }})"
needs_zebra_state: true
saves_to_disk: false
disk_suffix: checkpoint
Expand Down Expand Up @@ -267,7 +267,7 @@ jobs:
test_id: sync-full-mainnet
test_description: Test a full sync up to the tip
# TODO: update the test to use {{ input.network }} instead?
test_variables: ZEBRA_NETWORK__NETWORK=Mainnet,NEXTEST_PROFILE=sync-full-mainnet,SYNC_FULL_MAINNET_TIMEOUT_MINUTES=0
test_variables: ZEBRA_NETWORK__NETWORK=Mainnet,NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(sync_full_mainnet)
# This test runs for longer than 6 hours, so it needs multiple jobs
is_long_test: true
needs_zebra_state: false
Expand Down Expand Up @@ -298,7 +298,7 @@ jobs:
app_name: zebrad
test_id: sync-update-mainnet
test_description: Test syncing to tip with a Zebra tip state
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=sync-update-mainnet"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(sync_update_mainnet)"
needs_zebra_state: true
# update the disk on every PR, to increase CI speed
saves_to_disk: true
Expand Down Expand Up @@ -333,7 +333,7 @@ jobs:
test_id: generate-checkpoints-mainnet
test_description: Generate Zebra checkpoints on mainnet
# TODO: update the test to use {{ input.network }} instead?
test_variables: ZEBRA_NETWORK__NETWORK=Mainnet,NEXTEST_PROFILE=generate-checkpoints-mainnet
test_variables: ZEBRA_NETWORK__NETWORK=Mainnet,NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(generate_checkpoints_mainnet)
needs_zebra_state: true
# sync-update-mainnet updates the disk on every PR, so we don't need to do it here
saves_to_disk: false
Expand Down Expand Up @@ -370,7 +370,7 @@ jobs:
app_name: zebrad
test_id: sync-full-testnet
test_description: Test a full sync up to the tip on testnet
test_variables: ZEBRA_NETWORK__NETWORK=Testnet,NEXTEST_PROFILE=sync-full-testnet,SYNC_FULL_TESTNET_TIMEOUT_MINUTES=1
test_variables: ZEBRA_NETWORK__NETWORK=Testnet,NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(sync_full_testnet)
network: Testnet
# A full testnet sync could take 2-10 hours in April 2023.
# The time varies a lot due to the small number of nodes.
Expand Down Expand Up @@ -406,7 +406,7 @@ jobs:
app_name: zebrad
test_id: generate-checkpoints-testnet
test_description: Generate Zebra checkpoints on testnet
test_variables: ZEBRA_NETWORK__NETWORK=Testnet,NEXTEST_PROFILE=generate-checkpoints-testnet
test_variables: ZEBRA_NETWORK__NETWORK=Testnet,NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(generate_checkpoints_testnet)
network: Testnet
needs_zebra_state: true
# update the disk on every PR, to increase CI speed
Expand Down Expand Up @@ -445,7 +445,7 @@ jobs:
app_name: lightwalletd
test_id: lwd-sync-full
test_description: Test lightwalletd full sync
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=lwd-sync-full,TEST_LIGHTWALLETD=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(lwd_sync_full),TEST_LIGHTWALLETD=1"
# This test runs for longer than 6 hours, so it needs multiple jobs
is_long_test: true
needs_zebra_state: true
Expand Down Expand Up @@ -476,7 +476,7 @@ jobs:
app_name: lightwalletd
test_id: lwd-sync-update
test_description: Test lightwalletd update sync with both states
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=lwd-sync-update,TEST_LIGHTWALLETD=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(lwd_sync_update),TEST_LIGHTWALLETD=1"
needs_zebra_state: true
needs_lwd_state: true
saves_to_disk: true
Expand Down Expand Up @@ -508,7 +508,7 @@ jobs:
app_name: lightwalletd
test_id: lwd-rpc-test
test_description: Test lightwalletd RPC with a Zebra tip state
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=lwd-rpc-test,TEST_LIGHTWALLETD=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(lwd_rpc_test),TEST_LIGHTWALLETD=1"
needs_zebra_state: true
saves_to_disk: false

Expand All @@ -534,7 +534,7 @@ jobs:
app_name: lightwalletd
test_id: lwd-rpc-send-tx
test_description: Test sending transactions via lightwalletd
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=lwd-rpc-send-tx,TEST_LIGHTWALLETD=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(lwd_rpc_send_tx),TEST_LIGHTWALLETD=1"
needs_zebra_state: true
needs_lwd_state: true
saves_to_disk: false
Expand All @@ -561,7 +561,7 @@ jobs:
app_name: lightwalletd
test_id: lwd-grpc-wallet
test_description: Test gRPC calls via lightwalletd
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=lwd-grpc-wallet,TEST_LIGHTWALLETD=1"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(lwd_grpc_wallet),TEST_LIGHTWALLETD=1"
needs_zebra_state: true
needs_lwd_state: true
saves_to_disk: false
Expand Down Expand Up @@ -592,7 +592,7 @@ jobs:
app_name: zebrad
test_id: rpc-get-block-template
test_description: Test getblocktemplate RPC method via Zebra's rpc server
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=rpc-get-block-template"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(rpc_get_block_template)"
needs_zebra_state: true
needs_lwd_state: false
saves_to_disk: false
Expand All @@ -619,7 +619,7 @@ jobs:
app_name: zebrad
test_id: rpc-submit-block
test_description: Test submitting blocks via Zebra's rpc server
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=rpc-submit-block"
test_variables: "ZEBRA_NETWORK__NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},NEXTEST_PROFILE=ci-stateful,NEXTEST_FILTER=test(rpc_submit_block)"
needs_zebra_state: true
needs_lwd_state: false
saves_to_disk: false
Expand Down
Loading
Loading