Skip to content

Building & Testing


Toolchain

The Rust toolchain is pinned in rust-toolchain.toml at the repository root. rustup reads this file automatically, so the first build on a fresh checkout installs the exact compiler, rustfmt, and clippy versions used by CI.

[toolchain]
channel = "1.98.1"
components = ["rustfmt", "clippy"]
profile = "minimal"

No manual rustup override is needed.


Building

cargo build           # debug build
cargo build --release # optimised build → target/release/pylon

Testing

A bare cargo test needs all four services

Several test binaries connect to a real service before their first test can even run, and Cargo stops the whole run at the first binary that fails. On a machine that's missing one of them — say, no MongoDB — cargo test dies at mongo_app_manager's 30-second connection timeout, and every binary Cargo would have run after it (percore*, mysql_app_manager, postgres_app_manager, redis_*, rest, signin, tls, watchlist, webhooks) never runs at all — including the ones that need no infrastructure. Use the commands below instead of a bare cargo test.

Do not add --all-targets to cargo test

cargo test --all-targets -- --test-threads=1 fails, and not because anything is broken. --all-targets pulls in the four criterion benches (fanout, fanout_sink, mailbox, app_lookup), which are harness = false and parse their own arguments — criterion rejects --test-threads with error: unexpected argument found and the run dies after every real test has already passed.

--all-targets belongs on cargo clippy, where the repo and CI both use it, not on cargo test. A bare cargo test is safe (the bench targets are test = false, so they are never built or run). If you want everything except benches under a single-threaded harness, name the target kinds explicitly:

cargo test --locked --lib --bins --tests -- --test-threads=1

Benches are exercised separately with cargo bench.

Tests that need no infrastructure

This is the primary, always-on gate CI runs, and needs nothing but the pinned toolchain. Run it before opening a pull request if you don't have the services below available locally:

scripts/test-no-infra.sh

scripts/test-no-infra.sh is the single source for this command: CI's check job (services running) and its check-no-infra job (none at all) both run it verbatim, so this page and CI cannot drift apart — a future test that quietly needs a service (like one that used to sit here, see "Cluster / Redis tests" below) fails the check-no-infra job instead of only a contributor's local run.

Full suite (all services)

Running everything — the clustered/Redis suites and the per-backend AppManager suites included — requires all four services below:

Service Port Env var
Redis 6390 PYLON_TEST_REDIS_URL
MySQL 8 3307 PYLON_TEST_MYSQL_URL
Postgres 16 5433 PYLON_TEST_POSTGRES_URL
Mongo 7 27018 PYLON_TEST_MONGO_URL

deploy/docker/docker-compose.test.yml brings up all four on those ports: docker compose -f deploy/docker/docker-compose.test.yml up -d.

With those running and reachable, export the env vars above explicitly — naming the instance each suite talks to is worth the keystrokes when the run writes to it — and pass --no-fail-fast so one missing or misbehaving backend doesn't hide the results of the others:

export PYLON_TEST_REDIS_URL=redis://127.0.0.1:6390
export PYLON_TEST_MYSQL_URL=mysql://root:pylon@127.0.0.1:3307/pylon_test
export PYLON_TEST_POSTGRES_URL=postgres://postgres:pylon@127.0.0.1:5433/pylon_test
export PYLON_TEST_MONGO_URL=mongodb://127.0.0.1:27018/pylon_test

cargo test --locked --no-fail-fast -- --test-threads=1

Cluster / Redis tests

Tests that exercise the clustered path, the Redis adapter, or the Redis-backed L2 app cache and cross-node invalidation require a local Redis instance. Point at it with the PYLON_TEST_REDIS_URL environment variable:

PYLON_TEST_REDIS_URL=redis://127.0.0.1:6390 \
  cargo test --test cluster_bridge --test redis_app_cache --test redis_cluster -- --test-threads=1

Never FLUSH a shared Redis

Tests isolate themselves with random key prefixes. Do not run FLUSHALL or FLUSHDB on a Redis instance that holds data you care about — and never point PYLON_TEST_REDIS_URL at a production Redis.

Cluster tests must run serially (--test-threads=1) because several of them assert on short Redis round-trip timing windows that race under parallel execution.


Formatting and Linting

Both are gated in CI on every push and pull request:

cargo fmt --all --check   # check formatting (CI gate)
cargo fmt --all           # apply formatting (before committing)

cargo clippy --all-targets --locked -- -D warnings   # lint (CI gate; zero warnings allowed)
cargo clippy --locked --lib --bins -- -D warnings    # lint, default features only (CI gate)

CI runs both clippy commands, not just the first. A dev-dependency self-reference enables the test-hooks feature whenever test targets are in the build graph, so --all-targets cannot see warnings that only appear in the default-features build (cargo build --release, cargo install) — the second command is the only one that lints that build.


Load-Testing Crate

The load/ workspace crate contains scenario-based load tests and the pylon-ceiling capacity-finder binary. pylon-ceiling ramps connections in fixed-size batches (--conn-batch) to find the maximum sustainable concurrency on a given host, taking latency, CPU, and memory constraints as stop criteria.

See load/ for details on running load scenarios and the ceiling tool.