--- name: testing description: How to write and run tests in Celeriant. Unit tests with glommio_test! macro, integration tests via the test runner with category registry, Criterion benchmarks. Use when creating tests or benchmarks. --- # Testing ## Unit Tests Async code needs a Glommio executor. Use the `glommio_test!` macro defined locally in test modules: ```rust macro_rules! glommio_test { ($body:expr) => { LocalExecutorBuilder::new(Placement::Fixed(0)) .spawn(|| async move { $body }) .unwrap() .join() .unwrap() }; } #[test] fn my_async_test() { glommio_test!({ let result = some_async_fn().await; assert!(result.is_ok()); }); } ``` The macro is defined per test module (not crate-wide). See `celeriant_shard/src/shard_wal_sync.rs`, `celeriant_rotating_log/src/log_segment_file.rs`, `celeriant_watch/src/tests.rs` for examples. Test philosophy: test observable behavior, not implementation details. Understand the design spec before writing tests. Extract common helpers (`test_dir()`, `test_config()`, `make_aggregate_key()`). Descriptive function names (`write_with_empty_events_returns_error`). ## Integration Tests Single binary with a test runner and category-based registry. Not separate binaries. Each test is a module with `pub async fn run()` that spawns real server processes via `TestServer`, connects with `CeleriantClient`, and gets a fresh temp dir. The runner dispatches via `--run-test ` as subprocess, capturing output to `/tmp/celeriant_test_.log`. ### Build First Integration tests spawn the Celeriant server as a subprocess. Build it before running tests: ```bash cargo build -p celeriant --release cargo build -p celeriant_wal_inspect --release # some tests (e.g. stale_lease_restart_split_brain) shell out to it ``` ### Running ```bash # Run all standalone tests cargo run -p celeriant_integration_tests --release -- --standalone # Run a specific test cargo run -p celeriant_integration_tests --release -- --test single # Filter by category (AND) cargo run -p celeriant_integration_tests --release -- --include core,correctness # Filter by category (OR) cargo run -p celeriant_integration_tests --release -- --include-or edge,invariant # List matching tests without running cargo run -p celeriant_integration_tests --release -- --standalone --list # List all categories cargo run -p celeriant_integration_tests --release -- --list-categories ``` ### Adding a New Integration Test 1. Create `celeriant_integration_tests/src/my_test.rs` with `pub async fn run() -> Result<(), Box>` 2. Add `pub mod my_test;` to `lib.rs` 3. Add a `TestEntry` to `registry.rs` with name, description, estimated_secs, categories, and whether it requires distributed (MinIO + multi-node) 4. Add the dispatch arm in `main.rs` `dispatch_test()`: `"my_test" => my_test::run().await` Categories: Core, Replication, Election, Fencing, Invariant, Edge, Correctness, Durability, Performance, Operations, Security, Schema, Compaction, Debug. ## Benchmarks Criterion for statistical measurement, Glommio for async execution. Use `iter_custom` with manual timing since Criterion's `iter()` doesn't work with async. Fresh Glommio executor per iteration for clean state. `black_box` to prevent optimisation. Existing benchmarks live under `celeriant_shard/benches/` and `celeriant_wire/benches/`. Look at them for patterns including wave-based arrival and concurrent `spawn_local`. Baseline workflow: ```bash cargo bench --package celeriant_shard --bench aggregate_count_benchmark -- --save-baseline my_baseline critcmp --export my_baseline > ./celeriant_shard/benches/my_baseline.json critcmp my_baseline > ./celeriant_shard/benches/my_baseline.txt ```