# Testing

> For the complete documentation index, see [llms.txt](https://docs.lertha.com/llms.txt).
> Any documentation page is available as raw Markdown by appending `.md` to its URL.

> The in-memory engine for application tests, and the contract every engine must pass.

## Application tests: the in-memory engine

`InMemoryOrchestration` implements both `Orchestrator` and `Runner` in one object, with no
server. Retries, id policies, cancellation at heartbeats, timeouts and signals all behave as the
port specifies, so a service that takes an `Orchestrator` is tested with the real semantics and
none of the infrastructure.

```kotlin
val engine = InMemoryOrchestration().apply {
    register(GreetingTask)
    register(AgentRun)
    start()
}
val service = OnboardingService(orchestrator = engine)
```

Flows run their body on a plain thread and block on it, which is what an engine does too, minus
the durability. Cancellation and timeouts reach the body as thread interruption.

## Engine tests: the contract

`OrchestratorContract` (tasks, 10 tests) and `FlowContract` (flows, 10 tests) live in the api
module's test fixtures. An adapter passes them or it is not an adapter. They cover:

- typed results, status after completion, re-attaching by id
- retry until success, retry ceiling, non-retryable stop
- `FAIL` and `USE_EXISTING` on a duplicate id
- cancel observed at the next heartbeat, attempt timeout
- sequence, fan-out, signals (including ones sent before the body asks), receive with timeout,
  catching a failed task, a body that throws, sleep on engine time, cancel while waiting

The engine adapter's contract spawns a real headless dev orchestrator per test class. With
`LERTHA_FLOW_ADDRESS=host:port` set, the same suite runs against a live orchestrator instead:
that is the acceptance test for every orchestrator upgrade.

```bash
LERTHA_FLOW_ADDRESS=127.0.0.1:7233 ./gradlew test --rerun-tasks
```

## One JUnit trap

A Kotlin test whose last expression returns a value has a non-`Unit` return type, and JUnit
silently skips it. End such tests with `Unit`.
