# Integration Tests Harness
Dockerized integration harness for cross-client torrent interoperability.
Current stable scope:
- `superseedr -> superseedr` (seed + leech)
- `superseedr -> qbittorrent` (seed + leech)
- `qbittorrent -> superseedr` (seed + leech)
Experimental scope:
- `superseedr -> transmission` (seed + leech, currently `v1` only)
- `transmission -> superseedr` (seed + leech, currently `v1` only)
## Purpose
This harness exists to validate real interoperability behavior, not just unit-level correctness.
- Verify that one client can seed data that another client can fully download and validate.
- Catch protocol and metadata compatibility issues across torrent modes (`v1`, `v2`, `hybrid`).
- Produce deterministic artifacts (status snapshots, logs, validator reports) for local debugging and CI triage.
- Provide a stable adapter/scenario framework so additional clients can be added with minimal redesign.
## Test Design
Each mode run (`v1`, `v2`, or `hybrid`) follows the same design:
1. Generate deterministic fixture binaries and mode-specific torrent files with a local announce URL.
2. Create isolated runtime directories per run/mode (seed data, leech output, configs, logs).
3. Start Docker services in controlled order:
- build image once
- start tracker first and wait for readiness
- start seed + leech clients
4. Poll client status periodically (currently Superseedr state JSON) and write normalized snapshots.
5. Validate leech output against expected filesystem manifest/hash from `integration_tests/test_data`.
6. Emit per-mode artifacts and final run summary.
Pass criteria:
- No missing files and no hash/content mismatches in the leech output.
Failure criteria:
- Timeout before convergence, or any missing/mismatched files.
## Requirements
- Docker + Docker Compose plugin (`docker compose`)
- Python 3.12+ (3.10+ may work, CI uses 3.12)
- Python deps from `requirements-integration.txt`
- Git checkout of this repo
- Optional: Rust/Cargo for broader project tests (`cargo test`) outside this harness
Install Python dependencies:
```bash
python3 -m pip install -r requirements-integration.txt
```
## Commands
### Main local entrypoint
```bash
./integration_tests/run_interop.sh [all|v1|v2|hybrid] [scenario]
```
Environment variables:
- `INTEROP_TIMEOUT_SECS` (default `300`): per-mode timeout
- `INTEROP_SCENARIO` (default `superseedr_to_superseedr`): scenario name when not passed as arg 2
Example:
```bash
INTEROP_TIMEOUT_SECS=300 ./integration_tests/run_interop.sh all
```
Example (mixed client):
```bash
INTEROP_TIMEOUT_SECS=300 ./integration_tests/run_interop.sh all superseedr_to_qbittorrent
```
### Direct Python harness entrypoint
```bash
python3 -m integration_tests.harness.run \
--scenario superseedr_to_superseedr \
[--run-id run_YYYYMMDD_HHMMSS] \
[--skip-generation]
```
Accepted arguments:
- `--scenario`: one of:
- `superseedr_to_superseedr`
- `superseedr_to_qbittorrent`
- `qbittorrent_to_superseedr`
- `superseedr_to_transmission` (experimental)
- `transmission_to_superseedr` (experimental)
- `--mode`: `all`, `v1`, `v2`, `hybrid`
- `--timeout-secs`: timeout per mode in seconds
- `--run-id`: optional explicit run id
- `--skip-generation`: skip fixture/torrent regeneration
### Pytest wrapper
Unit tests (fast, no Docker):
```bash
python3 -m pytest integration_tests/harness/tests -m "not interop"
```
Interop tests via pytest (Docker):
```bash
RUN_INTEROP=1 INTEROP_TIMEOUT_SECS=300 \
python3 -m pytest integration_tests/harness/tests -m interop
```
## Cluster CLI Harness
The cluster CLI lane is separate from the Docker interop harness, but it follows
the same Docker-first testing model. It runs two Linux Superseedr containers
against one mounted shared root, reuses checked-in torrent fixtures, and
exercises shared offline, leader, follower, failover, and failback CLI flows.
Main entrypoints:
```bash
./integration_tests/run_cluster_cli.sh
```
or:
```bash
python3 -m integration_tests.cluster_cli.run
```
Pytest wrapper:
```bash
RUN_CLUSTER_CLI=1 python3 -m pytest integration_tests/cluster_cli/tests -m cluster_cli
```
Artifacts are written under:
- `integration_tests/artifacts/cluster_cli/<run_id>/`
## Artifacts and Monitoring
Per run output:
- `integration_tests/artifacts/runs/<run_id>/summary.json`
- `integration_tests/artifacts/runs/<run_id>/<mode>/validator_report.json`
- `integration_tests/artifacts/runs/<run_id>/<mode>/normalized_status.json`
- `integration_tests/artifacts/runs/<run_id>/<mode>/raw_client_status/*`
- `integration_tests/artifacts/runs/<run_id>/<mode>/logs/*`
Monitoring model:
- Superseedr is polled via its status JSON (`app_state.json`) and normalized into harness snapshots.
- Final pass/fail is determined by filesystem manifest/hash validation vs `integration_tests/test_data`.
- Tracker readiness is explicitly waited on before starting seed/leech services to reduce hybrid-mode flakes.
## CI
GitHub Actions workflow:
- `.github/workflows/integration-interop.yml`
- `.github/workflows/integration-cluster-cli.yml`
Behavior:
- Runs matrix over scenarios and modes:
- full modes (`v1`, `v2`, `hybrid`): `superseedr_to_superseedr`, `superseedr_to_qbittorrent`, `qbittorrent_to_superseedr`
- `v1` only: `superseedr_to_transmission`, `transmission_to_superseedr`
- Supports manual `workflow_dispatch` inputs:
- `mode` (`all|v1|v2|hybrid`)
- `timeout_secs`
- Uploads artifacts from `integration_tests/artifacts/`
## Current Status
As of February 21, 2026:
- `superseedr -> superseedr` passes for `v1`, `v2`, and `hybrid`.
- `superseedr -> qbittorrent` passes for `v1`, `v2`, and `hybrid`, with manifest/hash validation.
- `qbittorrent -> superseedr` now runs ungated in pytest and passes for `v1`, `v2`, and `hybrid` in local validation.
- qBittorrent container/auth/add/polling/log collection are implemented in `integration_tests/harness/clients/qbittorrent.py`.
- CI interop matrix now enforces all three scenarios (`superseedr_to_superseedr`, `superseedr_to_qbittorrent`, `qbittorrent_to_superseedr`) across all three modes.
- qBittorrent and tracker host ports are dynamically allocated in qBittorrent scenarios/tests to reduce local port-collision flakes.
- Transmission adapter now supports auth/session handshake, torrent add, status polling, and log collection.
- Transmission scenarios now run in CI for `v1` (`superseedr_to_transmission`, `transmission_to_superseedr`).
- Transmission `v2`/`hybrid` adds currently fail with RPC result `unrecognized info` on the linuxserver image.
## Plan / Next Tasks
1. Validate Transmission `v2`/`hybrid` compatibility and enable non-`v1` modes when supported.
2. Add focused diagnostics for reverse failures (piece-level mapping/torrent-level correlation) to shorten triage loops.
3. Extend transmission CI coverage to `v2`/`hybrid` once compatibility is available.