Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Test

When to read this: You want to run the right part of the suite for the change you made, understand why CI runs tests the way it does, or add a test in the place the next reader will look.

Goal

Know which make target or cargo test invocation covers your change, and be able to update a characterization golden deliberately rather than by accident.

Prerequisites

  • Build and run: make setup done.
  • make wasm-modules has been run (scripts/build-wasm-fixtures.sh). It stages every fixture and the echo and benford plugins under target/wasm-fixtures/<name>/, which is git-ignored; the integration and conformance suites fail with a missing-file error without it.

How the suite is organised

WhereWhat it coversRun with
#[cfg(test)] mod tests beside the code, in every crateUnit tests. Most logic is in chatty-core, so most tests are toomake test-fast (cargo test -p chatty-core --lib), or cargo test -p <crate>
crates/chatty-core/tests/integration.rsThe public API from an external consumer’s point of view: settings model CRUD lifecycle, ConversationsStore operations and ordering, provider capability propagation into ModelConfig, Azure provider configuration filtering, JSON serialization round-trips, token budget snapshot calculations. No display servercargo test -p chatty-core --test integration
crates/chatty-core/tests/browser_live.rsEnd to end against a real Chrome, #[ignore]d because the first run may download the pinned buildcargo test -p chatty-core --features browser --test browser_live -- --ignored --test-threads=1
crates/chatty-gpui/tests/core_integration.rschatty-core types compiled with the gpui-globals feature: every impl Global still behaves, cross-crate interactions, token budget snapshots. Links GPUI but needs no live displaymake test-gpui
crates/chatty-protocol-gateway/tests/ (gateway_tests.rs, echo_plugin_e2e.rs)The gateway’s routes, and the echo plugin over MCP (a plugin has no OpenAI or A2A route)make test-gateway
crates/chatty-protocol-gateway/tests/conformance/Plugin evaluation plan S3, rows 3.5–3.14 (3.1–3.4, 3.8 and 3.9 went with chat, PL-U3): MCP against echo, benford and the fixtures, and A2A against a participant, including real clients (rmcp, the MCP Inspector via npx on Node ≥ 22.19, chatty-core’s A2aClient). A missing npx prints SKIP and passes; CI installs Node. Rows red today are #[ignore = "known defect: …"]cargo test -p chatty-protocol-gateway --test conformance; add -- --ignored for the red rows
gemini_compat_tests in crates/chatty-core/src/tools/mod.rsEvery tool schema converts to a Gemini Schema without empty type stringscargo test -p chatty-core --lib gemini_compat
crates/chatty-core/src/repositories/store_conformance.rsEvery RepositoryRegistry family (13, token_tracking the newest) plus ConversationRepository against one load/save/round-trip contract, run against the JSON/SQLite and in-memory backends; exported under the test-support feature so hive’s store runs the identical suite (AGE-280)cargo test -p chatty-core --lib conformance
scripts/team-smoke.shThe coder-reviewer test-fixture team end to end in a container against a local Ollama, scored by a verifier — the one check for a change to the broker, --team or the worker tree. Not in CIbash scripts/team-smoke.sh; see Team smoke test
Characterization goldens (three directories, below)The event sequence each frontend produces for a scripted streamsee below
crates/hive-billing-sdk/tests/Standalone SDK with its own Cargo.lock; not a workspace member. .cargo/config.toml defaults the crate to wasm32-wasip2, which has no test-harness support, so tests need an explicit host targetcd crates/hive-billing-sdk && cargo test --target x86_64-unknown-linux-gnu
modules/benford/src/lib.rs unit testsStandalone plugin crate, also outside the workspace and defaulted to wasm32-wasip2 for the same reasoncd modules/benford && cargo test --target x86_64-unknown-linux-gnu
crates/chatty-module-sdk/Standalone SDK crate consumed by every module; no tests of its own, so CI holds it to a clean wasm32-wasip2 clippy pass insteadcd crates/chatty-module-sdk && cargo clippy --target wasm32-wasip2 -- -D warnings
crates/chatty-wasm-runtime/tests/sandbox.rsPlugin evaluation plan S1, rows 1.1–1.14: fuel, wall-clock, memory, traps, file/config/billing imports, WASI surface, against real fixtures. Doubles as AGE-600’s trip-wire: its non-ignored rows (fuel exhaustion, wit-0.1/core-module load failures, trap-then-reuse, …) already run in cargo test --all-features, so a sandbox regression fails CI without a separate test. Rows red today are #[ignore = "known defect: …"]cargo test -p chatty-wasm-runtime --features test-support --test sandbox; add -- --ignored for the red rows

Steps

1. Inner loop

make test-fast                    # cargo test -p chatty-core --lib
cargo test -p chatty-core --lib my_module::   # one module

2. The crate you touched

make test-tui       # cargo test -p chatty-tui
make test-gpui      # cargo test -p chatty-gpui
make test-gateway   # cargo test -p chatty-protocol-gateway

3. Everything, the way CI runs it

make test           # cargo test --all-features

--all-features matters: the excel, docx, pdf, pptx, math-render, mermaid, duckdb and browser tool groups only compile with their feature on, so a plain cargo test never sees them. make ci adds formatting, clippy and the repository check scripts; see Make targets & CI workflows.

Note

Tests run in parallel. CI used to pass --test-threads=1 because chatty-core tests intermittently died with SIGTRAP. The cause was pdfium, which is not thread-safe, being used from several test threads at once; create_pdfium() now hands out a PdfiumHandle that serialises every use (AGE-176). If a SIGTRAP, SIGSEGV or SIGABRT comes back under cargo test, look for a new pdfium call that bypasses create_pdfium() rather than re-adding the flag.

4. Characterization goldens

Turn orchestration — which chunk produces which event, which chunks end a turn, that cancellation produces a cancelled outcome, that sub-agent progress is drained ahead of stream chunks — is pinned by golden files rather than by assertions scattered across tests.

The scripted streams live in chatty_core::services::stream_fixtures (scenarios(), clarification_scenario(), scripted_stream, assert_golden). The module is compiled under #[cfg(any(test, feature = "test-support"))]; chatty-tui and chatty-gpui enable chatty-core/test-support from their dev-dependencies only, so it never reaches a release build.

Both frontends and the core loop drive the same scenarios and record their own event type, so a divergence between the two UIs shows up as a difference between two golden directories:

Golden directoryRecorded byEvent type
crates/chatty-core/src/services/goldens/stream_loop/loop_callback_sequence_matches_goldens in stream_processor.rsStreamChunkHandler callbacks
crates/chatty-core/src/session/goldens/session/tests.rsSessionEvent
crates/chatty-tui/src/engine/goldens/engine/characterization.rsAppEvent
crates/chatty-gpui/src/chatty/controllers/app_controller/goldens/session_characterization.rsStreamManagerEvent

Each directory holds one file per scenario: text_only, tool_call_then_result, tool_error, approval_granted, approval_denied, clarification_requested, provider_error_mid_stream, cancelled_mid_stream, sub_agent_progress, token_usage_on_done.

When a golden fails, the assertion prints both sequences. If the change is deliberate, rewrite the files and explain the diff in the PR:

UPDATE_GOLDENS=1 cargo test -p chatty-core --lib loop_callback_sequence
UPDATE_GOLDENS=1 cargo test -p chatty-core session::
UPDATE_GOLDENS=1 cargo test -p chatty-tui characterization
UPDATE_GOLDENS=1 cargo test -p chatty-gpui characterization

Deliberately not pinned: the real-time interleaving of progress events with chunks (racy in production, so scenarios queue progress before the chunks it accompanies), and anything wall-clock based — the stall watchdog has its own tests in stream_processor.rs.

5. Where mocks live

  • crates/chatty-core/src/tools/test_helpers.rs — MockMcpRepository and MockA2aRepository, in-memory repositories with injectable load/save errors, #[cfg(test)] only. Used by the MCP and A2A tool tests.
  • Providers are not mocked; the scripted streams above stand in for a provider whenever a test needs “what the model sent”.
  • The desktop characterization test talks to real entities (a ChatView in a headless test window, a live StreamManager), not stubs, so what it records is what the UI receives.
  • browser_live.rs builds a throwaway workspace with tempfile rather than fixtures on disk.

Verify

  • make test-fast is green after a core change; the crate target is green after a frontend change.
  • make test (full, serialised) is green before you push.
  • A golden you changed on purpose has its diff explained in the PR.

Checklist

  • make wasm-modules has been run on this checkout
  • New logic in chatty-core has a unit test beside it
  • A new tool has a gemini_compat guard (see Your first change)
  • Turn-lifecycle changes re-recorded all three golden directories, not one
  • make test passes

Common mistakes

MistakeDo this instead
Deleting a golden file to make a test passUPDATE_GOLDENS=1 and explain the diff
Updating the TUI goldens but not the desktop ones (or the reverse)Re-run all three; the point is that they agree
cargo test without --all-features before pushingmake test
A signal (SIGTRAP/SIGSEGV/SIGABRT) under cargo testFind the pdfium call that bypasses create_pdfium(); do not add --test-threads=1
Running browser_live in CIIt is #[ignore]d on purpose; run it locally
Enabling test-support in a normal [dependencies] blockDev-dependency only