testkit
Broker fixtures for the acceptance suite. Every milestone is verified against a real broker in a container — there are no mocked brokers in this workspace, by rule — and this crate is what makes that affordable.
publish = false.
Module map
| File | Lines | What |
|---|---|---|
config.rs | 616 | BrokerConfig, Security, SaslMechanism, SaslUser |
image.rs | 291 | the container image and its environment |
kafka.rs | 233 | single_broker, cluster, and the _with variants |
harness.rs | 173 | the Cluster trait and ExternalCluster |
error.rs | 58 | fixture errors |
The usual shapes
use testkit::{BrokerConfig, SaslMechanism, Security};
async fn example() -> testkit::Result<()> {
// The common case.
let broker = testkit::single_broker().await?;
let addr = broker.bootstrap_csv();
// Three nodes, for anything replica-aware.
let cluster = testkit::cluster(3).await?;
// Or configured.
let sasl = testkit::single_broker_with(
BrokerConfig::new()
.with_security(Security::SaslPlaintext)
.with_mechanism(SaslMechanism::Plain)
.with_user("alice", "alice-pw"),
)
.await?;
Ok(())
}
apache/kafka:4.3.1 in KRaft mode.
Tests take &dyn Cluster, never a concrete type
This is the crate's most important design decision and it is not abstraction for its own sake.
kaas-lib is the natural conformance harness for the
kaas broker, and that only works if the
acceptance suite can be pointed at something other than apache/kafka:4.3.1
without touching a single test. A hardcoded image in the fixtures would
quietly foreclose that — not with an error, but by making the alternative
expensive enough that nobody does it.
use std::fmt;
trait Cluster: fmt::Debug + Send + Sync {
/// Bootstrap addresses reachable from the *test process* — for container
/// fixtures the host-mapped port, not the in-container one.
fn bootstrap(&self) -> &[String];
fn nodes(&self) -> usize;
// …plus exec, for driving the Kafka shell tools inside a node
}
ExternalCluster is the other half of the bargain: it makes "point the suite
at a broker that is already running" a supported mode today, so the seam
gets exercised rather than rotting into a trait nobody has implemented twice.
exec matters more than it looks
exec(node, argv) runs a command inside a fixture node, and several fixtures
are generated by the Kafka shell tools rather than by a Rust client.
The group-kinds fixture is the reason. rdkafka
cannot create a KIP-932 share group — librdkafka has no share-group support
at all — so no Rust client in the workspace can generate that fixture. The
apache/kafka image already ships kafka-console-consumer.sh and
kafka-console-share-consumer.sh, and driving those through exec reaches
every group kind with zero build dependencies.
The config builder exists from the start
single_broker_with(BrokerConfig) was built before anything needed it,
because three later milestones do: SASL_PLAINTEXT/PLAIN and
SASL_SSL/SCRAM-SHA-512 brokers, an authorizer-enabled broker for ACLs, and
aggressive compaction settings for the backward-scan test.
The same argument applies to cluster(3). Retrofitting multi-broker fixtures
after the fact is painful, and leader spread, log dirs and reassignment are
not observable on one broker.
Start reading at harness.rs — it is short, and its module docs state
the conformance-harness argument in full.