Metadata-Version: 2.4
Name: choreo-harness
Version: 1.0.0
Summary: Choreo — a declarative async test harness for message-driven systems
Project-URL: Homepage, https://github.com/clear-route/choreo
Project-URL: Repository, https://github.com/clear-route/choreo
Project-URL: Issues, https://github.com/clear-route/choreo/issues
Project-URL: Changelog, https://github.com/clear-route/choreo/blob/main/CHANGELOG.md
Author: The Choreo Authors
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: async,kafka,messaging,nats,pubsub,pytest,rabbitmq,redis,testing
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: System :: Distributed Computing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: pyyaml<7,>=6.0
Provides-Extra: kafka
Requires-Dist: aiokafka<1,>=0.11; extra == 'kafka'
Provides-Extra: nats
Requires-Dist: nats-py<3,>=2.7; extra == 'nats'
Provides-Extra: rabbitmq
Requires-Dist: aio-pika<11,>=9.0; extra == 'rabbitmq'
Provides-Extra: redis
Requires-Dist: redis<7,>=5.0; extra == 'redis'
Provides-Extra: test
Requires-Dist: hypothesis<7,>=6.100; extra == 'test'
Requires-Dist: pytest-asyncio<2.0,>=0.24; extra == 'test'
Requires-Dist: pytest<10,>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# choreo — Choreo test harness library

An async Python test framework for message-driven systems. Write tests that
declare *"when I publish X, I expect Y"* and the harness handles routing,
correlation, timing, and reporting.

The library is transport-agnostic. Plug in a transport — `MockTransport` for
unit tests, `NatsTransport` / `KafkaTransport` / `RabbitTransport` /
`RedisTransport` for end-to-end, or your own — and the same scenario DSL
works against all of them.

- Python 3.11+
- No runtime dependencies; `pytest`, `pytest-asyncio`, and `pyyaml` are test
  extras only.
- Transport client libraries ship as optional extras
  (`pip install 'choreo[nats]'`, `choreo[kafka]`, `choreo[rabbitmq]`, `choreo[redis]`).

## Install

```bash
pip install choreo               # library only
pip install 'choreo[nats]'         # + NATS client for the e2e suite
pip install 'choreo[nats,test]'    # + pytest + pytest-asyncio + pyyaml
```

Pair with the companion reporter plugin for HTML + JSON test output:

```bash
pip install choreo-reporter
```

## Correlation policy

The library ships with three correlation profiles (ADR-0019):

```python
from choreo import Harness, NoCorrelationPolicy, DictFieldPolicy, test_namespace

# Default — transparent passthrough. Payloads are unchanged; every live scope
# on a topic sees every message (broadcast fallback). Safe on dedicated or
# per-run infrastructure; unsafe on a shared broker.
Harness(transport)

# Opt in to per-scope isolation by stamping/reading a dict field.
Harness(transport, correlation=DictFieldPolicy(field="trace_id", prefix="run-abc-"))

# Opt in to the TEST- prefix posture (downstream ingress filters on `TEST-`).
Harness(transport, correlation=test_namespace())
```

Custom policies implement the `CorrelationPolicy` protocol (`new_id`,
`write`, `read`, `routes_by_correlation`) and can stamp into any shape
the consumer's schema requires — a dict field, a transport header, a
tag-value-protocol tag, a protobuf field. See the ADR for the protocol
contract and the trust-boundary rules.

## Multi-transport scenarios

For tests that span two transports — typically a bridge or protocol
translator AUT that consumes on one wire and republishes on another,
or an orchestrator that fans out to many connected devices — use
`Stage`. A `Stage` wraps a named registry of `Harness` instances and
a `CorrelationBridge` so a single scenario can publish on transport
A, register a reactive reply on transport B, and assert on transport
A again, all under one deadline.

```python
from choreo import DictFieldPolicy, Harness, MappedBridge, Stage
from choreo.transports import KafkaTransport, NatsTransport

stage = Stage(
    harnesses={
        "kafka": Harness(KafkaTransport(...), correlation=DictFieldPolicy(field="correlation_id")),
        "nats":  Harness(NatsTransport(...),  correlation=DictFieldPolicy(field="correlation_id")),
    },
    bridge=MappedBridge(forwards={
        "kafka": lambda l: f"kafka-{l}",
        "nats":  lambda l: f"nats-{l}",
    }),
)
```

Single-transport tests should keep using a plain `Harness` — `Stage`
adds correlation translation and per-transport routing concepts that
cost nothing on the multi-transport path but are unnecessary noise
for a single-transport test.

See `examples/06-multi-transport-bridge/` and the
[Stage user guide](https://github.com/clear-route/choreo/blob/main/docs/guides/stage.md)
for the full pattern.

## Examples

Runnable example projects live in the repo's `examples/` directory:

- `examples/01-hello-world/` — minimum useful test.
- `examples/02-request-reply/` — staging a fake upstream with `on().publish()`.
- `examples/03-parallel-isolation/` — opting into a `CorrelationPolicy`.
- `examples/04-transport-auth/` — wiring a typed `auth=` descriptor into a transport.
- `examples/05-auth-resolver/` — fetching credentials at `connect()` time via sync / async resolvers.
- `examples/06-multi-transport-bridge/` — testing a multi-transport bridge / orchestrator with `Stage`.

```bash
pytest examples/01-hello-world/
```

## Documentation

See the project README at
<https://github.com/clear-route/choreo> for architecture, the Scenario DSL,
matchers, transports, and the downstream-consumer fixture pattern.

## Licence

Apache-2.0. See [LICENSE](LICENSE).
