--- type: testing title: Unit tests description: Overview of the in-module unit test suites in src/*.rs (including the web panel, the version flag, the configuration watcher and the audio transcription client), the ignored live relink test, what each one covers, how they isolate files, and how to run them. tags: [testing, cargo, unit-tests] sources: - id: openwiki-source-651d1fb6c9e49916a916ab51 resource: repo://Cargo.toml - id: openwiki-source-1b05507d7085e8e99a9b64f1 resource: repo://src/ai.rs - id: openwiki-source-9fec9d1af408501c9c455161 resource: repo://src/app.rs - id: openwiki-source-043e6252badc57a1ee6bfb48 resource: repo://src/autoreply.rs - id: openwiki-source-d6b2055bbb49804b8a855fd2 resource: repo://src/config_watch.rs - id: openwiki-source-2a737474d86fc75cc9d9694f resource: repo://src/config.rs - id: openwiki-source-ce0c4ea8cd3b15431fa691ce resource: repo://src/history.rs - id: openwiki-source-b55a21a31ede1b56cd31a6a6 resource: repo://src/main.rs - id: openwiki-source-feb00b7ad73520627c930a9f resource: repo://src/transcribe.rs - id: openwiki-source-6bddea306b9b87f180f1fc02 resource: repo://src/ui.rs - id: openwiki-source-7b75006986d8b0337adc6ca0 resource: repo://src/web.rs - id: openwiki-source-7ea5eac45e5030178a6ea05b resource: repo://src/whatsapp.rs generated: { by: "claude-code", at: "2026-10-25T12:54:53.684Z" } verified: - by: openwiki/0.7.2 at: 2026-10-25T12:54:53.684Z --- # Unit tests 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. ## Running ```sh 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:1293-1313`) is skipped by a plain `cargo test`. It starts a real client on a per-process `target/wh-relink-test-.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](../workflows/relink-and-pairing.md). ## Suites | Module | Tests | Location | Covers | |---|---|---|---| | `autoreply` | 58 | `src/autoreply.rs:529+` | 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, `today_label` (local `dd/mm/yyyy` date, empty for an unresolvable timestamp), `build_turns` (system turn naming private chat, group or self-chat, the `Oggi è .` line when a date is set, 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` | 41 | `src/config.rs:410+` | defaults, full files, ignored legacy keys (`system_prompt`, cooldown keys, `transcribe_command`), blank values, trimming, base URL normalisation, `Debug` masking, DB and AI log paths, `ai_log`, `reply_delay_seconds` (default, custom and `0`, negatives rejected), catch-up and `ai_retries` defaults/custom values/negative rejection, `web_port` (unset, valid, `0` and out of range rejected), transcription keys (unset disables with defaults, trimmed, blank falls back), vision model, 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:589+` | 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` | 23 + 1 ignored | `src/whatsapp.rs:1269+` | ignored live relink test (new QR with new device keys), fatal-only app state sync reporting, message rendering and placeholders, captions, image and audio references, voice/image detection, silent messages, JID device suffix, push-name filtering | | `app` | 23 | `src/app.rs:2041+` | autoreply enable rules (private, self-chat LID, groups; broadcasts, newsletters and missing AI refused), `web_chats` filtering and order (with an empty avatar cache), `descriptions_apply`, `select_media_to_describe` (last five images / last three audios in arrival order, known and out-of-context items skipped, empty when not enabled), `arrived_after`, `manual_reply_after`, description and transcription failure statuses, the voice-note text, list filter matching, selection, page steps and toggle helpers, AltGr handling, fixed function-key codes | | `history` | 19 | `src/history.rs:563+` | dedupe by message id, ordering and limits, `messages_since` (boundary, limit keeps most recent, ascending order), chat name fallbacks and rules, per-chat and global autoreply flags (including survival across incoming messages), `message_exists`, `update_message_text`, image descriptions (per chat, replaced, deleted with the chat), `delete_chat`, contacts, on-disk open | | `transcribe` | 17 | `src/transcribe.rs:299+` | multipart body shape (fields before file part, headers, terminator, bytes unchanged), language rules (`auto`/blank omitted, explicit sent once and trimmed), `audio_suffix` mappings and fallback, MIME sanitisation against header injection, boundary uniqueness, audio validation (empty, oversize), transcription parsing (trim, blank/missing text) | | `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:413+` | 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:218+` | only `--version` / `-V` request the version; `BUILD_VERSION` is `..` plus an all-digit build number (commit count) and a commit hash in parentheses | | `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: 238 test functions — 237 `#[test]` run by `cargo test` plus the ignored `#[tokio::test]` live relink test. Relink, configuration reload, the web panel avatar fetch and F3 targeting have no tests at the `App` level, and the WhatsApp offline presence and `profile_picture_url` are untested. ## Test isolation - History tests use `History::open_in_memory()` (only compiled for tests, `src/history.rs:83-90`); the on-disk test writes under `std::env::temp_dir()` in a per-process directory (`src/history.rs:864-872`). - AI and transcription tests check request bodies, parsing, log records and, for the AI client, the retry policy, without sending requests; the append test writes a per-process file under `std::env::temp_dir()` and removes it (`src/ai.rs:865-877`). - Config tests that read `SOUL.md`-style prompt files write per-process scratch files under the `target` directory and remove them (`scratch_path`, `src/config.rs:439-444`). The `config_watch` test does the same with a per-process file under `target` (`src/config_watch.rs:61-74`). - Web tests exercise the pure routing, parsing and QR rendering helpers; the HTTP server itself is never started. - The live relink test writes its session database under `target` and is ignored by default because it needs the WhatsApp servers. Pages with module details: [AI autoreply](../workflows/ai-autoreply.md), [Configuration](../operations/configuration-and-data.md), [AI client](../integrations/ai-client.md), [WhatsApp client](../integrations/whatsapp-client.md), [Web control panel](../integrations/web-control-panel.md), [History store](../concepts/history-store.md), [Terminal UI](../architecture/terminal-ui.md), [Application state](../architecture/app-state.md), [Runtime and event loop](../architecture/runtime-and-event-loop.md).