Files
bitchat/bitchatTests
jackandClaude Opus 5 cdfd4345cf Apply the origin-TTL draw everywhere, and stop overclaiming it
Review was right on both counts, and the second one matters more than the
first.

**It was wired into exactly one send path.** Public text drew a TTL;
voice, broadcast files, group messages, board posts and leave all still
originated at the fixed maximum — so those were still perfectly marked as
authored-here, while the privacy assessment said broadcasts were
randomized. A policy that exists but is not applied is worse than none,
because it reads as solved. All six authored-broadcast paths now draw.

Live voice draws **once per talk burst**, not per frame. At ~15 frames a
second a per-frame draw hands an observer the range maximum almost
immediately, so it would have cost reach and bought nothing. A burst is
now one sample, the same as a text message.

Deliberately still fixed, each for a reason now written down: announces
(link binding reads ttl == max as "direct link", and an announce already
names its sender), directed traffic (fewer hops means fewer deliveries —
a real trade that deserves its own change), prekey bundles and gateway
carriers (the payload already identifies its owner; a carrier is a
re-broadcast, not authorship).

**The docs claimed more than the mechanism delivers.** Relays strictly
decrement — every branch of RelayController emits ttlLimit - 1 — so the
top of the range can still only come from an origin. With three values
that is one message in three, and 1 - (2/3)^k, so roughly 87% of senders
are self-identified within five messages. It meaningfully protects an
occasional sender and barely protects a chatty one. Removing the marker
outright needs relays to sometimes not decrement, which trades against
TTL's job as the loop bound, so it is named as follow-up rather than
implied to be done. TransportConfig and the privacy assessment now say
this instead of implying the marker is gone.

Added a wiring guard that reads BLEService and fails if an authored
broadcast origination site uses the fixed maximum without being on an
explicit exclusion list with a reason. Verified it fails: injecting the
old fixed TTL back into the group-message path was caught with file and
line, and it went green again on revert. That is the specific regression
this had, so it is the specific regression now covered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:50:30 +01: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