assertAbortsOnMidHandshakeCollapse

inline suspend fun <E : Throwable> TestScope.assertAbortsOnMidHandshakeCollapse(seam: FakeSeam, drainedPeer: PeerId, timeout: Duration = 5.seconds, crossinline establish: suspend () -> Unit)

Consumer-level invariant: an establishment await over a controllable seam must abort (throw within a bound) when membership drains mid-handshake — it must never suspend forever.

What it models — the #1466 hardware failure

A membership drain is distinct from a transport tear: Seam.peers collapses to {self} while Seam.state stays us.tractat.kuilt.core.SeamState.Woven — the co-elector simply left the roster, no close() and no Torn latch. A consumer that only wakes on a seam tear (the transport-death half) silently suspends forever on this event; that is exactly how #1466 shipped green. FakeSeam.removePeer reproduces the drain precisely (drops _peers, leaves state us.tractat.kuilt.core.SeamState.Woven) — this helper drives it.

The protocol it drives

  1. Launch establish in a caught driver coroutine so a thrown exception is captured, not propagated to (and cancelling) the test scope.

  2. runCurrent — let establish subscribe to incoming / broadcast its first handshake frame and suspend at the mid-handshake point (awaiting a Freeze, a FreezeAck, etc.).

  3. Drain membership: FakeSeam.removePeer drops drainedPeer from peers — the seam stays Woven (no tear). This is the collapse.

  4. Assert establish resolves by throwing E within timeout. A TimeoutCancellationException here means establish suspended past the bound — the very hang this invariant forbids — and is reported as a loud AssertionError, never a silent timeout.

Runs under TestScope; pair with runTest(timeout = TEST_WEDGE_BACKSTOP) and a StandardTestDispatcher.

This does not depend on kotlin-test (not a commonMain dependency of :kuilt-test); it raises a plain AssertionError on failure, which every kotlin-test runner surfaces identically.

Parameters

seam

the controllable seam the consumer establishes over — must be the same FakeSeam reads, so drainedPeer's removal is observed by the in-flight handshake.

drainedPeer

the co-elector to drop mid-handshake (the elected host, or the awaited member).

Type Parameters

E

the terminal exception establish must throw on the collapse (e.g. LobbyTornException).