skip_on_cran() skip_if_offline(url_parse(hb())$domain) context("HttpClient retry: basics") test_that("retry has basic error checking", { cli <- HttpClient$new(url = hb()) expect_error(cli$retry()) expect_error(cli$retry(TRUE)) expect_error(cli$retry(times = 10)) expect_error(cli$retry("", times = 10)) expect_error(cli$retry("FOO", times = 10)) }) context("HttpClient retry: get") test_that("retry wrapping get request works", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("GET", path = "get") expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$parse, "function") expect_is(aa$parse(), "character") expect_true(aa$success()) }) test_that("retry wrapping get request - query parameters", { cli <- HttpClient$new(url = hb()) querya <- list(a = "Asdfadsf", hello = "world") aa <- cli$retry("GET", path = "get", query = querya) expect_is(aa, "HttpResponse") expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$parse, "function") expect_is(aa$parse(), "character") expect_true(aa$success()) library(urltools) params <- unlist(lapply( strsplit(urltools::url_parse(aa$request$url$url)$parameter, "&")[[1]], function(x) { tmp <- strsplit(x, "=")[[1]] as.list(stats::setNames(tmp[2], tmp[1])) } ), FALSE) expect_equal(params, querya) }) context("HttpClient retry: post") test_that("retry wrapping post request works", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("POST", path = "post") expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "post") expect_is(aa$parse, "function") expect_is(aa$parse(), "character") expect_true(aa$success()) expect_null(aa$request$fields) }) test_that("retry wrapping post request with body", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("POST", path = "post", body = list(hello = "world")) expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "post") expect_is(aa$parse, "function") expect_is(aa$parse(), "character") expect_true(aa$success()) expect_named(aa$request$fields, "hello") expect_equal(aa$request$fields[[1]], "world") }) test_that("retry wrapping post request with file upload", { # txt file ## as file file <- upload(system.file("CITATION")) cli <- HttpClient$new(url = hb()) aa <- cli$retry("POST", path = "post", body = list(a = file)) expect_is(aa, "HttpResponse") expect_is(aa$content, "raw") expect_null(aa$request$options$readfunction) out <- jsonlite::fromJSON(aa$parse("UTF-8")) expect_named(out$files, "a") expect_match(out$files$a, "bibentry") ## as data aa2 <- cli$retry("POST", path = "post", body = file) expect_is(aa2, "HttpResponse") expect_is(aa2$content, "raw") expect_is(aa2$request$options$readfunction, "function") out <- jsonlite::fromJSON(aa2$parse("UTF-8")) expect_equal(length(out$files), 0) expect_is(out$data, "character") expect_match(out$data, "bibentry") # binary file: jpeg file <- upload(file.path(Sys.getenv("R_DOC_DIR"), "html/logo.jpg")) cli <- HttpClient$new(url = hb()) aa <- cli$retry("POST", path = "post", body = list(a = file)) expect_is(aa, "HttpResponse") expect_is(aa$content, "raw") expect_named(aa$request$fields, "a") out <- jsonlite::fromJSON(aa$parse("UTF-8")) expect_named(out$files, "a") expect_match(out$files$a, "data:image/jpeg") }) context("HttpClient retry: put") test_that("retry wrapping put request works", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("PUT", path = "put") expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "put") expect_is(aa$parse, "function") expect_is(aa$parse(), "character") expect_true(aa$success()) expect_null(aa$request$fields) }) test_that("retry wrapping put request with body", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("PUT", path = "put", body = list(hello = "world")) expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "put") expect_is(aa$parse, "function") expect_is(aa$parse("UTF-8"), "character") expect_true(aa$success()) expect_named(aa$request$fields, "hello") expect_equal(aa$request$fields[[1]], "world") }) context("HttpClient retry: delete") test_that("retry wrapping delete request works", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("DELETE", path = "delete") expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "delete") expect_is(aa$parse, "function") expect_is(aa$parse(), "character") expect_true(aa$success()) expect_null(aa$request$fields) }) test_that("retry wrapping delete request with body", { cli <- HttpClient$new(url = hb()) aa <- cli$retry("DELETE", path = "delete", body = list(hello = "world")) expect_is(aa, "HttpResponse") expect_is(aa$handle, 'curl_handle') expect_is(aa$content, "raw") expect_is(aa$method, "character") expect_equal(aa$method, "delete") expect_is(aa$parse, "function") expect_is(aa$parse("UTF-8"), "character") expect_true(aa$success()) expect_named(aa$request$fields, "hello") expect_equal(aa$request$fields[[1]], "world") }) context("HttpClient retry: retry") test_that("retry actually retries on error", { cli <- HttpClient$new(url = hb()) ## baseline time for comparison tt <- system.time(cli$retry("GET", path = "status/200", times = 2)) ## try again if this took longer than expected if (tt["elapsed"] > 1) tt <- system.time(cli$retry("GET", path = "status/200", times = 2)) tt1 <- system.time(cli$retry("GET", path = "status/400", times = 2)) expect_gt(tt1["elapsed"] - tt["elapsed"], 2) }) test_that("retry recognizes retry headers", { skip_if_not_installed("webmockr") loadNamespace("webmockr") webmockr::enable() cli <- HttpClient$new(url = hb()) stub <- webmockr::stub_request("get", hb("/get")) stub <- webmockr::to_return(stub, status = 503, headers = list("retry-after" = 1)) tt0 <- system.time(cli$retry("GET", path = "get", times = 0)) expect_lt(tt0["elapsed"], 0.5) tt1 <- system.time(cli$retry("GET", path = "get", pause_base = 0, pause_min = 0, times = 2)) expect_gt(tt1["elapsed"], 2) webmockr::stub_registry_clear() stub <- webmockr::stub_request("get", hb("/get")) stub <- webmockr::to_return(stub, status = 429, headers = list("x-ratelimit-remaining" = "0", "retry-after" = "1")) tt1 <- system.time(cli$retry("GET", path = "get", pause_base = 0, pause_min = 0, times = 2)) expect_gt(tt1["elapsed"], 2) webmockr::stub_registry_clear() stub <- webmockr::stub_request("get", hb("/get")) stub <- webmockr::to_return(stub, status = 429, headers = list("x-ratelimit-remaining" = "0", "x-ratelimit-reset" = ceiling(as.numeric(Sys.time())) + 3)) tt1 <- system.time(cli$retry("GET", path = "get", pause_base = 0, pause_min = 0, times = 1)) expect_gt(tt1["elapsed"], 2.0) webmockr::stub_registry_clear() webmockr::disable() }) test_that("retry doesn't retry on error unless triggered", { cli <- HttpClient$new(url = hb()) ## baseline time tt <- system.time(cli$retry("GET", path = "status/200", times = 2)) ## try again if this took longer than expected if (tt["elapsed"] > 1) tt <- system.time(cli$retry("GET", path = "status/200", times = 2)) tt1 <- system.time(cli$retry("GET", path = "status/400", times = 0)) expect_lt(tt1["elapsed"], 2) expect_lt(abs(tt1["elapsed"] - tt["elapsed"]), 1) tt1 <- system.time(cli$retry("GET", path = "status/400", pause_cap = 0)) expect_lt(tt1["elapsed"], 2) expect_lt(abs(tt1["elapsed"] - tt["elapsed"]), 1) tt1 <- system.time(cli$retry("GET", path = "status/400", terminate_on = c(400))) expect_lt(tt1["elapsed"], 2) expect_lt(abs(tt1["elapsed"] - tt["elapsed"]), 1) tt1 <- system.time(cli$retry("GET", path = "status/404", terminate_on = c(400, 404))) expect_lt(tt1["elapsed"], 2) expect_lt(abs(tt1["elapsed"] - tt["elapsed"]), 1) tt1 <- system.time(cli$retry("GET", path = "status/404", retry_only_on = c(400))) expect_lt(tt1["elapsed"], 2) expect_lt(abs(tt1["elapsed"] - tt["elapsed"]), 1) }) context("HttpClient retry: using callback") test_that("retry invokes callback function if provided", { cli <- HttpClient$new(url = hb()) codes <- c() waittimes <- c() tt <- system.time(cli$retry("GET", path = "status/400", times = 2, onwait = function(resp, secs) { codes <<- c(codes, resp$status_code) waittimes <<- c(waittimes, secs) })) expect_length(codes, 2) expect_length(waittimes, 2) expect_identical(codes, c(400, 400)) expect_true(waittimes[1] >= 1 && waittimes[1] <= 2) expect_true(waittimes[2] >= 1 && waittimes[2] <= 4) })