# How to Write Unit and Integration Tests for the i-have-adhd Extension

> Learn to write unit and integration tests for the i-have-adhd extension. Explore the comprehensive test suite demonstrating component tests and runtime integration exercises.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: how-to-guide
- Published: 2026-08-19

---

**The i-have-adhd repository ships a complete test suite that demonstrates both unit-style tests for individual components and integration-style smoke tests that exercise the extension through the Pi and OMP runtimes.**

The `ayghri/i-have-adhd` repository provides a fully functional harness to write unit or integration tests for the i-have-adhd extension. By mirroring the existing patterns in `tests/` and `scripts/`, you can validate everything from front-matter stripping to full command-toggle workflows in isolated agent environments.

## Unit Test Architecture and Key Files

The **unit test** layer focuses on **pure functions** and small behaviors without spawning a full runtime. The extension entry point in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) exports **`stripFrontmatter`** for front-matter stripping, while [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) provides **`contextMessages`** and **`latestMarkerIsActive`** for state inspection.

The primary unit-level test files are:

- [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py) — Validates platform-specific hook scripts that inject the ADHD banner when an opt-in flag is present.
- [`tests/test_opencode_plugin.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_opencode_plugin.py) — Covers the OpenCode plugin version of the extension.
- [`tests/test_omp_package.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_omp_package.py) — Ensures the extension manifest is correctly declared for both Pi and OMP runtimes.

These files use standard **`unittest.TestCase`** classes and import utilities directly from the `extensions/` package using the repository’s module path structure.

## Writing a New Unit Test

When you add a pure-function change, create a new **`unittest.TestCase`** in the `tests/` directory and import the target function from its module path. For example, `stripFrontmatter` as implemented in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) can be tested like this:

```python
import unittest
from extensions.i_have_adhd import stripFrontmatter  # type: ignore

class FrontMatterTest(unittest.TestCase):
    def test_strips_simple_frontmatter(self):
        src = "---\nname: demo\n---\nHello world"
        self.assertEqual(stripFrontmatter(src), "Hello world")

```

This pattern follows the existing hook tests in [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py), which rely on repository-relative imports and runtime-agnostic helpers such as **`run_hook`** and **`run_codex_hook`** to execute scripts in isolation.

## Integration Test Architecture

**Integration tests** spin up a temporary Pi or OMP agent, load the extension, issue commands, and assert on the resulting session state. The main driver is [`scripts/check_pi_extension.py`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_pi_extension.py), which defines the **`RpcClient`** class and provides verification helpers like **`message_count`**, **`latest_enabled`**, and **`status_texts`**.

The integration layer validates end-to-end behaviors such as:

- Command registration, confirming that `/i-have-adhd` appears in the available command list.
- Toggling ADHD mode on and off via RPC prompts.
- Verifying that UI status texts and session entry markers update correctly after each command.

## Writing a New Integration Smoke Test

To cover end-to-end behavior, extend the pattern used in [`scripts/check_pi_extension.py`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_pi_extension.py). The following skeleton builds an isolated environment, instantiates an **`RpcClient`**, and asserts that toggling the extension off clears the UI status and adds the disabled marker:

```python
import unittest
from pathlib import Path
from scripts.check_pi_extension import RpcClient, build_isolated_env, status_texts, message_count

class IHaveADHDSmokeTest(unittest.TestCase):
    def test_toggle_off(self):
        from scripts.check_pi_extension import ROOT
        env = build_isolated_env(agent_dir="tmp-agent")
        client = RpcClient(
            "pi",
            env,
            "--no-session",
            "-e",
            str(ROOT / "extensions/i-have-adhd.ts")
        )

        try:
            cmds, _ = client.request("commands", {"type": "get_commands"})
            self.assertIn("i-have-adhd", {c["name"] for c in cmds["data"]["commands"]})

            client.request("toggle-on", {"type": "prompt", "message": "/i-have-adhd"})
            off_evt = client.request("toggle-off", {"type": "prompt", "message": "/i-have-adhd"})[1]
            self.assertIn(None, status_texts(off_evt))

            entries, _ = client.request("entries", {"type": "get_entries"})
            self.assertEqual(message_count(entries, "i-have-adhd-disabled"), 1)
        finally:
            client.close()

```

Key steps in this flow:

1. **Isolate the environment** — **`build_isolated_env`** creates a temporary agent directory.
2. **Instantiate the client** — **`RpcClient`** launches the Pi or OMP runtime with the extension entry point loaded.
3. **Verify registration** — Query the command list to ensure `i-have-adhd` is present.
4. **Drive the toggle** — Send prompt messages via RPC and inspect UI events using **`status_texts`**.
5. **Assert on session state** — Fetch entries and use **`message_count`** to confirm markers such as `i-have-adhd-disabled` appear exactly once.

## Testing the Always-On Hook Scripts

The **always-on hook** scripts inject the ADHD banner when an opt-in flag is detected. The file [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py) already verifies that these scripts correctly strip front-matter and respect the opt-in flag across supported runtimes.

To add coverage for a new runtime, extend the test class with a new case that invokes your hook via the existing **`run_hook`** helper:

```python
def test_new_runtime_respects_opt_in(self):
    command = ["node", self.plugin_root / "hooks" / "custom-runtime.mjs"]
    result = self.run_hook(command)
    self.assertEqual(0, result.returncode)

```

This approach keeps hook tests runtime-agnostic by relying on standard subprocess execution and output assertions.

## Running the Tests Locally

Execute the unit tests using Python’s built-in discovery:

```bash
python3 -m unittest discover -s tests -v

```

Run the integration smoke tests directly from the driver script for either runtime:

```bash
python3 scripts/check_pi_extension.py --runtime pi
python3 scripts/check_pi_extension.py --runtime omp

```

Both commands print a confirmation message when the extension behaves as expected. New test files added under `tests/` are picked up automatically as long as they follow the `test_*.py` naming convention.

## Summary

- The `ayghri/i-have-adhd` repository separates **unit tests** for pure functions and hooks from **integration smoke tests** that drive the full Pi or OMP runtime.
- Write unit tests by importing functions such as **`stripFrontmatter`** from `extensions.i_have_adhd` into standard `unittest` classes.
- Write integration tests by reusing **`RpcClient`**, **`build_isolated_env`**, **`status_texts`**, and **`message_count`** from [`scripts/check_pi_extension.py`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_pi_extension.py).
- Validate always-on hooks with the **`run_hook`** helper in [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py) to assert correct front-matter handling and opt-in behavior.
- Run the full suite with `python3 -m unittest discover -s tests -v` and the RPC driver with `python3 scripts/check_pi_extension.py`.

## Frequently Asked Questions

### How do I write unit tests for the i-have-adhd extension?

Create a Python file under `tests/` that imports the function you want to exercise from the `extensions/` package. Use the standard `unittest` library and assert on return values from pure functions like `stripFrontmatter`, following the pattern established in [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py).

### How do I write integration tests for the i-have-adhd extension?

Use the `RpcClient` class defined in [`scripts/check_pi_extension.py`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_pi_extension.py) to spin up a temporary Pi or OMP agent with the extension loaded. Send RPC commands to trigger `/i-have-adhd`, then inspect the returned events with helpers like `status_texts` and `message_count` to verify session state changes.

### What is the fastest way to run the i-have-adhd test suite?

Run `python3 -m unittest discover -s tests -v` for unit tests, or execute `python3 scripts/check_pi_extension.py --runtime pi` (or `--runtime omp`) for integration smoke tests. Both commands report pass or fail status directly in the terminal.

### Where are the main source files for the i-have-adhd extension?

The entry point is [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts), which registers commands and manages rule injection. Context utilities such as `contextMessages` and `latestMarkerIsActive` live in [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts), and both are exercised by the existing unit and integration tests according to the `ayghri/i-have-adhd` source code.