Reset BLEService's own receipt cache on panic completion

The panic media wipe runs on the file store PanicRecoveryOperations
.live() constructs, but receipt lookups are served by BLEService's own
incomingFileStore, whose in-memory index survived the wipe. Drop that
instance's cache in completePanicReset — after media deletion, before
radio admission reopens — so the next lookup rebuilds from the wiped
directory. The panic test now drives the production suspend/wipe/
complete sequence against the instance BLEService actually uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jack
2026-07-26 13:15:41 +02:00
co-authored by Claude Opus 4.8
parent 52709addc1
commit 9df056d6a8
3 changed files with 54 additions and 4 deletions
@@ -882,16 +882,40 @@ struct BLEFileTransferHandlerTests {
let seed = BLEPrivateMediaReceiptStore(baseDirectory: base)
#expect(seed.recordDeleted(messageID: messageID))
let store = BLEIncomingFileStore(baseDirectory: base)
// Production wiring: receipt lookups run against the service's OWN
// incoming-file store while the panic wipe runs on the separate store
// `PanicRecoveryOperations.live()` constructs. The test must reset
// the instance BLEService uses, not a same-instance shortcut.
let keychain = MockKeychain()
let identityManager = MockIdentityManager(keychain)
let service = BLEService(
keychain: keychain,
idBridge: NostrIdentityBridge(keychain: MockKeychainHelper()),
identityManager: identityManager,
initializeBluetoothManagers: false,
incomingFileStore: BLEIncomingFileStore(baseDirectory: base)
)
#expect(
store.privateMediaReceiptState(messageID: messageID)
service._test_privateMediaReceiptState(messageID: messageID)
== .tombstoned
)
try store.panicWipe()
service.suspendForPanicReset()
// A receive callback drained during suspension can still consult the
// ledger and re-cache the pre-wipe decision before media deletion.
#expect(
service._test_privateMediaReceiptState(messageID: messageID)
== .tombstoned
)
// The wipe itself runs on the recovery operations' distinct store,
// exactly like ChatViewModel's panic transaction.
let recoveryStore = BLEIncomingFileStore(baseDirectory: base)
try recoveryStore.panicWipe()
service.completePanicReset(restartServices: false)
#expect(
store.privateMediaReceiptState(messageID: messageID)
service._test_privateMediaReceiptState(messageID: messageID)
== .absent
)
}