4.3 KiB
Local 2×2 route E2E
This harness runs one bot-free control stack plus four isolated data-node
Compose projects. Every data node contains its own qBittorrent, Syncthing, and
archive-client containers and its own bind-mounted config, secrets, state,
backup, qB data, and sync roots. The stacks share only the explicitly named
archive-control-e2e network.
The initial scenario starts with no route folders. With the control policy set
to eager_mesh, two cache and two archive registrations must produce four
independent, bidirectionally verified Syncthing routes. The assertion talks to
the loopback-only test adapter through a curl sidecar sharing the control
container's network namespace; Telegram is not initialized.
Run from the archive-clients checkout:
./e2e/scripts/up.sh
./e2e/scripts/logs.sh
./e2e/scripts/down.sh
up.sh defaults ARCHIVE_CONTROL_SOURCE to the sibling playground
mogic-bot/mogic-repo checkout. Set that environment variable to test another
control worktree. E2E_WAIT_SECONDS overrides the 360-second assertion
deadline. down.sh removes only the five exact Compose projects and the
labelled E2E network; it intentionally retains all bind-mounted runtime state.
Set E2E_RUN_TRANSFER=1 to follow route verification with a real
cache-1 → archive-1 transfer, covered cache-1 eviction, and
archive-1 → cache-2 unarchive. The scenario creates a deterministic one-file
torrent in the isolated cache qBittorrent, uses fresh preview revisions for all
jobs, waits for the durable five/three/five-step flows, and verifies target
qBittorrent selections, archive retention, safe cache removal, and target file
digests.
Set E2E_RUN_COMPLEX=1 to run the Phase 8 selective and recovery matrix. It
uses a four-file torrent with complementary cache selections to verify
selective archive placement, multi-job archive/cache merging, union coverage
across two archive nodes, uncovered/no-op/stale-preview rejection, and
retention of shared and unknown files during eviction. It then pauses the
test scheduler at durable boundaries to verify record-only queued
cancellation, rollback after target materialization, control restart after
source staging, and command replay after a target client disconnect. The final
hostile cases change the source selection and make an absent target appear
after confirmation; each must fail before commit with its exact precondition
reason. qBittorrent is also checked for zero downloaded bytes at the replayed
target. Finally, a wrong-size pre-existing target and an unwritable target root
must fail without committing or losing the source; the permission fixture
restores the target root's original mode even on an aborted run.
The scheduler pause/resume endpoints exist only on the loopback test adapter; normal control and Telegram orchestration always use the automatic scheduler.
Set E2E_RUN_ADVERSARIAL=1 to run the Phase 8 v2/fault tranche in
one pass. The scenario performs archive → eviction → unarchive round trips for
a pure-v2 torrent and a hybrid torrent, checking that hybrid placements retain
both identities. It then:
- leaves a Syncthing command durable while archive-2 is offline, waits beyond
the five-second E2E stall threshold, verifies the job retains overall
progress while
STALLED, and reconnects the client to finish it; - recreates archive-2 with
client-exhausted.toml, whose impossible free-space reserve exercises the real capacity guard without consuming disk, and verifies a precise precommit failure with the source intact; and - runs a separate
protocol-probecontainer in the control network namespace. The probe sends job event 2 before event 1, requires a snapshot request and stale-state error, sends event 1 twice, and replays event 2 to prove reordered convergence and duplicate idempotency over the real WebSocket.
The adversarial script restores the normal archive-2 client configuration and automatic scheduler through an exit trap. It passed from a clean five-project topology on 2026-07-23. Run it with:
E2E_RUN_ADVERSARIAL=1 \
ARCHIVE_CONTROL_SOURCE=/path/to/mogic-bot \
./e2e/scripts/up.sh
The Syncthing 2.1.2 and LinuxServer qBittorrent multi-platform image indexes are digest-pinned. Runtime secrets are generated with mode 0600 and ignored by Git. The qBittorrent test config limits its authentication bypass to the isolated E2E network and must never be reused for deployment.