Files
bitchat/bitchatTests/Services/BLEOutboundFragmentTransferSchedulerTests.swift
9e84f5e822 [codex] Refactor BLE outbound scheduling and Noise queues (#1306)
* Refactor BLE transport event handling

* Make image output paths unique

* Keep queued Nostr read receipts alive

* Refine BLE ingress fanout

* Rediscover BLE service after invalidation

* Extract BLE notification retry buffer

* Extract BLE inbound write buffer

* Extract BLE fragment assembly buffer

* Tidy secure log handling from device run

* Extract BLE outbound fragment scheduler

* Harden app CI media tests

* Redact BLE message content from logs

* Extract BLE Noise session queues

* Fix BLE read receipt UI updates

* Allow self-authored RSR ingress replies

* Harden read receipt queue test timing

* Extract BLE outbound policy and incoming file storage

* Avoid duplicate BLE link snapshots during send

* Canonicalize Nostr relay URLs

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2026-05-31 14:16:11 +02:00

149 lines
6.0 KiB
Swift

import BitFoundation
import Foundation
import Testing
@testable import bitchat
struct BLEOutboundFragmentTransferSchedulerTests {
@Test
func submitStartsPublicMessageWithoutTransferReservation() {
var scheduler = BLEOutboundFragmentTransferScheduler()
let request = makeRequest(type: MessageType.message.rawValue, transferId: nil)
let result = scheduler.submit(request, maxConcurrentTransfers: 1)
if case let .start(_, reservedTransferId) = result {
#expect(reservedTransferId == nil)
#expect(scheduler.activeCount == 0)
#expect(scheduler.pendingCount == 0)
} else {
Issue.record("Expected non-file fragments to start without reserving a transfer slot")
}
}
@Test
func submitQueuesFileTransferWhenSlotsAreFull() {
var scheduler = BLEOutboundFragmentTransferScheduler()
let first = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "first")
let second = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "second")
guard case let .start(_, firstReservation?) = scheduler.submit(first, maxConcurrentTransfers: 1) else {
Issue.record("Expected first file transfer to reserve a slot")
return
}
#expect(firstReservation == "first")
let result = scheduler.submit(second, maxConcurrentTransfers: 1)
if case let .queued(_, transferId, position) = result {
#expect(transferId == "second")
#expect(position == .back)
#expect(scheduler.activeCount == 1)
#expect(scheduler.pendingCount == 1)
} else {
Issue.record("Expected second file transfer to queue while slots are full")
}
}
@Test
func submitQueuesDuplicateActiveTransferAtFront() {
var scheduler = BLEOutboundFragmentTransferScheduler()
let request = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "same")
_ = scheduler.submit(request, maxConcurrentTransfers: 2)
let result = scheduler.submit(request, maxConcurrentTransfers: 2)
if case let .queued(_, transferId, position) = result {
#expect(transferId == "same")
#expect(position == .front)
#expect(scheduler.activeCount == 1)
#expect(scheduler.pendingCount == 1)
} else {
Issue.record("Expected duplicate active transfer to queue at the front")
}
}
@Test
func cancelActiveTransferReturnsScheduledWorkItems() {
var scheduler = BLEOutboundFragmentTransferScheduler()
let request = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "active")
_ = scheduler.submit(request, maxConcurrentTransfers: 1)
let workItem = DispatchWorkItem {}
let didActivate = scheduler.activateReservedTransfer(id: "active", totalFragments: 2, workItems: [workItem])
#expect(didActivate)
if case let .active(transferId, workItems) = scheduler.cancelTransfer("active") {
#expect(transferId == "active")
#expect(workItems.count == 1)
#expect(scheduler.activeCount == 0)
} else {
Issue.record("Expected active transfer cancellation to return its work items")
}
}
@Test
func completedTransferFreesSlotForPendingTransfer() {
var scheduler = BLEOutboundFragmentTransferScheduler()
let first = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "first")
let second = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "second")
_ = scheduler.submit(first, maxConcurrentTransfers: 1)
let didActivate = scheduler.activateReservedTransfer(id: "first", totalFragments: 2, workItems: [])
#expect(didActivate)
_ = scheduler.submit(second, maxConcurrentTransfers: 1)
#expect(scheduler.markFragmentSent(transferId: "first") == .progress(sentFragments: 1, totalFragments: 2))
#expect(scheduler.markFragmentSent(transferId: "first") == .complete(sentFragments: 2, totalFragments: 2))
let starts = scheduler.reservePendingStarts(maxConcurrentTransfers: 1)
#expect(starts.count == 1)
if case let .start(_, reservedTransferId?) = starts.first {
#expect(reservedTransferId == "second")
#expect(scheduler.activeCount == 1)
#expect(scheduler.pendingCount == 0)
} else {
Issue.record("Expected pending transfer to reserve the freed slot")
}
}
@Test
func removeAllReturnsActiveWorkItemsAndDropsPendingTransfers() {
var scheduler = BLEOutboundFragmentTransferScheduler()
let active = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "active")
let pending = makeRequest(type: MessageType.fileTransfer.rawValue, transferId: "pending")
let workItem = DispatchWorkItem {}
_ = scheduler.submit(active, maxConcurrentTransfers: 1)
let didActivate = scheduler.activateReservedTransfer(id: "active", totalFragments: 1, workItems: [workItem])
#expect(didActivate)
_ = scheduler.submit(pending, maxConcurrentTransfers: 1)
let removed = scheduler.removeAll()
#expect(removed.count == 1)
#expect(removed.first?.id == "active")
#expect(removed.first?.workItems.count == 1)
#expect(scheduler.activeCount == 0)
#expect(scheduler.pendingCount == 0)
}
private func makeRequest(type: UInt8, transferId: String?) -> BLEOutboundFragmentTransferRequest {
BLEOutboundFragmentTransferRequest(
packet: BitchatPacket(
type: type,
senderID: Data([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]),
recipientID: nil,
timestamp: 0x0102030405,
payload: Data((transferId ?? "payload").utf8),
signature: nil,
ttl: 3
),
pad: false,
maxChunk: nil,
directedPeer: nil,
transferId: transferId
)
}
}