lilydocs
HTTP APIWebSocketConsumer
Reference4 min read

Testing Consumers

Test macros, topology, lifecycle and live broker/database scenarios as separate validation layers.

A compiling handler alone does not prove broker connectivity or database transaction behavior. Start with contract checks that need no broker, then run actual delivery, retry, recovery and transaction scenarios in a separate environment. State what was really executed for each result.

Compile feature profiles separately#

Terminal
cargo +1.96.1 check
cargo +1.96.1 test --test topology_test

Run these commands in your own application project. The first checks the selected manifest; the second requires the file below at tests/topology_test.rs. Keep single and factory examples in separate application profiles. --all-features can combine two modes of the same backend and create an invalid dependency graph.

Test topology contracts without a broker#

Cargo.toml — test additions
[dependencies]
lilyrs = { version = "0.1.0", features = ["consumer", "config"] }

[dev-dependencies]
toml = "0.8"

Keep existing application dependencies such as serde. The test embeds its own TOML fixture; the localhost credentials are only deserialized data and no network connection is opened. Its three checks cover retry-bucket generation, rejection of missing retention and exclusive queues, and the retry=0/finite-jitter rules.

topology_test.rs
use lilyrs::config::{LilyConfig, RabbitMqTopologyPlan};
fn config() -> LilyConfig { toml::from_str(r#"
[rabbitmq.consumer]
hostname = "127.0.0.1"
port = 5672
username = "guest"
password = "guest"
vhost = "/"
use_tls = false
pool_size = 1

[[rabbitmq.topology.queues]]
name = "orders.created"
exchange_name = "orders"
routing_key = "orders.created"
concurrency = 2
prefetch_count = 10
retry_attempts = 3
retry_backoff_millis = 1000
max_retry_backoff_millis = 60000

[rabbitmq.topology.queues.retention]
main_max_messages = 100000
main_max_bytes = 1073741824
retry_bucket_max_messages = 10000
retry_bucket_max_bytes = 268435456
dead_letter_max_messages = 10000
dead_letter_max_bytes = 268435456
"#).unwrap() }
#[test]
fn documented_topology_compiles_with_three_fixed_retry_buckets() {
    let config = config();
    let plan = RabbitMqTopologyPlan::compile(&config.rabbitmq.topology).unwrap();
    let queue = plan.queue("orders.created").unwrap();
    assert_eq!(queue.dead_letter_queue_name(), "orders.created.dlq.v2");
    assert_eq!(queue.retry_buckets().iter().map(|b| b.delay_millis()).collect::<Vec<_>>(), vec![1000,2000,4000]);
}
#[test]
fn missing_retention_and_exclusive_queue_are_rejected_before_broker_io() {
    let mut topology = config().rabbitmq.topology;
    topology.queues[0].retention = None;
    assert!(RabbitMqTopologyPlan::compile(&topology).is_err());
    let mut topology = config().rabbitmq.topology;
    topology.queues[0].exclusive = true;
    assert!(RabbitMqTopologyPlan::compile(&topology).is_err());
}
#[test]
fn retry_zero_declares_no_delay_buckets_and_jitter_is_finite() {
    let mut topology = config().rabbitmq.topology;
    topology.queues[0].retry_attempts = 0;
    assert!(RabbitMqTopologyPlan::compile(&topology).unwrap().queues()[0].retry_buckets().is_empty());
    topology.queues[0].retry_jitter_ratio = f64::NAN;
    assert!(RabbitMqTopologyPlan::compile(&topology).is_err());
}

Download the complete topology_test.rs example.

Cancellation before startup#

You can also test cancellation without accessing a configuration file or broker. Enable Tokio’s macros and rt-multi-thread features as a dev-dependency for this test. An already-cancelled token is observed before ordinary startup resources are acquired.

Lifecycle test
use lilyrs::consumer::{CancellationToken, Consumer};

#[tokio::test]
async fn cancellation_before_startup_needs_no_broker() {
    let cancellation = CancellationToken::new();
    cancellation.cancel();
    Consumer::builder()
        .run_with_cancellation(cancellation)
        .await
        .expect("pre-cancelled startup should finish cleanly");
}

Scope of live integration tests#

Scenario Required environment and observation
Delivery and retry/DLQ Isolated RabbitMQ; canonical messages, confirms, original ACKs, retry and DLQ outcomes
Recovery and shutdown Connection loss, readiness transitions, graceful/forced drain and terminal reports
PostgreSQL inbox/outbox Isolated database; migrations, duplicates and commit/ACK and publish/mark crash windows
MongoDB inbox/outbox Transaction-capable deployment; sessions, leases, body replay and ambiguous commits
AsyncAPI Document attachment and schema validation after successful queue registration

Qualification and E2E tests in the source repository illustrate these scenarios. Some require disposable storage and explicit opt-in; MongoDB fault scenarios may require a replica set and test-command support. Do not copy private test helpers as canonical application APIs. Do not point these fixtures at a production database.

Validation scope of this documentation#

During the documentation work on September 18, 2026, the separate PostgreSQL and MongoDB single/factory modules and the AsyncAPI module compiled with Rust 1.96.1 and crates.io lilyrs = "=0.1.0". No local framework path or patch dependency was used. The three topology tests above and the pre-cancelled Consumer test passed.

These results are not live RabbitMQ or database transaction tests. This work did not run broker delivery, PostgreSQL/MongoDB migration, replica-set replay, TLS/mTLS, process-crash or AsyncAPI attachment E2E tests. Reviewing source qualification files is not presented as evidence that those tests passed in this session.

Use the registry checker in the website repository to reproduce a selected example check. --mode check records manifest, source and provenance evidence; --mode smoke records results only for profiles with runtime tests. Add --offline only when the dependency cache is available.

Terminal
python3 scripts/check-rust-examples.py --profile consumer-postgresql --mode check
python3 scripts/check-rust-examples.py --profile consumer-mongodb-factory --mode check
python3 scripts/check-rust-examples.py --profile consumer-topology-test --mode smoke
Sources for this page v0.1.0

This guide is based on the reviewed framework commit.

QUICK START
Documentation
Source code