import Foundation
import Testing
@testable import IrdP2PSignaling
@Suite struct PacingTests {
@Test func relayBackoffFormula() {
#expect(Backoff.relay(attempt: 0, jitter: 0.5) == .milliseconds(500))
#expect(Backoff.relay(attempt: 1, jitter: 0.5) == .milliseconds(1000))
#expect(Backoff.relay(attempt: 3, jitter: 0) == .milliseconds(2000)) // 4000 × 0.5
#expect(Backoff.relay(attempt: 3, jitter: 0.999) == .milliseconds(5996))
#expect(Backoff.relay(attempt: 10, jitter: 0.5) == .milliseconds(15000)) // capped
#expect(Backoff.relay(attempt: 20, jitter: 0.999) == .milliseconds(22485))
}
@Test func mailboxBackoffFormula() {
#expect(Backoff.mailbox(attempt: 0) == .seconds(1))
#expect(Backoff.mailbox(attempt: 3) == .seconds(8))
#expect(Backoff.mailbox(attempt: 9) == .seconds(300))
#expect(Backoff.mailbox(attempt: 40) == .seconds(300))
}
@Test func tokenBucketAllowsBurstThenWaits() {
var b = TokenBucket(capacity: 3, refillPerSecond: 1, now: .zero)
#expect(b.take(1, now: .zero) == .zero)
#expect(b.take(1, now: .zero) == .zero)
#expect(b.take(1, now: .zero) == .zero)
#expect(b.take(1, now: .zero) == .seconds(1)) // deficit of one token at 1/s
#expect(b.take(1, now: .zero) == .seconds(2)) // queued behind the previous wait
#expect(b.take(1, now: .seconds(3)) == .zero) // debt of 2 repaid after 2 s, one token left over
#expect(b.available(now: .seconds(3)) == 0)
#expect(b.available(now: .seconds(5)) == 2)
}
@Test func relayPacerUsesDescriptorLimits() {
var p = RelayPacer(limits: .defaults, now: .zero)
for _ in 0..<60 { #expect(p.take(byteCount: 100, now: .zero) == .zero) } // burst of 60 messages
let wait = p.take(byteCount: 100, now: .zero)
#expect(wait > .zero && wait <= .milliseconds(34)) // 1/30 s
var q = RelayPacer(limits: .defaults, now: .zero)
#expect(q.take(byteCount: 262_144 * 4, now: .zero) == .zero) // 4× byte burst
#expect(q.take(byteCount: 262_144, now: .zero) == .seconds(1))
var off = RelayPacer(limits: .defaults, now: .zero)
off.enabled = false
for _ in 0..<1000 { #expect(off.take(byteCount: 1_000_000, now: .zero) == .zero) }
}
}