Courier spray: scope the overlap seam per store, and pin the receipt identity

Two more from cross-model review (codex).

The overlap seam was a mutable static. Swift Testing runs cases in parallel, so
the three tests that install it could overwrite each other's hook and tally an
overlap onto the wrong test — a flake that would have shown up as an
unreproducible count long after this landed. It is per-instance now; each test
already owns its store, so each owns its seam, and no serialization is needed.

Separately, ciphertextHash had no pinned value anywhere. Every use in the test
file derived it exactly as production does — CourierStore.ciphertextHash on
both sides of the assertion — so a change to the derivation moved both sides
and every test stayed green, while every already-shipped peer's receipts stopped
matching. The bytes are written down once now.

That pin uses the published courier vector's input and expected value, so it
moves into the vectors file unchanged once that lands. It is here rather than
there because this is where the function it checks lives.

Mutation-verified: changing the production truncation from 16 bytes to 12 fails
that one test and only that one. Four consecutive runs of the suite pass with
the per-instance seam.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ecgang 2026-07-26 11:53:55 -07:00
parent 85ef044af6
commit c1100273c3
2 changed files with 28 additions and 8 deletions

View File

@ -169,7 +169,11 @@ final class CourierStore {
/// Test seam for the overlap detector, mirroring `_test_onOutboundPacket`
/// in `BLEService`. Unset in normal debug runs, where an overlap trips
/// `assertionFailure` instead.
static var _test_onSprayOfferOverlap: (() -> Void)?
///
/// Per instance, not static: Swift Testing runs cases in parallel, so a
/// shared hook would let one case overwrite another's and tally overlaps
/// onto the wrong test. Each test owns its store, so each owns its seam.
var _test_onSprayOfferOverlap: (() -> Void)?
#endif
/// Marks the start of a spray offer's scan-to-commit span, reporting an
@ -179,7 +183,7 @@ final class CourierStore {
queue.sync {
defer { sprayOffersInFlight += 1 }
guard sprayOffersInFlight > 0 else { return }
if let hook = Self._test_onSprayOfferOverlap {
if let hook = _test_onSprayOfferOverlap {
hook()
} else {
assertionFailure("""

View File

@ -733,6 +733,25 @@ struct CourierStoreTests {
#expect(offerAll(store, to: courierB) == 2)
}
/// Pins the envelope identity a spray receipt carries.
///
/// Every other use in this file derives the hash the same way production
/// does `CourierStore.ciphertextHash(envelope.ciphertext)` on both sides
/// so a change to the derivation moves both and every one of them stays
/// green. This is the one place the bytes are written down, which is the
/// only way a receipt-identity change can fail a test rather than silently
/// re-key every pending offer and break interop with an already-shipped
/// peer.
///
/// Input and expected value are the published courier vector's, so when
/// the vectors file lands this assertion moves into it unchanged.
@Test func ciphertextHashIsPinnedSHA256TruncatedTo16() {
let ciphertext = Data("courier-vector-ciphertext-0001".utf8)
#expect(CourierStore.ciphertextHash(ciphertext).hexEncodedString()
== "bb85dcc4d8b17377c61817992df95826")
#expect(CourierStore.ciphertextHash(ciphertext).count == CourierEnvelope.tagLength)
}
#if DEBUG
/// The scan-to-commit span guard.
///
@ -756,8 +775,7 @@ struct CourierStoreTests {
let courierB = Data(repeating: 0xC2, count: 32)
let overlaps = OverlapCounter()
CourierStore._test_onSprayOfferOverlap = { overlaps.count += 1 }
defer { CourierStore._test_onSprayOfferOverlap = nil }
store._test_onSprayOfferOverlap = { overlaps.count += 1 }
_ = store.offerSprayCopies(to: courierA) { _ in
// Copies for A are on the wire; B now scans before A has committed.
@ -781,8 +799,7 @@ struct CourierStoreTests {
let courierB = Data(repeating: 0xC2, count: 32)
let overlaps = OverlapCounter()
CourierStore._test_onSprayOfferOverlap = { overlaps.count += 1 }
defer { CourierStore._test_onSprayOfferOverlap = nil }
store._test_onSprayOfferOverlap = { overlaps.count += 1 }
_ = store.offerSprayCopies(to: courierA) { _ in
_ = store.takeSprayCopies(for: courierB)
@ -808,8 +825,7 @@ struct CourierStoreTests {
let courierC = Data(repeating: 0xC3, count: 32)
let overlaps = OverlapCounter()
CourierStore._test_onSprayOfferOverlap = { overlaps.count += 1 }
defer { CourierStore._test_onSprayOfferOverlap = nil }
store._test_onSprayOfferOverlap = { overlaps.count += 1 }
_ = store.offerSprayCopies(to: courierA) { _ in
// B overlaps A and finishes; A is still in flight afterwards.