type: testing title: Unit tests description: Overview of the in-module unit test suites in src/*.rs (including the web panel, the version flag and the configuration watcher), the ignored live relink test, what each one covers, how they isolate files, and how to run them. tags: [testing, cargo, unit-tests] sources:
651d1fb6c9
resource: repo://Cargo.toml1b05507d70
resource: repo://src/ai.rs9fec9d1af4
resource: repo://src/app.rs043e6252ba
resource: repo://src/autoreply.rsd6b2055bbb
resource: repo://src/config_watch.rs2a737474d8
resource: repo://src/config.rsce0c4ea8cd
resource: repo://src/history.rsb55a21a31e
resource: repo://src/main.rs6bddea306b
resource: repo://src/ui.rs7b75006986
resource: repo://src/web.rs7ea5eac45e
resource: repo://src/whatsapp.rs
generated: { by: "claude-code", at: "2026-10-23T21:26:24.156Z" }
verified:All tests are plain Rust unit tests in #[cfg(test)] mod tests blocks at the
bottom of each source file. There are no integration tests and no
[dev-dependencies] (Cargo.toml only has [dependencies]). Apart from one
#[ignore]d live test (see below), no test touches the network, binds a port
or uses a real WhatsApp session.
cargo test # whole suite
cargo test autoreply:: # one module
cargo test should_schedule # by name filter
cargo test -- --ignored # only the ignored live test (needs network)
The live test whatsapp::tests::relink_deletes_the_session_and_offers_a_new_qr
(src/whatsapp.rs:1163-1183) is skipped by a plain cargo test. It starts a
real client on a per-process target/wh-relink-test-<pid>.db, waits up to
60 s for a pairing QR from the WhatsApp servers, calls WaHandle::relink,
waits for the next QR and asserts that the device keys in the two QR
payloads differ (a new device). It finally stops the session and empties the
test database, because on Windows the file stays open until the process
exits. See WhatsApp relink and pairing.
| Module | Tests | Location | Covers |
|---|---|---|---|
autoreply |
55 | src/autoreply.rs:505+ |
private/group/self-chat detection, supports_autoreply (groups yes, broadcasts and newsletters no), ReplyGate::should_schedule rules (disabled, own messages, self-chat, backlog, blank/placeholder text, voice notes and images trigger, pending chat), pending tracking and take_pending, answered_by_owner, catch_up_due rules, build_turns (system turn naming private chat, group or self-chat, roles, is_ai in the self-chat, context cap, blank texts skipped, voice notes, stored image descriptions vs. unseen images, assistant turns untouched, group sender prefixes), context_start, mark_own_mentions |
config |
37 | src/config.rs:350+ |
defaults, full files, ignored legacy system_prompt key, blank values, trimming, base URL normalisation, Debug masking, DB and AI log paths, ai_log, reply_delay_seconds (default, custom and 0, negatives rejected, legacy cooldown keys ignored), catch-up and ai_retries defaults/custom values/negative rejection, web_port (unset, valid, 0 and out of range rejected), BOM/CRLF, non-leaking parse errors, missing/invalid/unreadable files, SOUL.md reading (missing, blank, as written without BOM, non-UTF-8 error naming the path) |
ai |
27 | src/ai.rs:581+ |
text request body with the no_reply tool, image description body (no tools) and its VISION.md/default request, image validation limits, describe_image errors, Debug redaction, reply parsing (no_reply with or without text, other tool calls ignored, missing choices/content), endpoint construction, AI log records (no key, images redacted, no_reply record), logging only when enabled, append-only log writes, retry policy (is_retryable, StatusError message, linear retry_delay), truncation |
whatsapp |
22 + 1 ignored | src/whatsapp.rs:1140+ |
ignored live relink test (new QR with new device keys), fatal-only app state sync reporting, message rendering and placeholders, captions, image references, voice/image detection, silent messages, JID device suffix, push-name filtering |
app |
20 | src/app.rs:1596+ |
autoreply enable rules (private, self-chat LID, groups; broadcasts, newsletters and missing AI refused), web_chats filtering and order, vision applicability, select_images_to_describe (last five in arrival order, known and out-of-context images skipped, nothing without vision), arrived_after, manual_reply_after, description_failure_status, list filter matching, selection, page steps and toggle helpers, AltGr handling, fixed function-key codes |
history |
18 | src/history.rs:543+ |
dedupe by message id, ordering and limits, messages_since (boundary, limit keeps most recent, ascending order), chat name fallbacks, per-chat and global autoreply flags, message_exists, image descriptions (per chat, replaced, deleted with the chat), delete_chat, contacts, on-disk open |
ui |
15 | src/ui.rs:579+ |
footer key help (F4 highlight), version label, sender labels, clock time, QR text, wrapping, visible lines, input tail, messages title, list title (global marker, filter), selection clamp |
web |
11 | src/web.rs:411+ |
query stripping, route resolution (including POST /api/relink, and GET /api/relink not found), X-Wh-Request header check (any case, missing/wrong value), which routes require it (relink included), qr_svg output is an SVG document, request body trimming, empty/invalid UTF-8 rejection and the 1024-byte limit |
main |
2 | src/main.rs:207+ |
only --version / -V request the version; BUILD_VERSION starts with the package version and names a commit |
config_watch |
1 | src/config_watch.rs:54+ |
modification-time snapshots are None for a missing file and change when the file is created and deleted |
Total: 209 test functions — 208 #[test] run by cargo test plus the
ignored #[tokio::test] live relink test. Relink, configuration reload and
F3 targeting have no tests at the App level.
History::open_in_memory() (only compiled for tests,
src/history.rs:78-89); the on-disk test writes under
std::env::temp_dir() in a per-process directory (src/history.rs:837-852).std::env::temp_dir() and removes it (src/ai.rs:861-873).SOUL.md-style prompt files write per-process
scratch files under the target directory and remove them
(scratch_path, src/config.rs:357-383). The config_watch test does the
same with a per-process file under target (src/config_watch.rs:54-73).target and is
ignored by default because it needs the WhatsApp servers.Pages with module details: AI autoreply, Configuration, AI client, WhatsApp client, Web control panel, History store, Terminal UI, Application state, Runtime and event loop.