Files
bitchat/bitchatTests
jackandClaude Opus 4.8 906165c676 Defer timeout-restore queue drains until Noise convergence completes
A responder timeout restores the quarantined old session generation and
requests one mandatory convergence retry, but both were dispatched
unordered onto the transport queue. When the restore handler won the
race it re-entered handleNoisePeerAuthenticated and drained the pending
private-message and typed-payload queues under the restored keys — keys
a legitimately restarted counterpart may have already discarded after
completing the replacement handshake whose message 3 was lost. Every
drained DM, read receipt, and invite then failed authentication
remotely and disappeared silently.

Restores now carry a reason. Terminal restores (claimed-identity
mismatch, or failures owning no convergence retry) drain immediately as
before: the counterpart never finished replacement keys, so the
restored generation is still valid on both ends. Pending-convergence
restores rebuild the generation-bound protocol state for receive
purposes but leave both outbound queues parked; the convergence retry
(or any later reconnect the revalidation policy initiates) re-enters
the authenticated transition with a fresh generation and drains them
under keys both sides hold. If the retry is cancelled the queues keep
waiting for the next establishment — the same contract every other
queued-before-handshake message already relies on — rather than
gambling them on keys only this side can prove.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 12:54:07 +02:00
..

Test Harness Guide

This test suite uses an in-memory networking harness to make end-to-end and integration tests deterministic, fast, and race-free without touching production code.

In-Memory Bus

  • File: bitchatTests/Mocks/MockBLEService.swift
  • Registry/Adjacency: Global registry maps peerID to a MockBLEService instance; adjacency records simulated links between peers.
  • Setup: Call MockBLEService.resetTestBus() in setUp() to clear state between tests.
  • Topology: Use simulateConnectedPeer(_:) and simulateDisconnectedPeer(_:) to add/remove links. connectFullMesh() helpers in tests build larger topologies.
  • Handlers: Tests can observe data via messageDeliveryHandler (decoded BitchatMessage) and packetDeliveryHandler (raw BitchatPacket).
  • Deduplication: A thread-safe seenMessageIDs prevents duplicate deliveries during flooding/relays.

Broadcast Flooding

  • Flag: MockBLEService.autoFloodEnabled
  • Intent: When true, public broadcasts propagate across the entire connected component (ignores TTL for reach) while still deduping to prevent loops.
  • Usage: Enabled in Integration tests (setUp) to simulate large-network broadcast; disabled in E2E tests to keep routing explicit and verify TTL behavior (see PublicChatE2ETests.testZeroTTLNotRelayed).

Rehandshake Flow (Noise)

  • Why: The legacy NACK recovery path was removed; recovery now relies on Noise session rehandshake after decrypt failure or desync.
  • Manager: NoiseSessionManager manages per-peer sessions.
  • Pattern: On decrypt failure, proactively clear the local session and re-initiate a handshake. The peer accepts and replaces their session.
  • Test: IntegrationTests.testRehandshakeAfterDecryptionFailure
    • Corrupts ciphertext to induce a decrypt error.
    • Calls removeSession(for:) on the initiators manager before initiateHandshake(with:) to avoid alreadyEstablished.
    • Verifies encrypt/decrypt succeeds post-rehandshake.

Tips

  • Determinism: Add small async delays only where handler installation/topology changes could race the first send.
  • Scoping: Keep autoFloodEnabled toggled only within Integration tests; always reset in tearDown() to avoid cross-test contamination.
  • Direct vs Relay: Private messages target a specific peer when adjacent; otherwise they are surfaced to neighbors for relay and, if known, also delivered to the target.

Quick Start

  • Create nodes and connect them:
    • let svc = MockBLEService(); svc.myPeerID = "PEER1"
    • svc.simulateConnectedPeer("PEER2")
  • Observe messages:
    • svc.messageDeliveryHandler = { msg in /* asserts */ }
  • Enable broadcast flooding for Integration suites only:
    • MockBLEService.autoFloodEnabled = true