The rules below are the ones CI enforces or the code assumes. Nothing here is a matter of taste, and where a rule exists because of a specific failure, the failure is named.
The suite has two halves, and which one you need depends on what you touched.
Without a server. ./run_tests.sh --hermetic runs everything that reaches
neither a Redis nor the network: RESP parsing and deserialization, command
encoding, configuration parsing, the message queue, cluster topology arithmetic,
and the client stack over an in-memory pipe. About 470 tests in a second, with no
Docker and no deployment. Use it as the inner loop.
With a server. The rest needs a running deployment:
- from
redis/, rundocker_up.sh(ordocker_up.cmd) - run
./run_tests.sh
run_tests.sh refuses to start against a deployment that is up but unusable — a
cluster whose nodes announce a stale address never forms, and the tests then hang
instead of failing. RUSTIS_SKIP_DEPLOYMENT_CHECK=1 steps past the gate.
The server half requires --test-threads=1, which run_tests.sh passes:
those tests share one Redis instance and flush it, so running them in parallel
produces failures that belong to no test in particular.
The two halves are separated by the server-tests feature, which is on by
default. The gate lives on the module list in src/tests/mod.rs, never on an
individual test:
- a module gated on
server-testsneeds a live Redis; - a
<module>_servermodule holds the server-bound tests of the module it is named after, whose own tests stay hermetic; - everything ungated must pass with no server and no network.
Put a test that needs a Redis in the gated module. A test placed on the wrong
side is not silent: it fails ./run_tests.sh --hermetic.
A hermetic test that still needs a server to talk to has one:
src/tests/fake_server.rs answers RESP3 over an in-memory pipe, and
src/tests/fault_injection_proxy.rs scripts a broken one.
cargo fmt --all -- --check, andcargo checkin debug and release.cargo clippy --all-targets -- -D warnings, on the library feature set and again on the one that addsbenchandweb-examples— otherwise no job builds the benchmark targets or the gated examples, and they rot.- The MSRV job compiles with exactly the declared toolchain.
- A feature matrix compiles each combination on its own, and a second matrix asserts that the rejected combinations still fail with the message they promise.
cargo semver-checksreports the public-API breaks a pull request introduces. It never fails the job: the report is there so each break becomes a deliberateCHANGELOG.mdentry rather than a discovery made after publishing.- The four
cargo-fuzztargets run weekly. They reach the parser through thefuzzingfeature, which exposes the same kind of internal entry points asbenchdoes throughresp::bench_supportand carries the same absence of a stability guarantee:cd fuzz && cargo +nightly fuzz run <target>.
Never --all-features. The two TLS runtimes are mutually exclusive and
enabling both is a compile error; the CI feature sets are what to reproduce
locally.
Panics. unwrap, expect, panic, unreachable, todo, unimplemented
and arithmetic_side_effects are denied crate-wide, and indexing_slicing is
denied in resp/ and network/. A surviving site carries
#[expect(…, reason = "…")] naming the invariant that makes it unreachable —
expect rather than allow, so a justification whose lint stops firing becomes
a warning and gets deleted instead of rotting. Test code is exempt: a test that
panics is a test that failed.
#![forbid(unsafe_code)], argued in src/lib.rs rather than assumed.
MSRV. Declared as rust-version in Cargo.toml. Raising it is a breaking
change and is announced in CHANGELOG.md.
CHANGELOG.md. Every user-visible change gets an entry, in the section it
belongs to. A breaking change also gets a line in the BREAKING CHANGES
checklist at the top of [Unreleased], stating what a caller has to do.
From a running deployment, cargo bench --features bench. The feature is
required: every benchmark target declares required-features = ["bench"], so a
plain cargo bench skips all of them and reports success having measured
nothing.
A measurement that compares two variants must alternate them inside each round and compare per-round ratios. Two sequential series measure the machine's mood as much as the code.
Cargo.toml holds the version; the tag confirms it. Publishing runs from a
GitHub release, and the workflow refuses a tag that disagrees with the manifest,
checks the version bump against cargo semver-checks, and builds the docs.rs
feature set and the native-tls backend before publishing — cargo publish builds
with default features only, so neither is covered otherwise.