import Foundation import XCTest import RastrilloNative final class BackoffTests: XCTestCase { func testDoublesFromHalfASecondToTenWithEqualJitter() { var high = Backoff(random: { $0.upperBound }) XCTAssertEqual((0..<7).map { _ in high.next() }, [0.5, 1, 2, 4, 8, 10, 10]) var low = Backoff(random: { $0.lowerBound }) XCTAssertEqual((0..<6).map { _ in low.next() }, [0.25, 0.5, 1, 2, 4, 5]) var real = Backoff() for expected in [0.5, 1.0, 2.0, 4.0] { let delay = real.next() XCTAssertTrue((expected / 2...expected).contains(delay), "\(delay) outside [\(expected / 2), \(expected)]") } } func testRetryAfterIsAFloorCappedAtFiveMinutes() { var backoff = Backoff(random: { $0.upperBound }) XCTAssertEqual(backoff.next(retryAfter: 30), 30) XCTAssertEqual(backoff.next(retryAfter: 3600), 300) XCTAssertEqual(backoff.next(retryAfter: 0.1), 2, "A short Retry-After must not undercut the backoff") } func testResetsOnSuccessAndAfterAStableConnectionOnly() { var backoff = Backoff(random: { $0.upperBound }) _ = backoff.next(); _ = backoff.next(); _ = backoff.next() backoff.opened(at: 100) backoff.ended(at: 103) XCTAssertEqual(backoff.next(), 4, "A connection that dropped quickly must keep backing off") backoff.opened(at: 200) backoff.ended(at: 206) XCTAssertEqual(backoff.next(), 0.5) _ = backoff.next() backoff.reset() XCTAssertEqual(backoff.next(), 0.5) } /// The stable clock starts when the stream opens, not when the attempt /// starts: a slow refusal or a long access check must not count. func testAnAttemptThatNeverOpenedIsNeverStable() { var backoff = Backoff(random: { $0.upperBound }) _ = backoff.next(); _ = backoff.next() backoff.ended(at: 1_000) XCTAssertEqual(backoff.next(), 2, "An attempt that never opened reset the backoff") backoff.opened(at: 2_000) backoff.ended(at: 2_006) _ = backoff.next() backoff.ended(at: 9_000) XCTAssertEqual(backoff.next(), 1, "An earlier open was counted for a later attempt") } }