> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirobody.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing Guide

> How Mirobody's C++ test suite is structured and how to run it.

## Overview

Mirobody's tests are **C++ unit tests written with [Catch2 v3](https://github.com/catchorg/Catch2)**, living under [`tests/`](https://github.com/thetahealth/mirobody/tree/main-v2/tests) and compiled into a single `mirobody_tests` binary. They are built by default and discovered by CTest.

<Info>
  The tests are a **C++ Catch2 suite** — the `*_test.cpp` files under `tests/`.
</Info>

## Running tests

Tests build as part of the normal build (the `MIROBODY_BUILD_TESTS` CMake option defaults to **ON**, except on Android / iOS):

```bash theme={null}
./build.sh                      # builds the engine + tests
ctest --test-dir build          # run the whole suite via CTest
```

Or run the test binary directly and use Catch2's filtering:

```bash theme={null}
./build/tests/mirobody_tests                 # all tests
./build/tests/mirobody_tests "[fhir]"        # only the [fhir]-tagged tests
./build/tests/mirobody_tests "[transcode]"   # only transcoder tests
```

<Note>
  On Windows the binary is `build\tests\mirobody_tests.exe`. The build dir name follows your backend selector (e.g. `build-sqlite\tests\mirobody_tests.exe`).
</Note>

## Test layout

Tests mirror the `src/` module tree. Current coverage includes:

| Area            | Tests                                                                                                                         |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **FHIR**        | `tests/fhir/units_test.cpp` (UCUM normalization), `tests/fhir/resource_test.cpp` (resource validation)                        |
| **Transcode**   | `tests/transcode/document_test.cpp`, `tests/transcode/image_test.cpp`, `tests/transcode/file_test.cpp`                        |
| **LLM clients** | `tests/llm/*` — SSE parser, OpenAI / Gemini chat, realtime / live                                                             |
| **MCP**         | `tests/mcp/tool_test.cpp`, `tests/mcp/resource_test.cpp` (tool / resource registration)                                       |
| **Chat**        | `tests/chat/agent_test.cpp`, `event_test.cpp`, `history_test.cpp`, `persist_test.cpp`                                         |
| **Infra**       | `tests/cache/`, `tests/jwt/`, `tests/oauth/pkce_test.cpp`, `tests/storage/`, `tests/memory/`, `tests/circle/`, `tests/utils/` |

The test target links the self-registering objects (`mcp_tools`, `mcp_resources`, `agent_impls`) directly, the same way the app targets do — that's what lets the registration tests observe compiled-in tools and agents.

## Writing a test

Add a `*_test.cpp` under the matching `tests/<module>/` directory, then list it in [`tests/CMakeLists.txt`](https://github.com/thetahealth/mirobody/blob/main-v2/tests/CMakeLists.txt) in the `add_executable(mirobody_tests ...)` sources.

```cpp theme={null}
#include <catch2/catch_test_macros.hpp>
#include "fhir/units/normalize.hpp"

using namespace mirobody::fhir::units;

TEST_CASE("UCUM normalization folds common spellings", "[fhir][units]") {
    REQUIRE(normalize_unit("MG/DL") == "mg/dL");
    REQUIRE(normalize_unit("mmHg")  == "mm[Hg]");

    ParsedQuantity q = parse_value_unit("<5.6 mg/dL");
    REQUIRE(q.comparator == "<");
    REQUIRE(q.value == Catch::Approx(5.6));
    REQUIRE(q.unit == "mg/dL");
}
```

Tag tests with `[module]` so they can be selected (`mirobody_tests "[units]"`).

## What the unit suite covers — and what it doesn't

* **Pure / DB-free logic** (the units engine, the FHIR resource validator, SSE parsing, PKCE, JWT, HMAC signing) is covered by fast unit tests with no external dependencies.
* **DB-backed paths** (the FHIR REST handlers, chat persistence) are exercised end-to-end against a **running server with a database**, not in the unit binary.
* **Vendor clients** have **no unit tests** in the suite — they call live third-party APIs, so they're verified manually against a running server (see [Provider Testing](/en/development/provider-testing)). Their metadata (`VendorInfo`) is credential-free and could be asserted, but transport is validated by hand.

## Continuous integration

CI runs the build and tests on push. The pipeline is defined in the repository's CI config (`.gitlab-ci.yml`).

## Next steps

<CardGroup cols={2}>
  <Card title="Contributing" icon="code-pull-request" href="/en/development/contributing">
    Build and change workflow
  </Card>

  <Card title="Provider Testing" icon="flask" href="/en/development/provider-testing">
    Verify a vendor client by hand
  </Card>

  <Card title="Provider Integration" icon="plug" href="/en/development/provider-integration">
    Add a new `vendor::Vendor`
  </Card>

  <Card title="File Processing" icon="file" href="/en/development/file-processing">
    The transcoder and its tests
  </Card>
</CardGroup>
