import Foundation
import Testing
@testable import IrdP2PCrypto
@Suite struct ReplayWindowTests {
func iv(_ epoch: UInt8, _ counter: UInt64) -> Data {
var d = Data([epoch, 0, 0, 0])
var be = counter.bigEndian
withUnsafeBytes(of: &be) { d.append(contentsOf: $0) }
return d
}
/// Feeds the sequence and returns the accept/reject verdicts in order.
func verdicts(_ seq: [(UInt8, UInt64)]) -> [Bool] {
var w = ReplayWindow()
return seq.map { w.accept(iv: iv($0.0, $0.1)) }
}
@Test func acceptsFreshCountersInAnyOrderRejectsDuplicates() {
#expect(verdicts([(1, 0), (1, 1), (1, 5), (1, 5), (1, 3), (1, 3), (1, 6), (1, 0)]) == [true, true, true, false, true, false, true, false])
}
@Test func slidingWindowIs64Wide() {
#expect(verdicts([(1, 100), (1, 37), (1, 36), (1, 99), (1, 100), (1, 200), (1, 136), (1, 137)]) == [true, true, false, true, false, true, false, true])
}
@Test func bigJumpsResetTheWindow() {
#expect(verdicts([(1, 5), (1, 1000), (1, 5), (1, 999), (1, 1000)]) == [true, true, false, true, false])
}
@Test func retiredEpochsNeverComeBack() {
#expect(verdicts([(1, 9), (2, 0), (1, 10), (2, 0), (2, 1)]) == [true, true, false, false, true])
}
@Test func rejectsWrongLength() {
var w = ReplayWindow()
let ok = w.accept(iv: Data([1, 2, 3]))
#expect(!ok)
}
}