Skip to content

testing

Thomas Mangin edited this page Apr 8, 2026 · 1 revision

Pre-Alpha. This page describes behavior that may change.

Ze has four test suites: unit, functional, fuzz, and chaos. You run them through the Makefile. Each suite covers a different layer, and they are independent enough that you can run one without the others while you are iterating, and combined enough that make ze-verify runs lint, unit, functional, and ExaBGP tests when you are ready to submit.

The suites, in order

Unit tests. Go unit tests in *_test.go files alongside the code. These run in milliseconds to seconds and cover the small-scale correctness of every exported function. make ze-unit-test runs them.

Functional tests. .ci files under test/, driven by ze-test. They start real or mocked Ze instances, feed them configuration, drive sessions, and assert against the wire bytes, log lines, and exit codes. make ze-functional-test runs them.

Fuzz tests. Go fuzz targets, one per external-input parser. Peer wire input, config files, command-line arguments, plugin JSON, and every other input from outside the trust boundary has a fuzz harness. make ze-fuzz-test runs them in a loop.

Chaos tests. Deterministic, seed-driven fault injection orchestrated by ze-chaos. Validates protocol-level invariants under fault. make ze-chaos-test runs the unit and functional chaos suites, and chaos web tests.

The Make targets

make ze-unit-test               # Unit tests
make ze-unit-test-cover         # Unit tests with coverage
make ze-functional-test         # Full functional suite
make ze-encode-test             # Encoding-only functional tests
make ze-plugin-test             # Plugin behavior functional tests
make ze-reload-test             # Reload-path functional tests
make ze-parse-test              # Config parsing tests
make ze-fuzz-test               # All fuzz targets, one-shot
make ze-fuzz-one FUZZ=<name> PKG=<path> TIME=<duration>  # A single fuzz target, longer run
make ze-chaos-test              # Unit + functional chaos, and chaos web tests
make ze-test                    # Lint, unit, functional, ExaBGP, and fuzz combined
make ze-verify                  # Lint, unit, functional, and ExaBGP, pre-submission
make ze-verify-changed          # Same as ze-verify, only on packages your branch touched
make ze-interop-test            # Docker interop against FRR, BIRD, GoBGP
make ze-live-test               # Live BGP tests
make ze-stress-test             # Stress suite

make ze-verify-changed is the one you want to run on every save. It is fast enough that it does not break flow, and it catches most problems before you push.

Running specific tests

ze-test drives the functional suite. Listing and running individual tests is a handful of flags.

ze-test bgp encode --list               # List the encode tests
ze-test bgp encode 4 5 6                # Run specific tests by index
ze-test bgp encode --all                # Run all encode tests
ze-test bgp plugin --list
ze-test bgp plugin --all
ze-test bgp reload --all

Stress-testing a flaky test:

ze-test bgp encode --count 10 0 1       # Run tests 0 and 1, ten times each

If a test passes nine times and fails once, it is flaky and it is a bug. Do not land a PR that introduces a flake.

Writing functional tests

Functional tests are .ci files. The format is a small scripting language: stdin=..., cmd=..., expect=... lines, with terminator-delimited blocks for config and expected output.

A minimal parse test:

stdin=config:terminator=EOF_CONF
bgp {
    peer test-peer {
        remote {
            ip 127.0.0.1;
            as 65533;
        }
        router-id 10.0.0.2;
        local-as  65533;
    }
}
EOF_CONF

cmd=foreground:seq=1:exec=ze bgp validate -:stdin=config
expect=exit:code=0

The runner parses the file, executes the commands in order, and asserts the expectations. If an expectation fails, the runner prints a diff showing what it expected and what it saw.

The existing .ci files under test/ are the best reference for how to write a new one. Start with the closest existing test, copy it, and edit.

Writing fuzz harnesses

func FuzzParseWireUpdate(f *testing.F) {
    // Seed with known-good inputs.
    f.Add([]byte{0x00, 0x18, 0x0a, 0x00, 0x00})

    f.Fuzz(func(t *testing.T, data []byte) {
        // Must not panic, must not infinite-loop.
        _, _ = wireu.ParseUpdate(data)
    })
}

Fuzz seeds are defined inline in fuzz test functions. Adding a known-bad input that used to crash the parser keeps the regression covered.

Interop testing

The interop suite runs Ze against FRR, BIRD, and GoBGP in Docker containers and asserts that every pair negotiates cleanly and exchanges routes correctly. make ze-interop-test runs it. This is the slowest suite, and the one that catches "technically RFC-compliant but nobody else expects that" bugs.

Live testing

make ze-live-test runs tests against real RPKI caches (stayrtr container). It requires network access and external services that may be unavailable. It is optional for development and mandatory before a release.

Coverage

make ze-unit-test-cover produces a coverage profile. Coverage is a sanity check, not a target: the goal is that every non-trivial code path has a test, not that a percentage number stays above a threshold. The maintainer cares about whether the test actually exercises the logic, not whether the line is counted.

Pre-submission checklist

Before you submit a patch:

  1. make ze-verify-changed is green.
  2. make ze-verify on the full tree is green.
  3. Any new code has unit tests.
  4. Any new user-facing behaviour has a functional test.
  5. Any new external-input parser has a fuzz harness.
  6. Documentation updates land in the same commit.

If all six are true, run /ze-review-deep in Claude Code and fix whatever it surfaces. Then submit.

See also

  • Building for the make targets that produce the binaries.
  • Debugging for what to do when a test fails.
  • CI for what runs automatically on every PR.
  • Performance for the ze-perf BGP propagation benchmark tool.
  • Mock Servers for the ze-test built-in mock servers used in functional tests.
  • In-tree functional tests for the full .ci reference.

Adapted from main/docs/functional-tests.md and main/Makefile.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally