test_that("throttling affects request performance", { skip_on_cran() on.exit(throttle_reset()) local_mocked_bindings(unix_time = function() 0) req <- request_test() |> req_throttle(capacity = 4, fill_time_s = 1) . <- replicate(4, req_perform(req)) local_mocked_bindings(unix_time = function() 0.1) expect_snapshot(time <- system.time(req_perform(req))[[3]]) expect_gte(time, 1 / 4 - 0.1 - 1e-6) }) test_that("first request isn't throttled", { on.exit(throttle_reset()) mock_time <- 0 local_mocked_bindings(unix_time = function() mock_time) req <- request_test() |> req_throttle(rate = 1, fill_time_s = 1) expect_equal(throttle_delay(req), 0) mock_time <- 0.1 expect_equal(throttle_delay(req), 0.9) mock_time <- 1.5 expect_equal(throttle_delay(req), 0.5) }) test_that("throttle only reset if parameters change", { on.exit(throttle_reset()) mock_time <- 0 local_mocked_bindings(unix_time = function() mock_time) req <- request_test() |> req_throttle(capacity = 1, fill_time_s = 1) expect_equal(throttle_delay(req), 0) mock_time <- 0.5 expect_equal(throttle_delay(req), 0.5) mock_time <- 1 req <- request_test() |> req_throttle(capacity = 1, fill_time_s = 1) expect_equal(throttle_delay(req), 1) # reset: capacity changed req <- request_test() |> req_throttle(capacity = 2, fill_time_s = 1) expect_equal(throttle_delay(req), 0) # reset: fill time changed req <- request_test() |> req_throttle(capacity = 2, fill_time_s = 2) expect_equal(throttle_delay(req), 0) }) test_that("realm defaults to hostname but can be overridden", { on.exit(throttle_reset()) expect_named(the$throttle, character()) request_test() |> req_throttle(100 / 1) expect_named(the$throttle, "127.0.0.1") throttle_reset() request_test() |> req_throttle(100 / 1, realm = "custom") expect_named(the$throttle, "custom") }) # token bucket ---------------------------------------------------------------- test_that("token bucket respects capacity limits", { mock_time <- 0 local_mocked_bindings(unix_time = function() mock_time) bucket <- TokenBucket$new(capacity = 2, fill_rate = 2) expect_equal(bucket$take_token(), 0) expect_equal(bucket$tokens, 1) expect_equal(bucket$take_token(), 0) expect_equal(bucket$tokens, 0) expect_equal(bucket$take_token(), 0.5) mock_time <- 0.5 expect_equal(bucket$take_token(), 0.5) }) test_that("token bucket handles fractions correctly", { mock_time <- 0 local_mocked_bindings(unix_time = function() mock_time) bucket <- TokenBucket$new(capacity = 2, fill_rate = 2) bucket$tokens <- 0 expect_equal(bucket$take_token(), 0.5) expect_equal(bucket$tokens, -1) mock_time <- 0.5 expect_equal(bucket$refill(), 0) bucket$last_fill <- 0 bucket$tokens <- 0 mock_time <- 0.4 expect_equal(bucket$refill(), 0.80) expect_equal(bucket$take_token(), 0.1) mock_time <- mock_time + 0.1 expect_equal(bucket$refill(), 0) }) test_that("never returns negative time", { mock_time <- 0 local_mocked_bindings(unix_time = function() mock_time) bucket <- TokenBucket$new(capacity = 5, fill_rate = 1) mock_time <- 5 # first five are free replicate(5, expect_equal(bucket$take_token(), 0)) # we get another 5 after 5 seconds mock_time <- mock_time + 5 replicate(5, expect_equal(bucket$take_token(), 0)) # if we only wait a second, we only get one mock_time <- mock_time + 1 expect_equal(bucket$take_token(), 0) expect_equal(bucket$take_token(), 1) expect_equal(bucket$take_token(), 2) })