From f38285fb78a4be18ce5a8b8f12e6db2637c41f32 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 29 Sep 2020 18:05:12 -0700 Subject: [PATCH 01/66] fix #103 add support for simple auth mocking to wi_th() - add import base64enc for munging passwords and matching to crul/httr password munging - bump package version for this change - changes mostly within StubbedRequest, adapter for crul and httr, and in wi_th - update docs with discussion of auth mocking - add tests for auth mocking --- DESCRIPTION | 5 ++-- NAMESPACE | 1 + R/StubbedRequest.R | 32 +++++++++++++++++++++++-- R/adapter-crul.R | 10 ++++++-- R/adapter-httr.R | 10 ++++++-- R/webmockr.R | 1 + R/wi_th.R | 34 ++++++++++++++++++--------- R/zzz.R | 9 ++++++- man/StubbedRequest.Rd | 17 +++++++++++++- man/wi_th.Rd | 25 +++++++++++++------- tests/testthat/test-wi_th.R | 47 ++++++++++++++++++++++++++++++++++++- 11 files changed, 161 insertions(+), 30 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index fa7652b..f6c6fd3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.6.4.91 +Version: 0.6.5.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), @@ -29,7 +29,8 @@ Imports: R6 (>= 2.1.3), urltools (>= 1.6.0), fauxpas, - crul (>= 0.7.0) + crul (>= 0.7.0), + base64enc Suggests: testthat, xml2, diff --git a/NAMESPACE b/NAMESPACE index 6f480ce..4fb12df 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -51,6 +51,7 @@ export(webmockr_reset) export(wi_th) export(wi_th_) importFrom(R6,R6Class) +importFrom(base64enc,base64encode) importFrom(crul,mock) importFrom(fauxpas,HTTPRequestTimeout) importFrom(magrittr,"%>%") diff --git a/R/StubbedRequest.R b/R/StubbedRequest.R index e12eeaa..96689e3 100644 --- a/R/StubbedRequest.R +++ b/R/StubbedRequest.R @@ -29,6 +29,12 @@ #' headers = list(a = 5)) #' x$to_s() #' x +#' +#' # basic auth +#' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") +#' x$with(basic_auth = c("foo", "bar")) +#' x$to_s() +#' x #' #' # file path #' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") @@ -86,6 +92,8 @@ StubbedRequest <- R6::R6Class( query = NULL, #' @field body (xx) xx body = NULL, + #' @field basic_auth (xx) xx + basic_auth = NULL, #' @field request_headers (xx) xx request_headers = NULL, #' @field response_headers (xx) xx @@ -132,7 +140,9 @@ StubbedRequest <- R6::R6Class( else cat(sprintf(" body (class: %s): %s", class(self$body)[1L], hdl_lst(self$body)), sep = "\n") - cat(paste0(" request_headers: ", hdl_lst(self$request_headers)), + cat(paste0(" request_headers: ", + # hdl_lst(c(self$request_headers, prep_auth(self$basic_auth)))), + hdl_lst(self$request_headers)), sep = "\n") cat(" to_return: ", sep = "\n") rs <- self$responses_sequences @@ -158,13 +168,18 @@ StubbedRequest <- R6::R6Class( #' @param query (list) request query params, as a named list. optional #' @param body (list) request body, as a named list. optional #' @param headers (list) request headers as a named list. optional. + #' @param basic_auth (character) basic authentication. optional. #' @return nothing returned; sets only - with = function(query = NULL, body = NULL, headers = NULL) { + with = function(query = NULL, body = NULL, headers = NULL, basic_auth = NULL) { if (!is.null(query)) { query <- lapply(query, as.character) } self$query <- query self$body <- body + self$basic_auth <- basic_auth + if (!is.null(basic_auth)) { + headers <- c(prep_auth(basic_auth), headers) + } self$request_headers <- headers }, @@ -300,3 +315,16 @@ StubbedRequest <- R6::R6Class( } ) ) + +basic_auth_header <- function(x) { + assert(x, "character") + stopifnot(length(x) == 2) + encoded <- base64enc::base64encode(charToRaw(paste0(x, collapse = ':'))) + return(paste0("Basic ", encoded)) +} +prep_auth <- function(x) { + if (is.null(x)) return(NULL) + if (!is.null(x)) { + list(Authorization = basic_auth_header(x)) + } +} diff --git a/R/adapter-crul.R b/R/adapter-crul.R index c39aa1b..3e63ef2 100644 --- a/R/adapter-crul.R +++ b/R/adapter-crul.R @@ -51,14 +51,20 @@ build_crul_response <- function(req, resp) { #' @param x an unexecuted crul request object #' @return a crul request build_crul_request = function(x) { + headers <- x$headers %||% NULL + auth <- x$options$userpwd %||% NULL + if (!is.null(auth)) { + auth_header <- prep_auth(strsplit(auth, ":")[[1]]) + headers <- c(headers, auth_header) + } RequestSignature$new( method = x$method, uri = x$url$url, options = list( body = pluck_body(x), - headers = x$headers %||% NULL, + headers = headers, proxies = x$proxies %||% NULL, - auth = x$auth %||% NULL, + auth = auth, disk = x$disk %||% NULL ) ) diff --git a/R/adapter-httr.R b/R/adapter-httr.R index fc8e985..ee55555 100644 --- a/R/adapter-httr.R +++ b/R/adapter-httr.R @@ -65,14 +65,20 @@ httr_cookies_df <- function() { #' @param x an unexecuted httr request object #' @return a httr request build_httr_request = function(x) { + headers <- as.list(x$headers) %||% NULL + auth <- x$options$userpwd %||% NULL + if (!is.null(auth)) { + auth_header <- prep_auth(strsplit(auth, ":")[[1]]) + headers <- c(headers, auth_header) + } RequestSignature$new( method = x$method, uri = x$url, options = list( body = pluck_body(x), - headers = as.list(x$headers) %||% NULL, + headers = headers, proxies = x$proxies %||% NULL, - auth = x$auth %||% NULL, + auth = auth, disk = x$disk %||% NULL ) ) diff --git a/R/webmockr.R b/R/webmockr.R index f2848a4..445083c 100644 --- a/R/webmockr.R +++ b/R/webmockr.R @@ -4,6 +4,7 @@ #' @importFrom R6 R6Class #' @importFrom fauxpas HTTPRequestTimeout #' @importFrom crul mock +#' @importFrom base64enc base64encode #' @name webmockr-package #' @aliases webmockr #' @docType package diff --git a/R/wi_th.R b/R/wi_th.R index b2212b3..4c61185 100644 --- a/R/wi_th.R +++ b/R/wi_th.R @@ -1,23 +1,23 @@ #' Set additional parts of a stubbed request #' -#' Set query params, request body, and/or request headers +#' Set query params, request body, request headers and/or basic_auth #' #' @export #' @param .data input. Anything that can be coerced to a `StubbedRequest` class #' object #' @param ... Comma separated list of named variables. accepts the following: -#' `query`, `body`, `headers`. See Details. -#' @param .list named list, has to be one of 'query', 'body', -#' and/or 'headers'. An alternative to passing in via `...`. Don't pass the -#' same thing to both, e.g. don't pass 'query' to `...`, and also 'query' to -#' this parameter +#' `query`, `body`, `headers`, `basic_auth`. See Details. +#' @param .list named list, has to be one of `query`, `body`, +#' `headers` and/or `basic_auth`. An alternative to passing in via `...`. +#' Don't pass the same thing to both, e.g. don't pass 'query' to `...`, and +#' also 'query' to this parameter #' @details `with` is a function in the `base` package, so we went with #' `wi_th` #' @return an object of class `StubbedRequest`, with print method describing #' the stub #' @note see more examples in [stub_request()] #' @details -#' Values for query, body, and headers: +#' Values for query, body, headers, and basic_auth: #' #' - query: (list) a named list. values are coerced to character #' class in the recorded stub. You can pass numeric, integer, etc., but @@ -26,6 +26,10 @@ #' upload (`crul::upload` or `httr::upload_file`, they both create the #' same object in the end) #' - headers: (list) a named list +#' - basic_auth: (character) a length two vector, username and password. +#' authentication type (basic/digest/ntlm/etc.) is ignored. that is, +#' mocking authenciation right now does not take into account the +#' authentication type #' #' Note that there is no regex matching on query, body, or headers. They #' are tested for matches in the following ways: @@ -34,7 +38,8 @@ #' named lists, so both list names and values are compared #' - body: varies depending on the body format (list vs. character, etc.) #' - headers: compare stub and request values with `==`. list names are -#' compared with `%in%` +#' compared with `%in%`. `basic_auth` is included in headers (with the name +#' Authorization) #' #' @examples #' # first, make a stub object @@ -62,6 +67,10 @@ #' #' # .list - pass in a named list instead #' wi_th(req, .list = list(body = list(foo = "bar"))) +#' +#' # basic authentication +#' wi_th(req, basic_auth = c("user", "pass")) +#' wi_th(req, basic_auth = c("user", "pass"), headers = list(foo = "bar")) wi_th <- function(.data, ..., .list = list()) { assert(.data, "StubbedRequest") assert(.list, "list") @@ -69,20 +78,23 @@ wi_th <- function(.data, ..., .list = list()) { if (length(z) == 0) z <- NULL z <- c(z, .list) if ( - !any(c("query", "body", "headers") %in% names(z)) && + !any(c("query", "body", "headers", "basic_auth") %in% names(z)) && length(z) != 0 ) { - stop("'wi_th' only accepts query, body, headers") + stop("'wi_th' only accepts query, body, headers, basic_auth") } if (any(duplicated(names(z)))) stop("can not have duplicated names") assert(z$query, "list") if (!all(hz_namez(z$query))) stop("'query' must be a named list") assert(z$headers, "list") if (!all(hz_namez(z$headers))) stop("'headers' must be a named list") + assert(z$basic_auth, "character") + assert_eq(z$basic_auth, 2) .data$with( query = z$query, body = z$body, - headers = z$headers + headers = z$headers, + basic_auth = z$basic_auth ) return(.data) } diff --git a/R/zzz.R b/R/zzz.R index a645bf1..1e85556 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -112,13 +112,20 @@ assert <- function(x, y) { } } } - assert_gte <- function(x, y) { if (!x >= y) { stop(sprintf("%s must be greater than or equal to %s", deparse(substitute(x)), y), call. = FALSE) } } +assert_eq <- function(x, y) { + if (!is.null(x)) { + if (!length(x) == y) { + stop(sprintf("length of %s must be equal to %s", + deparse(substitute(x)), y), call. = FALSE) + } + } +} crul_head_parse <- function(z) { if (grepl("HTTP\\/", z)) { diff --git a/man/StubbedRequest.Rd b/man/StubbedRequest.Rd index 91bd450..eb924a9 100644 --- a/man/StubbedRequest.Rd +++ b/man/StubbedRequest.Rd @@ -35,6 +35,12 @@ x$to_return(status = 200, body = charToRaw("foo bar"), x$to_s() x +# basic auth +x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") +x$with(basic_auth = c("foo", "bar")) +x$to_s() +x + # file path x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") f <- tempfile() @@ -95,6 +101,8 @@ x \item{\code{body}}{(xx) xx} +\item{\code{basic_auth}}{(xx) xx} + \item{\code{request_headers}}{(xx) xx} \item{\code{response_headers}}{(xx) xx} @@ -172,7 +180,12 @@ print method for the \code{StubbedRequest} class \subsection{Method \code{with()}}{ Set expectations for what's given in HTTP request \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{StubbedRequest$with(query = NULL, body = NULL, headers = NULL)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{StubbedRequest$with( + query = NULL, + body = NULL, + headers = NULL, + basic_auth = NULL +)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -183,6 +196,8 @@ Set expectations for what's given in HTTP request \item{\code{body}}{(list) request body, as a named list. optional} \item{\code{headers}}{(list) request headers as a named list. optional.} + +\item{\code{basic_auth}}{(character) basic authentication. optional.} } \if{html}{\out{}} } diff --git a/man/wi_th.Rd b/man/wi_th.Rd index 74917c6..2f36194 100644 --- a/man/wi_th.Rd +++ b/man/wi_th.Rd @@ -11,25 +11,25 @@ wi_th(.data, ..., .list = list()) object} \item{...}{Comma separated list of named variables. accepts the following: -\code{query}, \code{body}, \code{headers}. See Details.} +\code{query}, \code{body}, \code{headers}, \code{basic_auth}. See Details.} -\item{.list}{named list, has to be one of 'query', 'body', -and/or 'headers'. An alternative to passing in via \code{...}. Don't pass the -same thing to both, e.g. don't pass 'query' to \code{...}, and also 'query' to -this parameter} +\item{.list}{named list, has to be one of \code{query}, \code{body}, +\code{headers} and/or \code{basic_auth}. An alternative to passing in via \code{...}. +Don't pass the same thing to both, e.g. don't pass 'query' to \code{...}, and +also 'query' to this parameter} } \value{ an object of class \code{StubbedRequest}, with print method describing the stub } \description{ -Set query params, request body, and/or request headers +Set query params, request body, request headers and/or basic_auth } \details{ \code{with} is a function in the \code{base} package, so we went with \code{wi_th} -Values for query, body, and headers: +Values for query, body, headers, and basic_auth: \itemize{ \item query: (list) a named list. values are coerced to character class in the recorded stub. You can pass numeric, integer, etc., but @@ -38,6 +38,10 @@ all will be coerced to character. upload (\code{crul::upload} or \code{httr::upload_file}, they both create the same object in the end) \item headers: (list) a named list +\item basic_auth: (character) a length two vector, username and password. +authentication type (basic/digest/ntlm/etc.) is ignored. that is, +mocking authenciation right now does not take into account the +authentication type } Note that there is no regex matching on query, body, or headers. They @@ -47,7 +51,8 @@ are tested for matches in the following ways: named lists, so both list names and values are compared \item body: varies depending on the body format (list vs. character, etc.) \item headers: compare stub and request values with \code{==}. list names are -compared with \code{\%in\%} +compared with \code{\%in\%}. \code{basic_auth} is included in headers (with the name +Authorization) } } \note{ @@ -79,4 +84,8 @@ wi_th(req, headers = list(`User-Agent` = "webmockr/v1", hello="world")) # .list - pass in a named list instead wi_th(req, .list = list(body = list(foo = "bar"))) + +# basic authentication +wi_th(req, basic_auth = c("user", "pass")) +wi_th(req, basic_auth = c("user", "pass"), headers = list(foo = "bar")) } diff --git a/tests/testthat/test-wi_th.R b/tests/testthat/test-wi_th.R index bf5fe3a..b1b5217 100644 --- a/tests/testthat/test-wi_th.R +++ b/tests/testthat/test-wi_th.R @@ -193,10 +193,55 @@ test_that("wi_th handles HEADERS with varied input classes", { expect_is(GET("https://x.com", add_headers(foo=30)), "response") }) +disable("httr") + +test_that("wi_th basic_auth", { + # crul + library(crul) + enable("crul") + con <- HttpClient$new("https://x.com", auth = auth("user", "passwd")) + # pass + stub_registry_clear() + stub_request("get", "https://x.com") %>% + wi_th(basic_auth=c("user", "passwd")) + expect_is(con$get(), "HttpResponse") + # ignores auth type + con$auth <- crul::auth("user", "passwd", "digest") + expect_is(con$get(), "HttpResponse") + # fail + stub_registry_clear() + stub_request("get", "https://x.com") %>% + wi_th(basic_auth=c("user", "passwd")) + con$auth <- crul::auth("user", "password") + expect_error(con$get(), "Unregistered") + disable("crul") + + # httr + library(httr) + enable("httr") + # pass + stub_registry_clear() + stub_request("get", "https://x.com") %>% + wi_th(basic_auth=c("user", "passwd")) + expect_is(GET("https://x.com", authenticate("user", "passwd")), "response") + # ignores auth type + expect_is( + GET("https://x.com", authenticate("user", "passwd", type = "digest")), + "response") + expect_is( + GET("https://x.com", authenticate("user", "passwd", type = "ntlm")), + "response") + # fail + stub_registry_clear() + stub_request("get", "https://x.com") %>% + wi_th(basic_auth=c("user", "passwd")) + expect_error(GET("https://x.com", authenticate("user", "password")), + "Unregistered") + disable("httr") +}) # cleanup stub_registry_clear() -disable("httr") context("wi_th_: defunct") test_that("wi_th_: defunct", { From 13aa3bc0a16504704a36a680cd60bd0d8fa6b17c Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 29 Sep 2020 18:25:44 -0700 Subject: [PATCH 02/66] update revdep checks --- revdep/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/revdep/README.md b/revdep/README.md index e80bda7..5a0f35c 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -2,21 +2,21 @@ |field |value | |:--------|:-------------------------------------------| -|version |R version 3.6.3 Patched (2020-02-29 r77909) | -|os |macOS Catalina 10.15.4 | -|system |x86_64, darwin15.6.0 | +|version |R version 4.0.2 Patched (2020-09-01 r79114) | +|os |macOS Catalina 10.15.6 | +|system |x86_64, darwin17.0 | |ui |X11 | |language |(EN) | |collate |en_US.UTF-8 | |ctype |en_US.UTF-8 | |tz |US/Pacific | -|date |2020-04-01 | +|date |2020-09-29 | # Dependencies |package |old |new |Δ | |:--------|:-----|:--------|:--| -|webmockr |0.6.2 |0.6.2.92 |* | +|webmockr |0.6.2 |0.6.5.91 |* | # Revdeps From c5c176e1118f9ea44d6a3002575dbc6c6aa17d9b Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 30 Sep 2020 08:40:46 -0700 Subject: [PATCH 03/66] bump to v0.7, update news and cran comments --- DESCRIPTION | 5 +++-- NEWS.md | 22 ++++++++++++++++++++++ cran-comments.md | 8 ++++---- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f6c6fd3..cdb810d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.6.5.91 +Version: 0.7.0 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), @@ -15,8 +15,9 @@ Authors@R: c( person("rOpenSci", role = "fnd", comment = "https://ropensci.org") ) License: MIT + file LICENSE -URL: https://github.com/ropensci/webmockr (devel), +URL: https://github.com/ropensci/webmockr (devel) https://books.ropensci.org/http-testing (user manual) + https://docs.ropensci.org/webmockr (documentation) BugReports: https://github.com/ropensci/webmockr/issues LazyData: true Encoding: UTF-8 diff --git a/NEWS.md b/NEWS.md index 6226867..88291c4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,25 @@ +webmockr 0.7.0 +============== + +### NEW FEATURES + +* Gains ability to define more than 1 returned HTTP response, and the order in which the HTTP responses are returned. The idea is from the Ruby webmock library, but the implementation is different because the Ruby and R languages are very different. You can give more than one `to_return()` one creating a stub, or if you want to return the same response each time, you can use the new `times` parameter within `to_return()`. As a related use case (#31) you can mock http retry's using this new feature (#10) (#32) (#101) +* Gains new function `webmockr_reset()` to be able to reset stub registry and request registry in one function call (#97) (#101) +* Gains support for mocking simple authentication. `wi_th()` now accepts `basic_auth` in addition to query, body, and headers. Note that authentication type is ignored (#103) + +### MINOR IMPROVEMENTS + +* change to how URI's are matched in `stub_request()`: we weren't allowing matching URI's without schemes; you can now do that. In addition, webmockr can match URI's without the "http" scheme, but does not match if the scheme is "https". See `UriPattern` for more (#102) +* another change to how URI's are matched: now query params compared separately to the URI; note that regex not allowed in query params (#104) - And now query parameters are compared with the same code both when regex uri is used and when it is not (#107) +* URI matching for stubs is now done only on the URI's themselves; that is, query parameters are removed before comparison, so only the base url with http scheme, plus paths, are compared (#107) +* wasn't sure `write_disk_path` behavior was correct when using httr, seems to be working, added tests for it (#79) +* values for query parameters given to `wi_th()` are now all coerced to character class to make sure that all comparisons of stubs and requests are done with the same class (character) (#107) + +### BUG FIXES + +* fix for `uri_regex` usage in `stub_request()`: no longer curl escape the `uri_regex` given, only escape a non-regex uri (#106) + + webmockr 0.6.2 ============== diff --git a/cran-comments.md b/cran-comments.md index b5ed5e1..60a8e88 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,7 +1,7 @@ ## Test environments -* local OS X install, R 3.6.3 Patched -* ubuntu 16.04 (on travis-ci), R 3.6.2 +* local OS X install, R 4.0.2 Patched +* ubuntu 16.04 (on travis-ci), R 4.0.2 * win-builder (devel and release) ## R CMD check results @@ -10,11 +10,11 @@ ## Reverse dependencies -I have checked the 7 reverse dependencies, and no problems were found. See (). +I have checked the 12 reverse dependencies, and no problems were found. See (). --- -This version makes a small change regarding paths. +This version makes many improvements to stub matching. Thanks! Scott Chamberlain From 0f8e70e3af9630fe17ecf910719afb769d10c523 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 30 Sep 2020 08:41:05 -0700 Subject: [PATCH 04/66] update codemeta.json --- codemeta.json | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/codemeta.json b/codemeta.json index e0da774..b410560 100644 --- a/codemeta.json +++ b/codemeta.json @@ -10,14 +10,13 @@ "codeRepository": "https://github.com/ropensci/webmockr", "issueTracker": "https://github.com/ropensci/webmockr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.6.2", + "version": "0.7.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", - "version": "3.6.3", "url": "https://r-project.org" }, - "runtimePlatform": "R version 3.6.3 Patched (2020-02-29 r77909)", + "runtimePlatform": "R version 4.0.2 Patched (2020-09-01 r79114)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -180,19 +179,32 @@ "url": "https://cran.r-project.org" }, "sameAs": "https://CRAN.R-project.org/package=crul" + }, + { + "@type": "SoftwareApplication", + "identifier": "base64enc", + "name": "base64enc", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=base64enc" } ], "applicationCategory": "Web", "isPartOf": "https://ropensci.org", "keywords": ["http", "https", "API", "web-services", "curl", "mock", "mocking", "fakeweb", "http-mocking", "testing", "testing-tools", "tdd", "rstats", "http-mock", "r", "r-package"], - "contIntegration": ["https://travis-ci.org/ropensci/webmockr", "https://ci.appveyor.com/project/sckott/webmockr", "https://codecov.io/gh/ropensci/webmockr"], + "contIntegration": "https://codecov.io/gh/ropensci/webmockr", "developmentStatus": "https://www.repostatus.org/#active", "releaseNotes": "https://github.com/ropensci/webmockr/blob/master/NEWS.md", "readme": "https://github.com/ropensci/webmockr/blob/master/README.md", - "fileSize": "77.822KB", + "fileSize": "0KB", "relatedLink": [ "https://ropenscilabs.github.io/http-testing-book/", - "https://books.ropensci.org/http-testing" + "https://books.ropensci.org/http-testing", + "https://docs.ropensci.org/webmockr" ], "funder": [ { From 9d7caafd2009d28bdb81068ccf620e1013cbd1fb Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 30 Sep 2020 09:15:24 -0700 Subject: [PATCH 05/66] change urls to crans liking --- DESCRIPTION | 4 ++-- README.Rmd | 2 +- README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index cdb810d..d5b29d2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,8 +16,8 @@ Authors@R: c( ) License: MIT + file LICENSE URL: https://github.com/ropensci/webmockr (devel) - https://books.ropensci.org/http-testing (user manual) - https://docs.ropensci.org/webmockr (documentation) + https://books.ropensci.org/http-testing/ (user manual) + https://docs.ropensci.org/webmockr/ (documentation) BugReports: https://github.com/ropensci/webmockr/issues LazyData: true Encoding: UTF-8 diff --git a/README.Rmd b/README.Rmd index 19f58a9..066c035 100644 --- a/README.Rmd +++ b/README.Rmd @@ -15,7 +15,7 @@ knitr::opts_chunk$set( [![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![R-CMD-check](https://github.com/ropensci/webmockr/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/webmockr/actions/) [![codecov](https://codecov.io/gh/ropensci/webmockr/branch/master/graph/badge.svg)](https://codecov.io/gh/ropensci/webmockr) -[![rstudio mirror downloads](https://cranlogs.r-pkg.org/badges/webmockr)](https://github.com/metacran/cranlogs.app) +[![rstudio mirror downloads](https://cranlogs.r-pkg.org/badges/webmockr)](https://github.com/r-hub/cranlogs.app) [![cran version](https://www.r-pkg.org/badges/version/webmockr)](https://cran.r-project.org/package=webmockr) diff --git a/README.md b/README.md index 5794cef..3a08936 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ webmockr [![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![R-CMD-check](https://github.com/ropensci/webmockr/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/webmockr/actions/) [![codecov](https://codecov.io/gh/ropensci/webmockr/branch/master/graph/badge.svg)](https://codecov.io/gh/ropensci/webmockr) -[![rstudio mirror downloads](https://cranlogs.r-pkg.org/badges/webmockr)](https://github.com/metacran/cranlogs.app) +[![rstudio mirror downloads](https://cranlogs.r-pkg.org/badges/webmockr)](https://github.com/r-hub/cranlogs.app) [![cran version](https://www.r-pkg.org/badges/version/webmockr)](https://cran.r-project.org/package=webmockr) From 5ce65766e7221d2271ebb9038292887cc0663243 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 6 Oct 2020 10:26:55 -0700 Subject: [PATCH 06/66] bump to dev verion --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index d5b29d2..5c87c79 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.0 +Version: 0.7.0.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), From 03ce53f0302390e971f11cccbf878d51d14f042f Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Mon, 2 Nov 2020 13:53:37 -0800 Subject: [PATCH 07/66] #108 fixes for simple auth handling - updated description of basic_auth option in wi_th that it only handles if a url is included in the userpwd - in both crul and httr adapters, now check for url in string and do not split by colon to avoid multiple colons issue - added new test file test-auth_handling.R to deal with this exact issue --- R/StubbedRequest.R | 7 ++-- R/adapter-crul.R | 4 +- R/adapter-httr.R | 14 ++++++- R/wi_th.R | 5 ++- man/wi_th.Rd | 5 ++- tests/testthat/test-auth_handling.R | 58 +++++++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 tests/testthat/test-auth_handling.R diff --git a/R/StubbedRequest.R b/R/StubbedRequest.R index 96689e3..293763e 100644 --- a/R/StubbedRequest.R +++ b/R/StubbedRequest.R @@ -141,7 +141,6 @@ StubbedRequest <- R6::R6Class( cat(sprintf(" body (class: %s): %s", class(self$body)[1L], hdl_lst(self$body)), sep = "\n") cat(paste0(" request_headers: ", - # hdl_lst(c(self$request_headers, prep_auth(self$basic_auth)))), hdl_lst(self$request_headers)), sep = "\n") cat(" to_return: ", sep = "\n") @@ -178,7 +177,7 @@ StubbedRequest <- R6::R6Class( self$body <- body self$basic_auth <- basic_auth if (!is.null(basic_auth)) { - headers <- c(prep_auth(basic_auth), headers) + headers <- c(prep_auth(paste0(basic_auth, collapse = ':')), headers) } self$request_headers <- headers }, @@ -318,8 +317,8 @@ StubbedRequest <- R6::R6Class( basic_auth_header <- function(x) { assert(x, "character") - stopifnot(length(x) == 2) - encoded <- base64enc::base64encode(charToRaw(paste0(x, collapse = ':'))) + stopifnot(length(x) == 1) + encoded <- base64enc::base64encode(charToRaw(x)) return(paste0("Basic ", encoded)) } prep_auth <- function(x) { diff --git a/R/adapter-crul.R b/R/adapter-crul.R index 3e63ef2..6dae4eb 100644 --- a/R/adapter-crul.R +++ b/R/adapter-crul.R @@ -52,9 +52,9 @@ build_crul_response <- function(req, resp) { #' @return a crul request build_crul_request = function(x) { headers <- x$headers %||% NULL - auth <- x$options$userpwd %||% NULL + auth <- check_user_pwd(x$options$userpwd) %||% NULL if (!is.null(auth)) { - auth_header <- prep_auth(strsplit(auth, ":")[[1]]) + auth_header <- prep_auth(auth) headers <- c(headers, auth_header) } RequestSignature$new( diff --git a/R/adapter-httr.R b/R/adapter-httr.R index ee55555..6d94c61 100644 --- a/R/adapter-httr.R +++ b/R/adapter-httr.R @@ -60,15 +60,25 @@ httr_cookies_df <- function() { df } +# x = "https://foobar.com" +# check_user_pwd(x) +check_user_pwd <- function(x) { + if (is.null(x)) return(x) + if (grepl("^https?://", x)) { + stop(sprintf("expecting string of pattern 'user:pwd', got '%s'", x)) + } + return(x) +} + #' Build a httr request #' @export #' @param x an unexecuted httr request object #' @return a httr request build_httr_request = function(x) { headers <- as.list(x$headers) %||% NULL - auth <- x$options$userpwd %||% NULL + auth <- check_user_pwd(x$options$userpwd) %||% NULL if (!is.null(auth)) { - auth_header <- prep_auth(strsplit(auth, ":")[[1]]) + auth_header <- prep_auth(auth) headers <- c(headers, auth_header) } RequestSignature$new( diff --git a/R/wi_th.R b/R/wi_th.R index 4c61185..231676b 100644 --- a/R/wi_th.R +++ b/R/wi_th.R @@ -29,7 +29,10 @@ #' - basic_auth: (character) a length two vector, username and password. #' authentication type (basic/digest/ntlm/etc.) is ignored. that is, #' mocking authenciation right now does not take into account the -#' authentication type +#' authentication type. We don't do any checking of the username/password +#' except to detect edge cases where for example, the username/password +#' were probably not set by the user on purpose (e.g., a URL is +#' picked up by an environment variable) #' #' Note that there is no regex matching on query, body, or headers. They #' are tested for matches in the following ways: diff --git a/man/wi_th.Rd b/man/wi_th.Rd index 2f36194..94028dd 100644 --- a/man/wi_th.Rd +++ b/man/wi_th.Rd @@ -41,7 +41,10 @@ same object in the end) \item basic_auth: (character) a length two vector, username and password. authentication type (basic/digest/ntlm/etc.) is ignored. that is, mocking authenciation right now does not take into account the -authentication type +authentication type. We don't do any checking of the username/password +except to detect edge cases where for example, the username/password +were probably not set by the user on purpose (e.g., a URL is +picked up by an environment variable) } Note that there is no regex matching on query, body, or headers. They diff --git a/tests/testthat/test-auth_handling.R b/tests/testthat/test-auth_handling.R new file mode 100644 index 0000000..e65abdc --- /dev/null +++ b/tests/testthat/test-auth_handling.R @@ -0,0 +1,58 @@ +# from https://github.com/ropensci/webmockr/issues/108 + +# httr +stub_registry()$remove_all_request_stubs() +skip_if_not_installed("httr") +library("httr") +enable("httr") + +test_that("auth handling: httr", { + stub_request("get", "http://stuff.com") + + # auth well-formed + expect_is( + GET("http://stuff.com", authenticate("adf", "adf")), + "response" + ) + + # user name invalid according to RFC, but we can't know that + expect_is( + GET("http://stuff.com", authenticate("foo:bar", "adf")), + "response" + ) + + # malformed: url as username + expect_error( + GET("http://stuff.com", authenticate("http://", "foo.com")) + ) +}) + + +# crul +disable() +stub_registry()$remove_all_request_stubs() +skip_if_not_installed("crul") +library("crul") +enable("crul") + +test_that("auth handling: httr", { + stub_request("get", "http://stuff.com") + + # auth well-formed + x <- HttpClient$new("http://stuff.com") + x$auth <- auth("adf", "adf") + expect_is(x$get(), "HttpResponse") + + # user name invalid according to RFC, but we can't know that + y <- HttpClient$new("http://stuff.com") + y$auth <- auth("foo:bar", "adf") + expect_is(y$get(), "HttpResponse") + + # malformed: url as username + z <- HttpClient$new("http://stuff.com") + z$auth <- auth("http://", "foo.com") + expect_error(z$get()) +}) + +stub_registry()$remove_all_request_stubs() +disable() From 20c726360ffaee6a060430159a8806964e8bade5 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 1 Dec 2020 15:17:10 -0800 Subject: [PATCH 08/66] fix #109 add fields and output to options input for RequestSignature, bump version --- DESCRIPTION | 2 +- R/RequestSignature.R | 38 ++++++++++---------------- R/adapter-httr.R | 4 ++- man/RequestSignature.Rd | 4 +++ tests/testthat/test-RequestSignature.R | 7 ++++- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5c87c79..4e3f966 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.0.91 +Version: 0.7.1.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/RequestSignature.R b/R/RequestSignature.R index 03461eb..739d1f4 100644 --- a/R/RequestSignature.R +++ b/R/RequestSignature.R @@ -64,6 +64,10 @@ RequestSignature <- R6::R6Class( url = NULL, #' @field disk (character) if writing to disk, the path disk = NULL, + #' @field fields (various) request body details + fields = NULL, + #' @field output (various) request output details, disk, memory, etc + output = NULL, #' @description Create a new `RequestSignature` object #' @param method the HTTP method (any, head, options, get, post, put, @@ -111,6 +115,10 @@ RequestSignature <- R6::R6Class( if (!is.null(self$disk)) { cat(paste0(" disk: ", self$disk), sep = "\n") } + if (!is.null(self$fields)) { + cat(" fields: ", sep = "\n") + cat_foo(self$fields) + } }, #' @description Request signature to a string @@ -137,29 +145,13 @@ RequestSignature <- R6::R6Class( private = list( assign_options = function(options) { - if ('body' %in% names(options)) { - if (!is.null(options$body) && length(options)) { - self$body <- options$body - } - } - if ('headers' %in% names(options)) { - if (!is.null(options$headers) && length(options)) { - self$headers <- options$headers - } - } - if ('proxies' %in% names(options)) { - if (!is.null(options$proxies) && length(options)) { - self$proxies <- options$proxies - } - } - if ('auth' %in% names(options)) { - if (!is.null(options$auth) && length(options)) { - self$auth <- options$auth - } - } - if ('disk' %in% names(options)) { - if (!is.null(options$disk) && length(options)) { - self$disk <- options$disk + op_vars <- c("body", "headers", "proxies", "auth", + "disk", "fields", "output") + for (i in seq_along(op_vars)) { + if (op_vars[i] %in% names(options)) { + if (!is.null(options[[ op_vars[i] ]]) && length(options)) { + self[[ op_vars[i] ]] <- options[[ op_vars[i] ]] + } } } } diff --git a/R/adapter-httr.R b/R/adapter-httr.R index 6d94c61..551e843 100644 --- a/R/adapter-httr.R +++ b/R/adapter-httr.R @@ -89,7 +89,9 @@ build_httr_request = function(x) { headers = headers, proxies = x$proxies %||% NULL, auth = auth, - disk = x$disk %||% NULL + disk = x$disk %||% NULL, + fields = x$fields %||% NULL, + output = x$output %||% NULL ) ) } diff --git a/man/RequestSignature.Rd b/man/RequestSignature.Rd index 9243016..bdf4863 100644 --- a/man/RequestSignature.Rd +++ b/man/RequestSignature.Rd @@ -69,6 +69,10 @@ bb$to_s() \item{\code{url}}{internal use} \item{\code{disk}}{(character) if writing to disk, the path} + +\item{\code{fields}}{(various) request body details} + +\item{\code{output}}{(various) request output details, disk, memory, etc} } \if{html}{\out{}} } diff --git a/tests/testthat/test-RequestSignature.R b/tests/testthat/test-RequestSignature.R index b127d92..0e997ac 100644 --- a/tests/testthat/test-RequestSignature.R +++ b/tests/testthat/test-RequestSignature.R @@ -11,6 +11,8 @@ test_that("RequestSignature: works", { expect_null(aa$body) expect_null(aa$headers) expect_null(aa$proxies) + expect_null(aa$fields) + expect_null(aa$output) expect_is(aa$method, "character") expect_equal(aa$method, "get") @@ -23,7 +25,8 @@ test_that("RequestSignature: works", { }) test_that("RequestSignature: different methods work", { - aa <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get") + aa <- RequestSignature$new(method = "post", uri = "https:/httpbin.org/post", + options = list(fields = list(foo = "bar"))) aa$headers <- list(Accept = "application/json") aa$body <- list(foo = "bar") @@ -31,6 +34,8 @@ test_that("RequestSignature: different methods work", { expect_is(aa$uri, "character") expect_is(aa$headers, "list") expect_is(aa$body, "list") + expect_is(aa$fields, "list") + expect_named(aa$fields, "foo") }) test_that("RequestSignature fails well", { From f2f145d9d230e929b3fcd5a9409299c05c718f1c Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 8 Dec 2020 15:03:30 -0800 Subject: [PATCH 09/66] update news, cran comments, bump version, update codemeta.json --- .Rbuildignore | 1 + DESCRIPTION | 2 +- LICENSE.md | 21 +++++++++++++++++++++ NEWS.md | 12 ++++++++++++ codemeta.json | 8 +++++--- cran-comments.md | 6 +++--- 6 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 LICENSE.md diff --git a/.Rbuildignore b/.Rbuildignore index 6aa8291..6d852ae 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -15,3 +15,4 @@ appveyor.yml write_disk_path-changes.R details.* ^_pkgdown.yml$ +^LICENSE\.md$ diff --git a/DESCRIPTION b/DESCRIPTION index 4e3f966..f2a7fdd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.1.91 +Version: 0.7.4 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..be4c2ca --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2020 Scott Chamberlain + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NEWS.md b/NEWS.md index 88291c4..2da128b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,15 @@ +webmockr 0.7.4 +============== + +### MINOR IMPROVEMENTS + +* to support vcr being able to recreate httr objects fully (see github issue ropensci/vcr#132) we needed to handle additional parts of httr request objects: fields and output - with this change vcr should return objects much closer to what real httr requests return (#109) + +### BUG FIXES + +* bug fix + improvement: fixes for simple authentication - `wi_th()` now supports `basic_auth` to mock basic authentication either with `crul::auth()` or `httr::authenticate()` (#108) + + webmockr 0.7.0 ============== diff --git a/codemeta.json b/codemeta.json index b410560..d26c571 100644 --- a/codemeta.json +++ b/codemeta.json @@ -10,13 +10,13 @@ "codeRepository": "https://github.com/ropensci/webmockr", "issueTracker": "https://github.com/ropensci/webmockr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.7.0", + "version": "0.7.4", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.0.2 Patched (2020-09-01 r79114)", + "runtimePlatform": "R version 4.0.3 Patched (2020-12-04 r79565)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -204,7 +204,9 @@ "relatedLink": [ "https://ropenscilabs.github.io/http-testing-book/", "https://books.ropensci.org/http-testing", - "https://docs.ropensci.org/webmockr" + "https://docs.ropensci.org/webmockr", + "https://books.ropensci.org/http-testing/", + "https://docs.ropensci.org/webmockr/" ], "funder": [ { diff --git a/cran-comments.md b/cran-comments.md index 60a8e88..844e33e 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,7 +1,7 @@ ## Test environments -* local OS X install, R 4.0.2 Patched -* ubuntu 16.04 (on travis-ci), R 4.0.2 +* local OS X install, R 4.0.3 Patched +* ubuntu 16.04 (on GitHub Actions), R 4.0.3 * win-builder (devel and release) ## R CMD check results @@ -14,7 +14,7 @@ I have checked the 12 reverse dependencies, and no problems were found. See ( Date: Tue, 8 Dec 2020 15:05:43 -0800 Subject: [PATCH 10/66] updates to gh actions script --- .github/workflows/R-CMD-check.yaml | 42 +++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 9327b2b..c9f604f 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -15,46 +15,62 @@ jobs: - { os: windows-latest, r: 'latest'} - { os: macOS-latest, r: 'latest'} - { os: macOS-latest, r: 'devel'} - - { os: ubuntu-16.04, r: '3.5', cran: "https://demo.rstudiopm.com/all/__linux__/xenial/latest"} - - { os: ubuntu-16.04, r: 'latest', cran: "https://demo.rstudiopm.com/all/__linux__/xenial/latest"} + - { os: ubuntu-16.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} + - { os: ubuntu-16.04, r: 'latest', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - CRAN: ${{ matrix.config.cran }} + CRAN: ${{ matrix.config.rspm }} steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - - uses: r-lib/actions/setup-r@master + - uses: r-lib/actions/setup-r@v1 with: r-version: ${{ matrix.config.r }} - - uses: r-lib/actions/setup-pandoc@master + - uses: r-lib/actions/setup-pandoc@v1 - name: Cache R packages - if: runner.os != 'Windows' - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ env.R_LIBS_USER }} key: ${{ runner.os }}-r-${{ matrix.config.r }}-${{ hashFiles('DESCRIPTION') }} + - name: Install pak + run: | + install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/") + shell: Rscript {0} + - name: Install system dependencies if: runner.os == 'Linux' env: RHUB_PLATFORM: linux-x86_64-ubuntu-gcc run: | - Rscript -e "install.packages('remotes')" -e "remotes::install_github('r-hub/sysreqs')" - sysreqs=$(Rscript -e "cat(sysreqs::sysreq_commands('DESCRIPTION'))") - sudo -s eval "$sysreqs" + while read -r cmd + do + eval sudo $cmd + done < <(Rscript -e 'writeLines(pak::local_system_requirements("ubuntu", "16.04"))') + - name: Install dependencies - run: Rscript -e "install.packages('remotes')" -e "remotes::install_deps(dependencies = TRUE)" -e "remotes::install_cran('rcmdcheck')" + run: | + pak::local_install_dev_deps() + pak::pkg_install("rcmdcheck") + shell: Rscript {0} + + - name: Session info + run: | + options(width = 100) + pkgs <- installed.packages()[, "Package"] + sessioninfo::session_info(pkgs, include_base = TRUE) + shell: Rscript {0} - name: Check run: Rscript -e "rcmdcheck::rcmdcheck(args = '--no-manual', error_on = 'warning', check_dir = 'check')" - name: Upload check results if: failure() - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v2 with: name: ${{ runner.os }}-r${{ matrix.config.r }}-results path: check From b161d68f6fb9ea442da21cca7e4ffd4a3ba37463 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 8 Dec 2020 15:07:24 -0800 Subject: [PATCH 11/66] remove ropensci footer in readme --- README.Rmd | 3 -- README.md | 91 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/README.Rmd b/README.Rmd index 066c035..55a6d93 100644 --- a/README.Rmd +++ b/README.Rmd @@ -289,7 +289,4 @@ simply gives the last response you specified. Although if you set a `to_timeout` * Get citation information for `webmockr` in R doing `citation(package = 'webmockr')` * Please note that this package is released with a [Contributor Code of Conduct](https://ropensci.org/code-of-conduct/). By contributing to this project, you agree to abide by its terms. -[![ropensci_footer](https://ropensci.org/public_images/github_footer.png)](https://ropensci.org) - - [vcr]: https://github.com/ropensci/vcr diff --git a/README.md b/README.md index 3a08936..9e700b2 100644 --- a/README.md +++ b/README.md @@ -128,17 +128,17 @@ stub_request("get", "https://httpbin.org/get") %>% #> body: #> request_headers: #> to_return: -#> status: 200 +#> - status: 200 #> body: success! #> response_headers: -#> should_timeout: FALSE -#> should_raise: FALSE +#> should_timeout: FALSE +#> should_raise: FALSE # check that it's in the stub registry stub_registry() #> #> Registered Stubs -#> GET: https://httpbin.org/get | to_return: with body "success!" with status 200 +#> GET: https://httpbin.org/get | to_return: with body "success!" with status 200 # make the request z <- crul::HttpClient$new(url = "https://httpbin.org")$get("get") @@ -171,12 +171,7 @@ stub_request("get", "https://httpbin.org/get") #> query: #> body: #> request_headers: -#> to_return: -#> status: -#> body: -#> response_headers: -#> should_timeout: FALSE -#> should_raise: FALSE +#> to_return: ``` @@ -186,7 +181,7 @@ x$get('get') #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.54.0 r-curl/4.2 crul/0.9.0 +#> User-Agent: libcurl/7.73.0 r-curl/4.3 crul/1.0.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -209,11 +204,11 @@ stub_request("get", "https://httpbin.org/get") %>% #> body: #> request_headers: #> to_return: -#> status: 418 +#> - status: 418 #> body: #> response_headers: -#> should_timeout: FALSE -#> should_raise: FALSE +#> should_timeout: FALSE +#> should_raise: FALSE ``` @@ -222,7 +217,7 @@ x$get('get', query = list(hello = "world")) #> #> url: https://httpbin.org/get?hello=world #> request_headers: -#> User-Agent: libcurl/7.54.0 r-curl/4.2 crul/0.9.0 +#> User-Agent: libcurl/7.73.0 r-curl/4.3 crul/1.0.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -246,12 +241,7 @@ stub_request("get", "https://httpbin.org/get") %>% #> query: hello=world #> body: #> request_headers: User-Agent=libcurl/7.51.0 r-cur..., Accept-Encoding=gzip, deflate -#> to_return: -#> status: -#> body: -#> response_headers: -#> should_timeout: FALSE -#> should_raise: FALSE +#> to_return: ``` @@ -260,7 +250,7 @@ stub_registry() #> #> Registered Stubs #> GET: https://httpbin.org/get -#> GET: https://httpbin.org/get?hello=world | to_return: with status 418 +#> GET: https://httpbin.org/get?hello=world | to_return: with status 418 #> GET: https://httpbin.org/get?hello=world with headers {"User-Agent":"libcurl/7.51.0 r-curl/2.6 crul/0.3.6","Accept-Encoding":"gzip, deflate"} ``` @@ -271,7 +261,7 @@ x$get('get', query = list(hello = "world")) #> #> url: https://httpbin.org/get?hello=world #> request_headers: -#> User-Agent: libcurl/7.54.0 r-curl/4.2 crul/0.9.0 +#> User-Agent: libcurl/7.73.0 r-curl/4.3 crul/1.0.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -293,11 +283,11 @@ stub_request("post", "https://httpbin.org/post") %>% to_timeout() #> body: #> request_headers: #> to_return: -#> status: +#> - status: #> body: #> response_headers: -#> should_timeout: TRUE -#> should_raise: FALSE +#> should_timeout: TRUE +#> should_raise: FALSE x <- HttpClient$new(url = "https://httpbin.org") x$post('post') #> Error: Request Timeout (HTTP 408). @@ -318,11 +308,11 @@ stub_request("get", "https://httpbin.org/get?a=b") %>% to_raise(HTTPBadRequest) #> body: #> request_headers: #> to_return: -#> status: +#> - status: #> body: #> response_headers: -#> should_timeout: FALSE -#> should_raise: HTTPBadRequest +#> should_timeout: FALSE +#> should_raise: HTTPBadRequest x <- HttpClient$new(url = "https://httpbin.org") x$get('get', query = list(a = "b")) #> Error: Bad Request (HTTP 400). @@ -379,11 +369,11 @@ stub_request('get', uri = 'https://httpbin.org/get') %>% #> body: #> request_headers: Accept=application/json, te... #> to_return: -#> status: 418 +#> - status: 418 #> body: I'm a teapot!!! #> response_headers: im_a=teapot -#> should_timeout: FALSE -#> should_raise: FALSE +#> should_timeout: FALSE +#> should_raise: FALSE ``` now returns mocked response @@ -438,6 +428,40 @@ readLines(out$content) Writing to disk is supported in both `crul` and `httr` +## Many requests in a row + +e.g., many redirects, then a final successful request + + +```r +webmockr::enable() +library(crul) +library(fauxpas) + +z <- stub_request("get", "https://httpbin.org/get") +to_return(z, status = 200, body = "foobar", headers = list(a = 5)) +to_return(z, status = 200, body = "bears", headers = list(b = 6)) +to_raise(z, HTTPBadRequest) +z + +con <- crul::HttpClient$new(url = "https://httpbin.org") +# the first to_return() +first <- con$get("get") +first +first$parse("UTF-8") +# the second to_return() +second <- con$get("get") +second +second$parse("UTF-8") +# the third to_return() - fails as specified +third <- con$get("get") +``` + +Note that subsequent requests past the number of responses given with `to_return()`/etc. +simply gives the last response you specified. Although if you set a `to_timeout` or +`to_raise` this feature won't happen since you fail out. + + ## Contributors * [Scott Chamberlain](https://github.com/sckott) @@ -450,7 +474,4 @@ Writing to disk is supported in both `crul` and `httr` * Get citation information for `webmockr` in R doing `citation(package = 'webmockr')` * Please note that this package is released with a [Contributor Code of Conduct](https://ropensci.org/code-of-conduct/). By contributing to this project, you agree to abide by its terms. -[![ropensci_footer](https://ropensci.org/public_images/github_footer.png)](https://ropensci.org) - - [vcr]: https://github.com/ropensci/vcr From a8acabe042858c0c89e19fdcc1d2addff2302ba2 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 8 Dec 2020 15:16:06 -0800 Subject: [PATCH 12/66] udpate revdep checks --- cran-comments.md | 2 +- revdep/README.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cran-comments.md b/cran-comments.md index 844e33e..1be1cc4 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -10,7 +10,7 @@ ## Reverse dependencies -I have checked the 12 reverse dependencies, and no problems were found. See (). +I have checked the 13 reverse dependencies, and no problems were found. See (). --- diff --git a/revdep/README.md b/revdep/README.md index 5a0f35c..5478be4 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -2,21 +2,21 @@ |field |value | |:--------|:-------------------------------------------| -|version |R version 4.0.2 Patched (2020-09-01 r79114) | -|os |macOS Catalina 10.15.6 | +|version |R version 4.0.3 Patched (2020-12-04 r79565) | +|os |macOS Catalina 10.15.7 | |system |x86_64, darwin17.0 | |ui |X11 | |language |(EN) | |collate |en_US.UTF-8 | |ctype |en_US.UTF-8 | |tz |US/Pacific | -|date |2020-09-29 | +|date |2020-12-08 | # Dependencies -|package |old |new |Δ | -|:--------|:-----|:--------|:--| -|webmockr |0.6.2 |0.6.5.91 |* | +|package |old |new |Δ | +|:--------|:-----|:-----|:--| +|webmockr |0.7.0 |0.7.4 |* | # Revdeps From 37a76749183a97195734583161715ed68de78d36 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 29 Jan 2021 10:00:39 -0800 Subject: [PATCH 13/66] #110 in httr adapter build_httr_response fxn, use the ulr from the response object, not from request --- DESCRIPTION | 2 +- R/adapter-httr.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f2a7fdd..3c464df 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.4 +Version: 0.7.4.93 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/adapter-httr.R b/R/adapter-httr.R index 551e843..9be1476 100644 --- a/R/adapter-httr.R +++ b/R/adapter-httr.R @@ -5,7 +5,7 @@ #' @return a httr response build_httr_response <- function(req, resp) { - try_url <- tryCatch(req$url$url, error = function(e) e) + try_url <- tryCatch(resp$url, error = function(e) e) lst <- list( url = try_url %|s|% req$url, From b4a91b44fee9cb219ed0c261353700ccfc93f70d Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 29 Jan 2021 12:11:00 -0800 Subject: [PATCH 14/66] #110 in crul adapter build_crul_response fxn, use the ulr from the response object, not from request --- DESCRIPTION | 2 +- R/adapter-crul.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3c464df..75f7704 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.4.93 +Version: 0.7.4.94 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/adapter-crul.R b/R/adapter-crul.R index 6dae4eb..55e27bf 100644 --- a/R/adapter-crul.R +++ b/R/adapter-crul.R @@ -31,7 +31,7 @@ build_crul_response <- function(req, resp) { crul::HttpResponse$new( method = req$method, - url = req$url$url, + url = resp$url, status_code = resp$status_code, request_headers = c('User-Agent' = req$options$useragent, req$headers), response_headers = { From 8f7b437b4a00b0bbc84725a52c3601063a47a703 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 25 Feb 2021 08:55:16 -0800 Subject: [PATCH 15/66] #111 new helper fxn handle_separate_redirects used in Adapter class - check if record_separate_redirects=TRUE - if TRUE, set followlocation=0L (dont follow redirects) both in the curl options and in the curl handle --- R/adapter.R | 2 ++ R/zzz.R | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/R/adapter.R b/R/adapter.R index 357244d..8fe07c3 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -113,6 +113,7 @@ Adapter <- R6::R6Class("Adapter", # VCR: recordable/ignored if (vcr_cassette_inserted()) { + req <- handle_separate_redirects(req) # use RequestHandler - gets current cassette & record interaction resp <- private$request_handler(req)$handle() @@ -137,6 +138,7 @@ Adapter <- R6::R6Class("Adapter", # if vcr loaded: record http interaction into vcr namespace # VCR: recordable if (vcr_loaded()) { + req <- handle_separate_redirects(req) # use RequestHandler instead? - which gets current cassette for us resp <- private$request_handler(req)$handle() diff --git a/R/zzz.R b/R/zzz.R index 1e85556..76a179c 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -197,10 +197,21 @@ vcr_loaded <- function() { "package:vcr" %in% search() } +handle_separate_redirects <- function(req) { + cs <- vcr::current_cassette() + stopifnot("record_separate_redirects must be logical" = + is.logical(cs$record_separate_redirects)) + if (cs$record_separate_redirects) { + req$options$followlocation <- 0L + curl::handle_setopt(req$url$handle, followlocation = 0L) + } + return(req) +} + # check whether a cassette is inserted without assuming vcr is installed vcr_cassette_inserted <- function() { if (vcr_loaded()) { return(length(vcr::current_cassette()) > 0) } - return(FALSE) + return(FALSE) } From e518fa149c6f37de3ccdfe3693cdd3304779fa7f Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 25 Feb 2021 12:19:03 -0800 Subject: [PATCH 16/66] #111 fix to handle_separate_redirects() fxn - only set handle opt followlocation if in crul --- R/zzz.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/zzz.R b/R/zzz.R index 76a179c..2add7ab 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -203,7 +203,8 @@ handle_separate_redirects <- function(req) { is.logical(cs$record_separate_redirects)) if (cs$record_separate_redirects) { req$options$followlocation <- 0L - curl::handle_setopt(req$url$handle, followlocation = 0L) + if (is.list(req$url)) + curl::handle_setopt(req$url$handle, followlocation = 0L) } return(req) } From 408bb73d28c9d10cc74408c6ddfde96c66f19180 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 2 Mar 2021 13:30:37 -0800 Subject: [PATCH 17/66] #111 more work on handling redirects - a few new helper fxns, not working yet --- R/adapter.R | 4 ++++ R/zzz.R | 31 ++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/R/adapter.R b/R/adapter.R index 8fe07c3..a24f42c 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -167,6 +167,10 @@ Adapter <- R6::R6Class("Adapter", wi_th(tmp, .list = list(query = urip$parameter, headers = req$headers)) } + # check if new request/response from redirects in vcr + req <- redirects_request(req) + resp <- redirects_response(resp) + } else { private$mock(on = FALSE) resp <- private$fetch_request(req) diff --git a/R/zzz.R b/R/zzz.R index 2add7ab..81ad722 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -197,10 +197,23 @@ vcr_loaded <- function() { "package:vcr" %in% search() } -handle_separate_redirects <- function(req) { +# check whether a cassette is inserted without assuming vcr is installed +vcr_cassette_inserted <- function() { + if (vcr_loaded()) { + return(length(vcr::current_cassette()) > 0) + } + return(FALSE) +} + +check_redirect_setting <- function() { cs <- vcr::current_cassette() stopifnot("record_separate_redirects must be logical" = is.logical(cs$record_separate_redirects)) + return(cs) +} + +handle_separate_redirects <- function(req) { + cs <- check_redirect_setting() if (cs$record_separate_redirects) { req$options$followlocation <- 0L if (is.list(req$url)) @@ -209,10 +222,14 @@ handle_separate_redirects <- function(req) { return(req) } -# check whether a cassette is inserted without assuming vcr is installed -vcr_cassette_inserted <- function() { - if (vcr_loaded()) { - return(length(vcr::current_cassette()) > 0) - } - return(FALSE) +redirects_request <- function(x) { + cs <- check_redirect_setting() + if (cs$record_separate_redirects) return(cs$request_handler$request_original) + x +} + +redirects_response <- function(x) { + cs <- check_redirect_setting() + if (cs$record_separate_redirects) return(last(cs$redirect_pool)[[1]]) + x } From e4cb8b0e420cc483af3f98b25680e06945de79c0 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 2 Mar 2021 13:45:40 -0800 Subject: [PATCH 18/66] undo redirects usage of redirects_request and redirects_response for now --- R/adapter.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/adapter.R b/R/adapter.R index a24f42c..a23edcb 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -168,8 +168,8 @@ Adapter <- R6::R6Class("Adapter", } # check if new request/response from redirects in vcr - req <- redirects_request(req) - resp <- redirects_response(resp) + # req <- redirects_request(req) + # resp <- redirects_response(resp) } else { private$mock(on = FALSE) From 8c5f61b122cd7b93eba97a6979ec53f33ffd50ba Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 2 Mar 2021 13:50:50 -0800 Subject: [PATCH 19/66] undo some redirects changes --- R/adapter.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/adapter.R b/R/adapter.R index a23edcb..621326a 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -113,7 +113,7 @@ Adapter <- R6::R6Class("Adapter", # VCR: recordable/ignored if (vcr_cassette_inserted()) { - req <- handle_separate_redirects(req) + # req <- handle_separate_redirects(req) # use RequestHandler - gets current cassette & record interaction resp <- private$request_handler(req)$handle() @@ -138,7 +138,7 @@ Adapter <- R6::R6Class("Adapter", # if vcr loaded: record http interaction into vcr namespace # VCR: recordable if (vcr_loaded()) { - req <- handle_separate_redirects(req) + # req <- handle_separate_redirects(req) # use RequestHandler instead? - which gets current cassette for us resp <- private$request_handler(req)$handle() From f125a2543e318452bcd29d38a0b180b8e5dda840 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 2 Mar 2021 15:25:43 -0800 Subject: [PATCH 20/66] fix a test for change in vcr --- tests/testthat/test-writing-to-disk-write_disk_path.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-writing-to-disk-write_disk_path.R b/tests/testthat/test-writing-to-disk-write_disk_path.R index 6937681..375c96e 100644 --- a/tests/testthat/test-writing-to-disk-write_disk_path.R +++ b/tests/testthat/test-writing-to-disk-write_disk_path.R @@ -15,9 +15,9 @@ test_that("with crul", { # path not set expect_error( - use_cassette("write_disk_path_not_set_crul_error", { + suppressWarnings(use_cassette("write_disk_path_not_set_crul_error", { out <- HttpClient$new("https://httpbin.org/get")$get(disk = f) - }), + })), "write_disk_path must be given" ) @@ -85,9 +85,9 @@ test_that("with httr", { # path not set expect_error( - use_cassette("write_disk_path_not_set_crul_error", { + suppressWarnings(use_cassette("write_disk_path_not_set_crul_error", { out <- GET("https://httpbin.org/get", write_disk(f)) - }), + })), "write_disk_path must be given" ) From 1a87a40ed7b81cbefbd630c3f2e4adef967f0eb3 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 2 Mar 2021 16:20:08 -0800 Subject: [PATCH 21/66] fix #112 add quiet param --- DESCRIPTION | 2 +- R/adapter.R | 12 ++++++++---- R/flipswitch.R | 13 +++++++------ man/Adapter.Rd | 18 ++++++++++++++++-- man/enable.Rd | 6 ++++-- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 75f7704..424cac5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.4.94 +Version: 0.7.4.95 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/adapter.R b/R/adapter.R index 621326a..b994287 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -65,9 +65,11 @@ Adapter <- R6::R6Class("Adapter", }, #' @description Enable the adapter + #' @param quiet (logical) suppress messages? default: `FALSE` #' @return `TRUE`, invisibly - enable = function() { - message(sprintf("%s enabled!", self$name)) + enable = function(quiet = FALSE) { + assert(quiet, "logical") + if (!quiet) message(sprintf("%s enabled!", self$name)) webmockr_lightswitch[[self$client]] <- TRUE switch(self$client, @@ -77,9 +79,11 @@ Adapter <- R6::R6Class("Adapter", }, #' @description Disable the adapter + #' @param quiet (logical) suppress messages? default: `FALSE` #' @return `FALSE`, invisibly - disable = function() { - message(sprintf("%s disabled!", self$name)) + disable = function(quiet = FALSE) { + assert(quiet, "logical") + if (!quiet) message(sprintf("%s disabled!", self$name)) webmockr_lightswitch[[self$client]] <- FALSE self$remove_stubs() diff --git a/R/flipswitch.R b/R/flipswitch.R index 6535be4..923de2c 100644 --- a/R/flipswitch.R +++ b/R/flipswitch.R @@ -10,6 +10,7 @@ webmockr_adapters <- c('crul', 'httr') #' one or the other. if none given, we attempt to enable both #' adapters #' @param options list of options - ignored for now. +#' @param quiet (logical) suppress messages? default: `FALSE` #' @details `enable()` enables \pkg{webmockr} for all adapters. #' `disable()` disables \pkg{webmockr} for all adapters. `enabled()` #' answers whether \pkg{webmockr} is enabled for a given adapter @@ -17,7 +18,7 @@ webmockr_adapters <- c('crul', 'httr') #' each adapter, as a result of running enable or disable, respectively, #' on each [HttpLibAdapaterRegistry] object. `enabled` returns a #' single boolean -enable <- function(adapter = NULL, options = list()) { +enable <- function(adapter = NULL, options = list(), quiet = FALSE) { adnms <- vapply(http_lib_adapter_registry$adapters, function(w) w$client, "") if (!is.null(adapter)) { if (!adapter %in% webmockr_adapters) { @@ -27,7 +28,7 @@ enable <- function(adapter = NULL, options = list()) { message(adapter, " not installed, skipping enable") return(invisible(FALSE)) } - http_lib_adapter_registry$adapters[[grep(adapter, adnms)]]$enable() + http_lib_adapter_registry$adapters[[grep(adapter, adnms)]]$enable(quiet) } else { invisible(vapply(http_lib_adapter_registry$adapters, function(z) { pkgname <- z$client @@ -37,7 +38,7 @@ enable <- function(adapter = NULL, options = list()) { FALSE } else { # if instaled, enable - z$enable() + z$enable(quiet) } }, logical(1))) } @@ -55,7 +56,7 @@ enabled <- function(adapter = "crul") { #' @export #' @rdname enable -disable <- function(adapter = NULL, options = list()) { +disable <- function(adapter = NULL, options = list(), quiet = FALSE) { adnms <- vapply(http_lib_adapter_registry$adapters, function(w) w$client, "") if (!is.null(adapter)) { if (!adapter %in% webmockr_adapters) { @@ -65,7 +66,7 @@ disable <- function(adapter = NULL, options = list()) { message(adapter, " not installed, skipping disable") return(invisible(FALSE)) } - http_lib_adapter_registry$adapters[[grep(adapter, adnms)]]$disable() + http_lib_adapter_registry$adapters[[grep(adapter, adnms)]]$disable(quiet) } else { invisible(vapply(http_lib_adapter_registry$adapters, function(z) { pkgname <- z$client @@ -75,7 +76,7 @@ disable <- function(adapter = NULL, options = list()) { FALSE } else { # if instaled, disable - z$disable() + z$disable(quiet) } }, logical(1))) } diff --git a/man/Adapter.Rd b/man/Adapter.Rd index f9f2521..516ff57 100644 --- a/man/Adapter.Rd +++ b/man/Adapter.Rd @@ -187,9 +187,16 @@ Create a new Adapter object \subsection{Method \code{enable()}}{ Enable the adapter \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Adapter$enable()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Adapter$enable(quiet = FALSE)}\if{html}{\out{
}} } +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{quiet}}{(logical) suppress messages? default: \code{FALSE}} +} +\if{html}{\out{
}} +} \subsection{Returns}{ \code{TRUE}, invisibly } @@ -200,9 +207,16 @@ Enable the adapter \subsection{Method \code{disable()}}{ Disable the adapter \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Adapter$disable()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Adapter$disable(quiet = FALSE)}\if{html}{\out{
}} } +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{quiet}}{(logical) suppress messages? default: \code{FALSE}} +} +\if{html}{\out{
}} +} \subsection{Returns}{ \code{FALSE}, invisibly } diff --git a/man/enable.Rd b/man/enable.Rd index 4a88f0e..ac9f1e5 100644 --- a/man/enable.Rd +++ b/man/enable.Rd @@ -6,11 +6,11 @@ \alias{disable} \title{Enable or disable webmockr} \usage{ -enable(adapter = NULL, options = list()) +enable(adapter = NULL, options = list(), quiet = FALSE) enabled(adapter = "crul") -disable(adapter = NULL, options = list()) +disable(adapter = NULL, options = list(), quiet = FALSE) } \arguments{ \item{adapter}{(character) the adapter name, 'crul' or 'httr'. @@ -18,6 +18,8 @@ one or the other. if none given, we attempt to enable both adapters} \item{options}{list of options - ignored for now.} + +\item{quiet}{(logical) suppress messages? default: \code{FALSE}} } \value{ \code{enable()} and \code{disable()} invisibly returns booleans for From ee233483fe62adcd33cae895ebd17d79a76bd375 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 12 Mar 2021 10:41:05 -0800 Subject: [PATCH 22/66] fix #100 document diff between to_return and to_raise --- DESCRIPTION | 2 +- R/to_raise.R | 10 ++++++++++ R/to_return.R | 2 ++ man/to_raise.Rd | 12 ++++++++++++ man/to_return.Rd | 12 ++++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 424cac5..2bf7945 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.4.95 +Version: 0.7.5.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/to_raise.R b/R/to_raise.R index 70aa3b9..bb48073 100644 --- a/R/to_raise.R +++ b/R/to_raise.R @@ -8,6 +8,16 @@ #' possible exceptions #' @return an object of class `StubbedRequest`, with print method describing #' the stub +#' @section Raise vs. Return: +#' `to_raise()` always raises a stop condition, while `to_return(status=xyz)` only +#' sets the status code on the returned HTTP response object. So if you want to +#' raise a stop condition then `to_raise()` is what you want. But if you +#' don't want to raise a stop condition use `to_return()`. Use cases for each +#' vary. For example, in a unit test you may have a test expecting a 503 error; +#' in this case `to_raise()` makes sense. In another case, if a unit test +#' expects to test some aspect of an HTTP response object that httr or crul +#' typically returns, then you'll want `to_return()`. +#' #' @details The behavior in the future will be: #' #' When multiple exceptions are passed, the first is used on the first diff --git a/R/to_return.R b/R/to_return.R index 0f64b76..77f1200 100644 --- a/R/to_return.R +++ b/R/to_return.R @@ -47,6 +47,8 @@ #' after which you can use multiple responses again (after creating #' your stub(s) again of course) #' +#' @inheritSection to_raise Raise vs. Return +#' #' @examples #' # first, make a stub object #' foo <- function() { diff --git a/man/to_raise.Rd b/man/to_raise.Rd index 16e972c..0f6a0e4 100644 --- a/man/to_raise.Rd +++ b/man/to_raise.Rd @@ -33,3 +33,15 @@ But for now, only the first exception is used until we get that fixed \note{ see examples in \code{\link[=stub_request]{stub_request()}} } +\section{Raise vs. Return}{ + +\code{to_raise()} always raises a stop condition, while \code{to_return(status=xyz)} only +sets the status code on the returned HTTP response object. So if you want to +raise a stop condition then \code{to_raise()} is what you want. But if you +don't want to raise a stop condition use \code{to_return()}. Use cases for each +vary. For example, in a unit test you may have a test expecting a 503 error; +in this case \code{to_raise()} makes sense. In another case, if a unit test +expects to test some aspect of an HTTP response object that httr or crul +typically returns, then you'll want \code{to_return()}. +} + diff --git a/man/to_return.Rd b/man/to_return.Rd index 56c004b..ab8cdcf 100644 --- a/man/to_return.Rd +++ b/man/to_return.Rd @@ -66,6 +66,18 @@ after which you can use multiple responses again (after creating your stub(s) again of course) } +\section{Raise vs. Return}{ + +\code{to_raise()} always raises a stop condition, while \code{to_return(status=xyz)} only +sets the status code on the returned HTTP response object. So if you want to +raise a stop condition then \code{to_raise()} is what you want. But if you +don't want to raise a stop condition use \code{to_return()}. Use cases for each +vary. For example, in a unit test you may have a test expecting a 503 error; +in this case \code{to_raise()} makes sense. In another case, if a unit test +expects to test some aspect of an HTTP response object that httr or crul +typically returns, then you'll want \code{to_return()}. +} + \examples{ # first, make a stub object foo <- function() { From adde2347687185f41270372a257a1a2562e0aee9 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 12 Mar 2021 12:08:05 -0800 Subject: [PATCH 23/66] rbuildignore readme.md; update readme.md --- .Rbuildignore | 1 + README.md | 14 +++++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index 6d852ae..cec22ea 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -2,6 +2,7 @@ ^\.Rproj\.user$ man-roxygen/ ^README.Rmd$ +^README.md$ ^\.travis.yml$ ^CODE_OF_CONDUCT\.md$ Makefile diff --git a/README.md b/README.md index 9e700b2..8829899 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,7 @@ x$get('get') #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.73.0 r-curl/4.3 crul/1.0.0 +#> User-Agent: libcurl/7.64.1 r-curl/4.3 crul/1.1.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -215,14 +215,12 @@ stub_request("get", "https://httpbin.org/get") %>% ```r x$get('get', query = list(hello = "world")) #> -#> url: https://httpbin.org/get?hello=world +#> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.73.0 r-curl/4.3 crul/1.0.0 +#> User-Agent: libcurl/7.64.1 r-curl/4.3 crul/1.1.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: -#> params: -#> hello: world #> status: 418 ``` @@ -259,14 +257,12 @@ stub_registry() x <- HttpClient$new(url = "https://httpbin.org") x$get('get', query = list(hello = "world")) #> -#> url: https://httpbin.org/get?hello=world +#> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.73.0 r-curl/4.3 crul/1.0.0 +#> User-Agent: libcurl/7.64.1 r-curl/4.3 crul/1.1.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: -#> params: -#> hello: world #> status: 418 ``` From 735fa6be4cc15946c8bc8320e6b016c9298efe3b Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 12 Mar 2021 13:43:40 -0800 Subject: [PATCH 24/66] fix #113 bump version - change build_crul_response in crul adapter to use request url if no response url --- DESCRIPTION | 2 +- R/adapter-crul.R | 3 ++- R/stub_request.R | 7 +++++++ man/stub_request.Rd | 7 +++++++ tests/testthat/test-uri_regex.R | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 2bf7945..63bd96e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.5.91 +Version: 0.7.5.95 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/adapter-crul.R b/R/adapter-crul.R index 55e27bf..c4a2593 100644 --- a/R/adapter-crul.R +++ b/R/adapter-crul.R @@ -31,7 +31,8 @@ build_crul_response <- function(req, resp) { crul::HttpResponse$new( method = req$method, - url = resp$url, + # if resp URL is empty, use URL from request + url = resp$url %||% req$url$url, status_code = resp$status_code, request_headers = c('User-Agent' = req$options$useragent, req$headers), response_headers = { diff --git a/R/stub_request.R b/R/stub_request.R index 15cf3f9..232806e 100644 --- a/R/stub_request.R +++ b/R/stub_request.R @@ -97,11 +97,18 @@ #' # just body #' stub_request("any", uri_regex = ".+") %>% #' wi_th(body = list(foo = 'bar')) +#' ## with crul #' library(crul) #' x <- crul::HttpClient$new(url = "https://httpbin.org") #' crul::mock() #' x$post('post', body = list(foo = 'bar')) #' x$put('put', body = list(foo = 'bar')) +#' ## with httr +#' library(httr) +#' httr_mock() +#' POST('https://example.com', body = list(foo = 'bar')) +#' PUT('https://google.com', body = list(foo = 'bar')) +#' #' #' # just headers #' headers <- list( diff --git a/man/stub_request.Rd b/man/stub_request.Rd index e882abe..5a52e16 100644 --- a/man/stub_request.Rd +++ b/man/stub_request.Rd @@ -118,11 +118,18 @@ wi_th(z, .list = list(query = list(foo = "bar"))) # just body stub_request("any", uri_regex = ".+") \%>\% wi_th(body = list(foo = 'bar')) +## with crul library(crul) x <- crul::HttpClient$new(url = "https://httpbin.org") crul::mock() x$post('post', body = list(foo = 'bar')) x$put('put', body = list(foo = 'bar')) +## with httr +library(httr) +httr_mock() +POST('https://example.com', body = list(foo = 'bar')) +PUT('https://google.com', body = list(foo = 'bar')) + # just headers headers <- list( diff --git a/tests/testthat/test-uri_regex.R b/tests/testthat/test-uri_regex.R index 6882043..a21132f 100644 --- a/tests/testthat/test-uri_regex.R +++ b/tests/testthat/test-uri_regex.R @@ -6,6 +6,7 @@ test_that("uri_regex with crul", { library(crul) enable(adapter = "crul") + invisible( lapply(c('elephants', 'bears', 'leaves', 'foo', 'bar'), function(z) { expect_true(HttpClient$new("https://httpbin.org")$get(z)$success()) @@ -21,8 +22,24 @@ test_that("uri_regex with crul", { "Real HTTP connections are disabled") }) ) + + # regex to match any URL + ## https://github.com/ropensci/webmockr/issues/113 + ## when matching any url with `.+`, it would lead to an empty url in response + ## object, at least with crul + stub_request("get", uri_regex = ".+") + invisible( + lapply(c('Anounce', 'apple', 'Afar', 'after'), function(z) { + url <- sprintf("https://%s.io", z) + res <- HttpClient$new(url)$get(z) + expect_is(res, "HttpResponse") + expect_true(grepl(res$url, file.path(url, z), ignore.case = TRUE)) + }) + ) }) +stub_registry_clear() + test_that("uri_regex with httr", { stub_request("get", uri_regex = "httpbin.org/.+") %>% to_return(body = list(foo = "bar")) @@ -44,4 +61,20 @@ test_that("uri_regex with httr", { "Real HTTP connections are disabled") }) ) + + # regex to match any URL + ## https://github.com/ropensci/webmockr/issues/113 + ## when matching any url with `.+`, it would lead to an empty url in response + ## object, at least with crul + stub_request("get", uri_regex = ".+") + invisible( + lapply(c('Anounce', 'apple', 'Afar', 'after'), function(z) { + url <- sprintf("https://%s.io", z) + res <- GET(url, path = z) + expect_is(res, "response") + expect_true(grepl(res$url, file.path(url, z), ignore.case = TRUE)) + }) + ) }) + +stub_registry_clear() From 0bced07b67a851f20d77f70bf15a39c5de9761d9 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 12 Mar 2021 13:55:05 -0800 Subject: [PATCH 25/66] update revdep checks --- revdep/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/revdep/README.md b/revdep/README.md index 5478be4..b69e5b8 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -2,21 +2,22 @@ |field |value | |:--------|:-------------------------------------------| -|version |R version 4.0.3 Patched (2020-12-04 r79565) | -|os |macOS Catalina 10.15.7 | +|version |R version 4.0.4 Patched (2021-02-17 r80031) | +|os |macOS Big Sur 10.16 | |system |x86_64, darwin17.0 | |ui |X11 | |language |(EN) | |collate |en_US.UTF-8 | |ctype |en_US.UTF-8 | |tz |US/Pacific | -|date |2020-12-08 | +|date |2021-03-12 | # Dependencies -|package |old |new |Δ | -|:--------|:-----|:-----|:--| -|webmockr |0.7.0 |0.7.4 |* | +|package |old |new |Δ | +|:--------|:-----|:--------|:--| +|webmockr |0.7.4 |0.7.5.95 |* | +|crul |NA |1.1.0 |* | # Revdeps From 110a1ea9a9ac7f393a2d3a0582b5356107feeb7d Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 12 Mar 2021 14:05:23 -0800 Subject: [PATCH 26/66] update news, cran comments, and codemeta.json --- DESCRIPTION | 2 +- NEWS.md | 13 +++++++++++++ codemeta.json | 6 +++--- cran-comments.md | 6 +++--- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 63bd96e..04e8bf4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.7.5.95 +Version: 0.8.0 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/NEWS.md b/NEWS.md index 2da128b..af668b7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,16 @@ +webmockr 0.8.0 +============== + +### NEW FEATURES + +* `enable()` and the `enable()` method on the `Adapter` R6 class gain new parameter `quiet` to toggle whether messages are printed or not (#112) + +### MINOR IMPROVEMENTS + +* to re-create http response objects for both httr and crul we were using the url from the request object; now we use the url from the response object, BUT if there is no url in the response object we fall back to using the url from the request object (#110) (#113) +* improve docs: add further explanation to manual files for both `to_raise()` and `to_return()` to explain the differenc between them and when you may want to use them (#100) + + webmockr 0.7.4 ============== diff --git a/codemeta.json b/codemeta.json index d26c571..7798477 100644 --- a/codemeta.json +++ b/codemeta.json @@ -10,13 +10,13 @@ "codeRepository": "https://github.com/ropensci/webmockr", "issueTracker": "https://github.com/ropensci/webmockr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.7.4", + "version": "0.8.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.0.3 Patched (2020-12-04 r79565)", + "runtimePlatform": "R version 4.0.4 Patched (2021-02-17 r80031)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -200,7 +200,7 @@ "developmentStatus": "https://www.repostatus.org/#active", "releaseNotes": "https://github.com/ropensci/webmockr/blob/master/NEWS.md", "readme": "https://github.com/ropensci/webmockr/blob/master/README.md", - "fileSize": "0KB", + "fileSize": "342.582KB", "relatedLink": [ "https://ropenscilabs.github.io/http-testing-book/", "https://books.ropensci.org/http-testing", diff --git a/cran-comments.md b/cran-comments.md index 1be1cc4..9280bf3 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,7 +1,7 @@ ## Test environments -* local OS X install, R 4.0.3 Patched -* ubuntu 16.04 (on GitHub Actions), R 4.0.3 +* local macOS install, R 4.0.4 Patched +* ubuntu (on GitHub Actions), R 4.0.4 * win-builder (devel and release) ## R CMD check results @@ -14,7 +14,7 @@ I have checked the 13 reverse dependencies, and no problems were found. See ( Date: Sat, 13 Mar 2021 12:20:07 -0800 Subject: [PATCH 27/66] gh actions: run on windows R-devel --- .github/workflows/R-CMD-check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index c9f604f..214445c 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -13,6 +13,7 @@ jobs: matrix: config: - { os: windows-latest, r: 'latest'} + - { os: windows-latest, r: 'devel'} - { os: macOS-latest, r: 'latest'} - { os: macOS-latest, r: 'devel'} - { os: ubuntu-16.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} From f3da1da0c9415dc008aa3a2ebc025a5677248b29 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 14 Oct 2021 09:04:47 -0700 Subject: [PATCH 28/66] #114 change to UriPattern to make sure regex matching is as intended --- R/RequestPattern.R | 10 +++++++--- R/stub_request.R | 17 +++++++++++------ man/UriPattern.Rd | 4 ++-- man/stub_request.Rd | 20 ++++++++++++-------- tests/testthat/test-RequestPattern.R | 3 +-- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/R/RequestPattern.R b/R/RequestPattern.R index 6d0e7dd..8bff27c 100644 --- a/R/RequestPattern.R +++ b/R/RequestPattern.R @@ -506,7 +506,7 @@ BODY_FORMATS <- list( #' z$matches("https://foobar.com/pizzas?pizza=cheese") # FALSE #' #' # query parameters in the regex uri -#' (z <- UriPattern$new(regex_pattern = "https://x.com/.+/order?fruit=apple")) +#' (z <- UriPattern$new(regex_pattern = "https://x.com/.+/order\\?fruit=apple")) #' z$add_query_params() # have to run this method to gather query params #' z$matches("https://x.com/a/order?fruit=apple") # TRUE #' z$matches("https://x.com/a?fruit=apple") # FALSE @@ -544,12 +544,15 @@ UriPattern <- R6::R6Class( self$pattern <- normalize_uri(pattern, self$regex) }, - #' @description Match a list of headers against that stored + #' @description Match a uri against a pattern #' @param uri (character) a uri #' @return a boolean matches = function(uri) { uri <- normalize_uri(uri, self$regex) - self$pattern_matches(uri) && self$query_params_matches(uri) + if (self$regex) + grepl(self$pattern, uri) + else + self$pattern_matches(uri) && self$query_params_matches(uri) }, #' @description Match a URI @@ -580,6 +583,7 @@ UriPattern <- R6::R6Class( #' @param query_params (list|character) list or character #' @return nothing returned, updates uri pattern add_query_params = function(query_params) { + if (self$regex) return(NULL) if (missing(query_params) || is.null(query_params)) { self$query_params <- self$extract_query(self$pattern) } else { diff --git a/R/stub_request.R b/R/stub_request.R index 232806e..95f2fb2 100644 --- a/R/stub_request.R +++ b/R/stub_request.R @@ -6,9 +6,9 @@ #' @param uri (character) The request uri. Can be a full or partial uri. #' \pkg{webmockr} can match uri's without the "http" scheme, but does #' not match if the scheme is "https". required, unless `uri_regex` given. -#' See [UriPattern] for more. +#' See [UriPattern] for more. See the "uri vs. uri_regex" section #' @param uri_regex (character) A URI represented as regex. required, if `uri` -#' not given. See examples +#' not given. See examples and the "uri vs. uri_regex" section #' @return an object of class `StubbedRequest`, with print method describing #' the stub. #' @details Internally, this calls [StubbedRequest] which handles the logic @@ -29,10 +29,15 @@ #' [to_return()] for details on how response status/body/headers #' are handled #' -#' @section Matching URI's: -#' - Trailing slashes are dropped from stub URIs before matching -#' - Query parameters are dropped from stub URIs before matching; -#' URIs are compared without query parameters +#' @note Trailing slashes are dropped from stub URIs before matching +#' +#' @section uri vs. uri_regex: +#' When you use `uri`, we compare the URIs without query params AND +#' also the query params themselves without the URIs. +#' +#' When you use `uri_regex` we don't compare URIs and query params; +#' we just use your regex string defined in `uri_regex` as the pattern +#' for a call to [grepl] #' #' @section Mocking writing to disk: #' See [mocking-disk-writing] diff --git a/man/UriPattern.Rd b/man/UriPattern.Rd index 83ce3ff..45824e8 100644 --- a/man/UriPattern.Rd +++ b/man/UriPattern.Rd @@ -64,7 +64,7 @@ z$matches("https://foobar.com/pizzas/order?pizza=cheese") # TRUE z$matches("https://foobar.com/pizzas?pizza=cheese") # FALSE # query parameters in the regex uri -(z <- UriPattern$new(regex_pattern = "https://x.com/.+/order?fruit=apple")) +(z <- UriPattern$new(regex_pattern = "https://x.com/.+/order\\\\?fruit=apple")) z$add_query_params() # have to run this method to gather query params z$matches("https://x.com/a/order?fruit=apple") # TRUE z$matches("https://x.com/a?fruit=apple") # FALSE @@ -131,7 +131,7 @@ A new \code{UriPattern} object \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-matches}{}}} \subsection{Method \code{matches()}}{ -Match a list of headers against that stored +Match a uri against a pattern \subsection{Usage}{ \if{html}{\out{
}}\preformatted{UriPattern$matches(uri)}\if{html}{\out{
}} } diff --git a/man/stub_request.Rd b/man/stub_request.Rd index 5a52e16..8be7c52 100644 --- a/man/stub_request.Rd +++ b/man/stub_request.Rd @@ -13,10 +13,10 @@ stub_request(method = "get", uri = NULL, uri_regex = NULL) \item{uri}{(character) The request uri. Can be a full or partial uri. \pkg{webmockr} can match uri's without the "http" scheme, but does not match if the scheme is "https". required, unless \code{uri_regex} given. -See \link{UriPattern} for more.} +See \link{UriPattern} for more. See the "uri vs. uri_regex" section} \item{uri_regex}{(character) A URI represented as regex. required, if \code{uri} -not given. See examples} +not given. See examples and the "uri vs. uri_regex" section} } \value{ an object of class \code{StubbedRequest}, with print method describing @@ -44,13 +44,17 @@ See \code{\link[=wi_th]{wi_th()}} for details on request body/query/headers and \code{\link[=to_return]{to_return()}} for details on how response status/body/headers are handled } -\section{Matching URI's}{ - -\itemize{ -\item Trailing slashes are dropped from stub URIs before matching -\item Query parameters are dropped from stub URIs before matching; -URIs are compared without query parameters +\note{ +Trailing slashes are dropped from stub URIs before matching } +\section{uri vs. uri_regex}{ + +When you use \code{uri}, we compare the URIs without query params AND +also the query params themselves without the URIs. + +When you use \code{uri_regex} we don't compare URIs and query params; +we just use your regex string defined in \code{uri_regex} as the pattern +for a call to \link{grepl} } \section{Mocking writing to disk}{ diff --git a/tests/testthat/test-RequestPattern.R b/tests/testthat/test-RequestPattern.R index 08fd2ed..1893ed9 100644 --- a/tests/testthat/test-RequestPattern.R +++ b/tests/testthat/test-RequestPattern.R @@ -186,8 +186,7 @@ test_that("UriPattern: structure is correct", { expect_false(z$matches("https://foobar.com")) # regex with query parameters - z <- UriPattern$new(regex_pattern = "https://x.com/.+/order?fruit=apple") - z$add_query_params() # this is done automatically in the pkg + z <- UriPattern$new(regex_pattern = "https://x.com/.+/order\\?fruit=apple") expect_is(z, "UriPattern") expect_is(z$pattern, "character") From 76f9ec50a797f7f70622a6af781b2e20eff5f0bf Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Mon, 27 Dec 2021 16:27:25 -0800 Subject: [PATCH 29/66] bump roxygen version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 04e8bf4..4a82c7d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,7 +37,7 @@ Suggests: xml2, vcr, httr -RoxygenNote: 7.1.1 +RoxygenNote: 7.1.2 X-schema.org-applicationCategory: Web X-schema.org-keywords: http, https, API, web-services, curl, mock, mocking, fakeweb, http-mocking, testing, testing-tools, tdd X-schema.org-isPartOf: https://ropensci.org From 45e0118934053fd5123a003d81d647d7c35b9153 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Aug 2022 22:01:26 -0700 Subject: [PATCH 30/66] update man files for new roxygen2 version, bump dev version --- DESCRIPTION | 4 +- man/Adapter.Rd | 100 ++++++++++++++++----------------- man/BodyPattern.Rd | 24 ++++---- man/HashCounter.Rd | 18 +++--- man/HeadersPattern.Rd | 30 +++++----- man/HttpLibAdapaterRegistry.Rd | 18 +++--- man/MethodPattern.Rd | 24 ++++---- man/RequestPattern.Rd | 24 ++++---- man/RequestRegistry.Rd | 30 +++++----- man/RequestSignature.Rd | 24 ++++---- man/Response.Rd | 90 ++++++++++++++--------------- man/StubRegistry.Rd | 48 ++++++++-------- man/StubbedRequest.Rd | 48 ++++++++-------- man/UriPattern.Rd | 48 ++++++++-------- 14 files changed, 265 insertions(+), 265 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4a82c7d..bd68c33 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.0 +Version: 0.8.1.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), @@ -37,7 +37,7 @@ Suggests: xml2, vcr, httr -RoxygenNote: 7.1.2 +RoxygenNote: 7.2.1 X-schema.org-applicationCategory: Web X-schema.org-keywords: http, https, API, web-services, curl, mock, mocking, fakeweb, http-mocking, testing, testing-tools, tdd X-schema.org-isPartOf: https://ropensci.org diff --git a/man/Adapter.Rd b/man/Adapter.Rd index 516ff57..b6276cb 100644 --- a/man/Adapter.Rd +++ b/man/Adapter.Rd @@ -72,23 +72,23 @@ if (requireNamespace("httr", quietly = TRUE)) { \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-clone}{\code{CrulAdapter$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../webmockr/html/Adapter.html#method-disable}{\code{webmockr::Adapter$disable()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-enable}{\code{webmockr::Adapter$enable()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-handle_request}{\code{webmockr::Adapter$handle_request()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-initialize}{\code{webmockr::Adapter$initialize()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-remove_stubs}{\code{webmockr::Adapter$remove_stubs()}}\out{} -} -\out{
} -} +\item \href{#method-CrulAdapter-clone}{\code{CrulAdapter$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-CrulAdapter-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ @@ -119,23 +119,23 @@ The objects of this class are cloneable with this method. \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-clone}{\code{HttrAdapter$clone()}} -} -} -\if{html}{ -\out{
Inherited methods} -\itemize{ -\item \out{}\href{../../webmockr/html/Adapter.html#method-disable}{\code{webmockr::Adapter$disable()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-enable}{\code{webmockr::Adapter$enable()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-handle_request}{\code{webmockr::Adapter$handle_request()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-initialize}{\code{webmockr::Adapter$initialize()}}\out{} -\item \out{}\href{../../webmockr/html/Adapter.html#method-remove_stubs}{\code{webmockr::Adapter$remove_stubs()}}\out{} -} -\out{
} -} +\item \href{#method-HttrAdapter-clone}{\code{HttrAdapter$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HttrAdapter-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ @@ -163,17 +163,17 @@ The objects of this class are cloneable with this method. \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Adapter$new()}} -\item \href{#method-enable}{\code{Adapter$enable()}} -\item \href{#method-disable}{\code{Adapter$disable()}} -\item \href{#method-handle_request}{\code{Adapter$handle_request()}} -\item \href{#method-remove_stubs}{\code{Adapter$remove_stubs()}} -\item \href{#method-clone}{\code{Adapter$clone()}} +\item \href{#method-Adapter-new}{\code{Adapter$new()}} +\item \href{#method-Adapter-enable}{\code{Adapter$enable()}} +\item \href{#method-Adapter-disable}{\code{Adapter$disable()}} +\item \href{#method-Adapter-handle_request}{\code{Adapter$handle_request()}} +\item \href{#method-Adapter-remove_stubs}{\code{Adapter$remove_stubs()}} +\item \href{#method-Adapter-clone}{\code{Adapter$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Adapter-new}{}}} \subsection{Method \code{new()}}{ Create a new Adapter object \subsection{Usage}{ @@ -182,8 +182,8 @@ Create a new Adapter object } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-enable}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Adapter-enable}{}}} \subsection{Method \code{enable()}}{ Enable the adapter \subsection{Usage}{ @@ -202,8 +202,8 @@ Enable the adapter } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-disable}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Adapter-disable}{}}} \subsection{Method \code{disable()}}{ Disable the adapter \subsection{Usage}{ @@ -222,8 +222,8 @@ Disable the adapter } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-handle_request}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Adapter-handle_request}{}}} \subsection{Method \code{handle_request()}}{ All logic for handling a request \subsection{Usage}{ @@ -242,8 +242,8 @@ various outcomes } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-remove_stubs}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Adapter-remove_stubs}{}}} \subsection{Method \code{remove_stubs()}}{ Remove all stubs \subsection{Usage}{ @@ -255,8 +255,8 @@ nothing returned; removes all request stubs } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Adapter-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/BodyPattern.Rd b/man/BodyPattern.Rd index ab9bbfe..407521a 100644 --- a/man/BodyPattern.Rd +++ b/man/BodyPattern.Rd @@ -58,15 +58,15 @@ z$matches(bb$body) \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{BodyPattern$new()}} -\item \href{#method-matches}{\code{BodyPattern$matches()}} -\item \href{#method-to_s}{\code{BodyPattern$to_s()}} -\item \href{#method-clone}{\code{BodyPattern$clone()}} +\item \href{#method-BodyPattern-new}{\code{BodyPattern$new()}} +\item \href{#method-BodyPattern-matches}{\code{BodyPattern$matches()}} +\item \href{#method-BodyPattern-to_s}{\code{BodyPattern$to_s()}} +\item \href{#method-BodyPattern-clone}{\code{BodyPattern$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-BodyPattern-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{BodyPattern} object \subsection{Usage}{ @@ -85,8 +85,8 @@ A new \code{BodyPattern} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-BodyPattern-matches}{}}} \subsection{Method \code{matches()}}{ Match a list of headers against that stored \subsection{Usage}{ @@ -107,8 +107,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-BodyPattern-to_s}{}}} \subsection{Method \code{to_s()}}{ Print pattern for easy human consumption \subsection{Usage}{ @@ -120,8 +120,8 @@ a string } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-BodyPattern-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/HashCounter.Rd b/man/HashCounter.Rd index 2950cde..bf52c10 100644 --- a/man/HashCounter.Rd +++ b/man/HashCounter.Rd @@ -34,14 +34,14 @@ Other request-registry: \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-put}{\code{HashCounter$put()}} -\item \href{#method-get}{\code{HashCounter$get()}} -\item \href{#method-clone}{\code{HashCounter$clone()}} +\item \href{#method-HashCounter-put}{\code{HashCounter$put()}} +\item \href{#method-HashCounter-get}{\code{HashCounter$get()}} +\item \href{#method-HashCounter-clone}{\code{HashCounter$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-put}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HashCounter-put}{}}} \subsection{Method \code{put()}}{ Register a request by it's key \subsection{Usage}{ @@ -61,8 +61,8 @@ internal counter } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HashCounter-get}{}}} \subsection{Method \code{get()}}{ Get a request by key \subsection{Usage}{ @@ -81,8 +81,8 @@ Get a request by key } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HashCounter-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/HeadersPattern.Rd b/man/HeadersPattern.Rd index 2737d3c..ac2712a 100644 --- a/man/HeadersPattern.Rd +++ b/man/HeadersPattern.Rd @@ -50,16 +50,16 @@ x$matches(headers) \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{HeadersPattern$new()}} -\item \href{#method-matches}{\code{HeadersPattern$matches()}} -\item \href{#method-empty_headers}{\code{HeadersPattern$empty_headers()}} -\item \href{#method-to_s}{\code{HeadersPattern$to_s()}} -\item \href{#method-clone}{\code{HeadersPattern$clone()}} +\item \href{#method-HeadersPattern-new}{\code{HeadersPattern$new()}} +\item \href{#method-HeadersPattern-matches}{\code{HeadersPattern$matches()}} +\item \href{#method-HeadersPattern-empty_headers}{\code{HeadersPattern$empty_headers()}} +\item \href{#method-HeadersPattern-to_s}{\code{HeadersPattern$to_s()}} +\item \href{#method-HeadersPattern-clone}{\code{HeadersPattern$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HeadersPattern-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{HeadersPattern} object \subsection{Usage}{ @@ -79,8 +79,8 @@ A new \code{HeadersPattern} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HeadersPattern-matches}{}}} \subsection{Method \code{matches()}}{ Match a list of headers against that stored \subsection{Usage}{ @@ -99,8 +99,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-empty_headers}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HeadersPattern-empty_headers}{}}} \subsection{Method \code{empty_headers()}}{ Are headers empty? tests if null or length==0 \subsection{Usage}{ @@ -119,8 +119,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HeadersPattern-to_s}{}}} \subsection{Method \code{to_s()}}{ Print pattern for easy human consumption \subsection{Usage}{ @@ -132,8 +132,8 @@ a string } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HeadersPattern-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/HttpLibAdapaterRegistry.Rd b/man/HttpLibAdapaterRegistry.Rd index 2d9c2e4..c1fed1b 100644 --- a/man/HttpLibAdapaterRegistry.Rd +++ b/man/HttpLibAdapaterRegistry.Rd @@ -23,14 +23,14 @@ x$adapters[[1]]$name \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-print}{\code{HttpLibAdapaterRegistry$print()}} -\item \href{#method-register}{\code{HttpLibAdapaterRegistry$register()}} -\item \href{#method-clone}{\code{HttpLibAdapaterRegistry$clone()}} +\item \href{#method-HttpLibAdapaterRegistry-print}{\code{HttpLibAdapaterRegistry$print()}} +\item \href{#method-HttpLibAdapaterRegistry-register}{\code{HttpLibAdapaterRegistry$register()}} +\item \href{#method-HttpLibAdapaterRegistry-clone}{\code{HttpLibAdapaterRegistry$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HttpLibAdapaterRegistry-print}{}}} \subsection{Method \code{print()}}{ print method for the \code{HttpLibAdapaterRegistry} class \subsection{Usage}{ @@ -48,8 +48,8 @@ print method for the \code{HttpLibAdapaterRegistry} class } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-register}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HttpLibAdapaterRegistry-register}{}}} \subsection{Method \code{register()}}{ Register an http library adapter \subsection{Usage}{ @@ -68,8 +68,8 @@ nothing, registers the library adapter } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-HttpLibAdapaterRegistry-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/MethodPattern.Rd b/man/MethodPattern.Rd index 5891b95..f2c1e68 100644 --- a/man/MethodPattern.Rd +++ b/man/MethodPattern.Rd @@ -33,15 +33,15 @@ x$matches(method = "HEAD") \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{MethodPattern$new()}} -\item \href{#method-matches}{\code{MethodPattern$matches()}} -\item \href{#method-to_s}{\code{MethodPattern$to_s()}} -\item \href{#method-clone}{\code{MethodPattern$clone()}} +\item \href{#method-MethodPattern-new}{\code{MethodPattern$new()}} +\item \href{#method-MethodPattern-matches}{\code{MethodPattern$matches()}} +\item \href{#method-MethodPattern-to_s}{\code{MethodPattern$to_s()}} +\item \href{#method-MethodPattern-clone}{\code{MethodPattern$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-MethodPattern-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{MethodPattern} object \subsection{Usage}{ @@ -60,8 +60,8 @@ A new \code{MethodPattern} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-MethodPattern-matches}{}}} \subsection{Method \code{matches()}}{ test if the pattern matches a given http method \subsection{Usage}{ @@ -80,8 +80,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-MethodPattern-to_s}{}}} \subsection{Method \code{to_s()}}{ Print pattern for easy human consumption \subsection{Usage}{ @@ -93,8 +93,8 @@ a string } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-MethodPattern-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/RequestPattern.Rd b/man/RequestPattern.Rd index 331796d..5885fa4 100644 --- a/man/RequestPattern.Rd +++ b/man/RequestPattern.Rd @@ -86,15 +86,15 @@ pattern classes for HTTP method \link{MethodPattern}, headers \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{RequestPattern$new()}} -\item \href{#method-matches}{\code{RequestPattern$matches()}} -\item \href{#method-to_s}{\code{RequestPattern$to_s()}} -\item \href{#method-clone}{\code{RequestPattern$clone()}} +\item \href{#method-RequestPattern-new}{\code{RequestPattern$new()}} +\item \href{#method-RequestPattern-matches}{\code{RequestPattern$matches()}} +\item \href{#method-RequestPattern-to_s}{\code{RequestPattern$to_s()}} +\item \href{#method-RequestPattern-clone}{\code{RequestPattern$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestPattern-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{RequestPattern} object \subsection{Usage}{ @@ -131,8 +131,8 @@ A new \code{RequestPattern} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestPattern-matches}{}}} \subsection{Method \code{matches()}}{ does a request signature match the selected matchers? \subsection{Usage}{ @@ -151,8 +151,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestPattern-to_s}{}}} \subsection{Method \code{to_s()}}{ Print pattern for easy human consumption \subsection{Usage}{ @@ -164,8 +164,8 @@ a string } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestPattern-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/RequestRegistry.Rd b/man/RequestRegistry.Rd index bbe29a3..bbbf015 100644 --- a/man/RequestRegistry.Rd +++ b/man/RequestRegistry.Rd @@ -72,16 +72,16 @@ Other request-registry: \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-print}{\code{RequestRegistry$print()}} -\item \href{#method-reset}{\code{RequestRegistry$reset()}} -\item \href{#method-register_request}{\code{RequestRegistry$register_request()}} -\item \href{#method-times_executed}{\code{RequestRegistry$times_executed()}} -\item \href{#method-clone}{\code{RequestRegistry$clone()}} +\item \href{#method-RequestRegistry-print}{\code{RequestRegistry$print()}} +\item \href{#method-RequestRegistry-reset}{\code{RequestRegistry$reset()}} +\item \href{#method-RequestRegistry-register_request}{\code{RequestRegistry$register_request()}} +\item \href{#method-RequestRegistry-times_executed}{\code{RequestRegistry$times_executed()}} +\item \href{#method-RequestRegistry-clone}{\code{RequestRegistry$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestRegistry-print}{}}} \subsection{Method \code{print()}}{ print method for the \code{RequestRegistry} class \subsection{Usage}{ @@ -99,8 +99,8 @@ print method for the \code{RequestRegistry} class } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-reset}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestRegistry-reset}{}}} \subsection{Method \code{reset()}}{ Reset the registry to no registered requests \subsection{Usage}{ @@ -112,8 +112,8 @@ nothing returned; ressets registry to no requests } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-register_request}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestRegistry-register_request}{}}} \subsection{Method \code{register_request()}}{ Register a request \subsection{Usage}{ @@ -133,8 +133,8 @@ nothing returned; registers the request } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-times_executed}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestRegistry-times_executed}{}}} \subsection{Method \code{times_executed()}}{ How many times has a request been made \subsection{Usage}{ @@ -157,8 +157,8 @@ integer, the number of times the request has been made } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestRegistry-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/RequestSignature.Rd b/man/RequestSignature.Rd index bdf4863..28d98d9 100644 --- a/man/RequestSignature.Rd +++ b/man/RequestSignature.Rd @@ -79,15 +79,15 @@ bb$to_s() \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{RequestSignature$new()}} -\item \href{#method-print}{\code{RequestSignature$print()}} -\item \href{#method-to_s}{\code{RequestSignature$to_s()}} -\item \href{#method-clone}{\code{RequestSignature$clone()}} +\item \href{#method-RequestSignature-new}{\code{RequestSignature$new()}} +\item \href{#method-RequestSignature-print}{\code{RequestSignature$print()}} +\item \href{#method-RequestSignature-to_s}{\code{RequestSignature$to_s()}} +\item \href{#method-RequestSignature-clone}{\code{RequestSignature$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestSignature-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{RequestSignature} object \subsection{Usage}{ @@ -111,8 +111,8 @@ A new \code{RequestSignature} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestSignature-print}{}}} \subsection{Method \code{print()}}{ print method for the \code{RequestSignature} class \subsection{Usage}{ @@ -130,8 +130,8 @@ print method for the \code{RequestSignature} class } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestSignature-to_s}{}}} \subsection{Method \code{to_s()}}{ Request signature to a string \subsection{Usage}{ @@ -143,8 +143,8 @@ a character string representation of the request signature } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-RequestSignature-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/Response.Rd b/man/Response.Rd index daca248..0beefae 100644 --- a/man/Response.Rd +++ b/man/Response.Rd @@ -64,26 +64,26 @@ x$get_exception() \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{Response$new()}} -\item \href{#method-print}{\code{Response$print()}} -\item \href{#method-set_url}{\code{Response$set_url()}} -\item \href{#method-get_url}{\code{Response$get_url()}} -\item \href{#method-set_request_headers}{\code{Response$set_request_headers()}} -\item \href{#method-get_request_headers}{\code{Response$get_request_headers()}} -\item \href{#method-set_response_headers}{\code{Response$set_response_headers()}} -\item \href{#method-get_respone_headers}{\code{Response$get_respone_headers()}} -\item \href{#method-set_body}{\code{Response$set_body()}} -\item \href{#method-get_body}{\code{Response$get_body()}} -\item \href{#method-set_status}{\code{Response$set_status()}} -\item \href{#method-get_status}{\code{Response$get_status()}} -\item \href{#method-set_exception}{\code{Response$set_exception()}} -\item \href{#method-get_exception}{\code{Response$get_exception()}} -\item \href{#method-clone}{\code{Response$clone()}} +\item \href{#method-Response-new}{\code{Response$new()}} +\item \href{#method-Response-print}{\code{Response$print()}} +\item \href{#method-Response-set_url}{\code{Response$set_url()}} +\item \href{#method-Response-get_url}{\code{Response$get_url()}} +\item \href{#method-Response-set_request_headers}{\code{Response$set_request_headers()}} +\item \href{#method-Response-get_request_headers}{\code{Response$get_request_headers()}} +\item \href{#method-Response-set_response_headers}{\code{Response$set_response_headers()}} +\item \href{#method-Response-get_respone_headers}{\code{Response$get_respone_headers()}} +\item \href{#method-Response-set_body}{\code{Response$set_body()}} +\item \href{#method-Response-get_body}{\code{Response$get_body()}} +\item \href{#method-Response-set_status}{\code{Response$set_status()}} +\item \href{#method-Response-get_status}{\code{Response$get_status()}} +\item \href{#method-Response-set_exception}{\code{Response$set_exception()}} +\item \href{#method-Response-get_exception}{\code{Response$get_exception()}} +\item \href{#method-Response-clone}{\code{Response$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{Response} object \subsection{Usage}{ @@ -102,8 +102,8 @@ A new \code{Response} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-print}{}}} \subsection{Method \code{print()}}{ print method for the \code{Response} class \subsection{Usage}{ @@ -121,8 +121,8 @@ print method for the \code{Response} class } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-set_url}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-set_url}{}}} \subsection{Method \code{set_url()}}{ set the url for the response \subsection{Usage}{ @@ -141,8 +141,8 @@ nothing returned; sets url } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_url}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-get_url}{}}} \subsection{Method \code{get_url()}}{ get the url for the response \subsection{Usage}{ @@ -154,8 +154,8 @@ get the url for the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-set_request_headers}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-set_request_headers}{}}} \subsection{Method \code{set_request_headers()}}{ set the request headers for the response \subsection{Usage}{ @@ -177,8 +177,8 @@ nothing returned; sets request headers on the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_request_headers}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-get_request_headers}{}}} \subsection{Method \code{get_request_headers()}}{ get the request headers for the response \subsection{Usage}{ @@ -190,8 +190,8 @@ get the request headers for the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-set_response_headers}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-set_response_headers}{}}} \subsection{Method \code{set_response_headers()}}{ set the response headers for the response \subsection{Usage}{ @@ -213,8 +213,8 @@ nothing returned; sets response headers on the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_respone_headers}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-get_respone_headers}{}}} \subsection{Method \code{get_respone_headers()}}{ get the response headers for the response \subsection{Usage}{ @@ -226,8 +226,8 @@ get the response headers for the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-set_body}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-set_body}{}}} \subsection{Method \code{set_body()}}{ set the body of the response \subsection{Usage}{ @@ -248,8 +248,8 @@ nothing returned; sets body on the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_body}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-get_body}{}}} \subsection{Method \code{get_body()}}{ get the body of the response \subsection{Usage}{ @@ -261,8 +261,8 @@ various } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-set_status}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-set_status}{}}} \subsection{Method \code{set_status()}}{ set the http status of the response \subsection{Usage}{ @@ -281,8 +281,8 @@ nothing returned; sets the http status of the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_status}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-get_status}{}}} \subsection{Method \code{get_status()}}{ get the http status of the response \subsection{Usage}{ @@ -294,8 +294,8 @@ get the http status of the response } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-set_exception}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-set_exception}{}}} \subsection{Method \code{set_exception()}}{ set an exception \subsection{Usage}{ @@ -314,8 +314,8 @@ nothing returned; sets an exception } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-get_exception}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-get_exception}{}}} \subsection{Method \code{get_exception()}}{ get the exception, if set \subsection{Usage}{ @@ -327,8 +327,8 @@ get the exception, if set } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Response-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/StubRegistry.Rd b/man/StubRegistry.Rd index b4a6402..cbb146d 100644 --- a/man/StubRegistry.Rd +++ b/man/StubRegistry.Rd @@ -45,19 +45,19 @@ Other stub-registry: \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-print}{\code{StubRegistry$print()}} -\item \href{#method-register_stub}{\code{StubRegistry$register_stub()}} -\item \href{#method-find_stubbed_request}{\code{StubRegistry$find_stubbed_request()}} -\item \href{#method-request_stub_for}{\code{StubRegistry$request_stub_for()}} -\item \href{#method-remove_request_stub}{\code{StubRegistry$remove_request_stub()}} -\item \href{#method-remove_all_request_stubs}{\code{StubRegistry$remove_all_request_stubs()}} -\item \href{#method-is_registered}{\code{StubRegistry$is_registered()}} -\item \href{#method-clone}{\code{StubRegistry$clone()}} +\item \href{#method-StubRegistry-print}{\code{StubRegistry$print()}} +\item \href{#method-StubRegistry-register_stub}{\code{StubRegistry$register_stub()}} +\item \href{#method-StubRegistry-find_stubbed_request}{\code{StubRegistry$find_stubbed_request()}} +\item \href{#method-StubRegistry-request_stub_for}{\code{StubRegistry$request_stub_for()}} +\item \href{#method-StubRegistry-remove_request_stub}{\code{StubRegistry$remove_request_stub()}} +\item \href{#method-StubRegistry-remove_all_request_stubs}{\code{StubRegistry$remove_all_request_stubs()}} +\item \href{#method-StubRegistry-is_registered}{\code{StubRegistry$is_registered()}} +\item \href{#method-StubRegistry-clone}{\code{StubRegistry$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-print}{}}} \subsection{Method \code{print()}}{ print method for the \code{StubRegistry} class \subsection{Usage}{ @@ -75,8 +75,8 @@ print method for the \code{StubRegistry} class } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-register_stub}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-register_stub}{}}} \subsection{Method \code{register_stub()}}{ Register a stub \subsection{Usage}{ @@ -95,8 +95,8 @@ nothing returned; registers the stub } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-find_stubbed_request}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-find_stubbed_request}{}}} \subsection{Method \code{find_stubbed_request()}}{ Find a stubbed request \subsection{Usage}{ @@ -115,8 +115,8 @@ an object of type \link{StubbedRequest}, if matched } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-request_stub_for}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-request_stub_for}{}}} \subsection{Method \code{request_stub_for()}}{ Find a stubbed request \subsection{Usage}{ @@ -135,8 +135,8 @@ logical, 1 or more } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-remove_request_stub}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-remove_request_stub}{}}} \subsection{Method \code{remove_request_stub()}}{ Remove a stubbed request by matching request signature \subsection{Usage}{ @@ -155,8 +155,8 @@ nothing returned; removes the stub from the registry } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-remove_all_request_stubs}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-remove_all_request_stubs}{}}} \subsection{Method \code{remove_all_request_stubs()}}{ Remove all request stubs \subsection{Usage}{ @@ -168,8 +168,8 @@ nothing returned; removes all request stubs } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-is_registered}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-is_registered}{}}} \subsection{Method \code{is_registered()}}{ Find a stubbed request \subsection{Usage}{ @@ -188,8 +188,8 @@ nothing returned; registers the stub } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubRegistry-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/StubbedRequest.Rd b/man/StubbedRequest.Rd index eb924a9..c0f0ad7 100644 --- a/man/StubbedRequest.Rd +++ b/man/StubbedRequest.Rd @@ -116,19 +116,19 @@ x \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{StubbedRequest$new()}} -\item \href{#method-print}{\code{StubbedRequest$print()}} -\item \href{#method-with}{\code{StubbedRequest$with()}} -\item \href{#method-to_return}{\code{StubbedRequest$to_return()}} -\item \href{#method-to_timeout}{\code{StubbedRequest$to_timeout()}} -\item \href{#method-to_raise}{\code{StubbedRequest$to_raise()}} -\item \href{#method-to_s}{\code{StubbedRequest$to_s()}} -\item \href{#method-clone}{\code{StubbedRequest$clone()}} +\item \href{#method-StubbedRequest-new}{\code{StubbedRequest$new()}} +\item \href{#method-StubbedRequest-print}{\code{StubbedRequest$print()}} +\item \href{#method-StubbedRequest-with}{\code{StubbedRequest$with()}} +\item \href{#method-StubbedRequest-to_return}{\code{StubbedRequest$to_return()}} +\item \href{#method-StubbedRequest-to_timeout}{\code{StubbedRequest$to_timeout()}} +\item \href{#method-StubbedRequest-to_raise}{\code{StubbedRequest$to_raise()}} +\item \href{#method-StubbedRequest-to_s}{\code{StubbedRequest$to_s()}} +\item \href{#method-StubbedRequest-clone}{\code{StubbedRequest$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{StubbedRequest} object \subsection{Usage}{ @@ -156,8 +156,8 @@ A new \code{StubbedRequest} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-print}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-print}{}}} \subsection{Method \code{print()}}{ print method for the \code{StubbedRequest} class \subsection{Usage}{ @@ -175,8 +175,8 @@ print method for the \code{StubbedRequest} class } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-with}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-with}{}}} \subsection{Method \code{with()}}{ Set expectations for what's given in HTTP request \subsection{Usage}{ @@ -206,8 +206,8 @@ nothing returned; sets only } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_return}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-to_return}{}}} \subsection{Method \code{to_return()}}{ Set expectations for what's returned in HTTP response \subsection{Usage}{ @@ -232,8 +232,8 @@ nothing returned; sets whats to be returned } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_timeout}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-to_timeout}{}}} \subsection{Method \code{to_timeout()}}{ Response should time out \subsection{Usage}{ @@ -245,8 +245,8 @@ nothing returned } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_raise}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-to_raise}{}}} \subsection{Method \code{to_raise()}}{ Response should raise an exception \code{x} \subsection{Usage}{ @@ -265,8 +265,8 @@ nothing returned } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-to_s}{}}} \subsection{Method \code{to_s()}}{ Response as a character string \subsection{Usage}{ @@ -278,8 +278,8 @@ Response as a character string } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ diff --git a/man/UriPattern.Rd b/man/UriPattern.Rd index 45824e8..da351ef 100644 --- a/man/UriPattern.Rd +++ b/man/UriPattern.Rd @@ -92,19 +92,19 @@ z$matches("https://stuff.com/apple?bears=brown&bats=grey") # TRUE \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-new}{\code{UriPattern$new()}} -\item \href{#method-matches}{\code{UriPattern$matches()}} -\item \href{#method-pattern_matches}{\code{UriPattern$pattern_matches()}} -\item \href{#method-query_params_matches}{\code{UriPattern$query_params_matches()}} -\item \href{#method-extract_query}{\code{UriPattern$extract_query()}} -\item \href{#method-add_query_params}{\code{UriPattern$add_query_params()}} -\item \href{#method-to_s}{\code{UriPattern$to_s()}} -\item \href{#method-clone}{\code{UriPattern$clone()}} +\item \href{#method-UriPattern-new}{\code{UriPattern$new()}} +\item \href{#method-UriPattern-matches}{\code{UriPattern$matches()}} +\item \href{#method-UriPattern-pattern_matches}{\code{UriPattern$pattern_matches()}} +\item \href{#method-UriPattern-query_params_matches}{\code{UriPattern$query_params_matches()}} +\item \href{#method-UriPattern-extract_query}{\code{UriPattern$extract_query()}} +\item \href{#method-UriPattern-add_query_params}{\code{UriPattern$add_query_params()}} +\item \href{#method-UriPattern-to_s}{\code{UriPattern$to_s()}} +\item \href{#method-UriPattern-clone}{\code{UriPattern$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-new}{}}} \subsection{Method \code{new()}}{ Create a new \code{UriPattern} object \subsection{Usage}{ @@ -128,8 +128,8 @@ A new \code{UriPattern} object } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-matches}{}}} \subsection{Method \code{matches()}}{ Match a uri against a pattern \subsection{Usage}{ @@ -148,8 +148,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-pattern_matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-pattern_matches}{}}} \subsection{Method \code{pattern_matches()}}{ Match a URI \subsection{Usage}{ @@ -168,8 +168,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-query_params_matches}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-query_params_matches}{}}} \subsection{Method \code{query_params_matches()}}{ Match query parameters of a URI \subsection{Usage}{ @@ -188,8 +188,8 @@ a boolean } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-extract_query}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-extract_query}{}}} \subsection{Method \code{extract_query()}}{ Extract query parameters as a named list \subsection{Usage}{ @@ -208,8 +208,8 @@ named list, or \code{NULL} if no query parameters } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-add_query_params}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-add_query_params}{}}} \subsection{Method \code{add_query_params()}}{ Add query parameters to the URI \subsection{Usage}{ @@ -228,8 +228,8 @@ nothing returned, updates uri pattern } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-to_s}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-to_s}{}}} \subsection{Method \code{to_s()}}{ Print pattern for easy human consumption \subsection{Usage}{ @@ -241,8 +241,8 @@ a string } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-UriPattern-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ From 68f2850f99ae673cc9bb1b218feb142e9948ff12 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sat, 20 Aug 2022 21:21:27 -0700 Subject: [PATCH 31/66] remove lazydata true in description --- DESCRIPTION | 1 - 1 file changed, 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index bd68c33..84695cf 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,6 @@ URL: https://github.com/ropensci/webmockr (devel) https://books.ropensci.org/http-testing/ (user manual) https://docs.ropensci.org/webmockr/ (documentation) BugReports: https://github.com/ropensci/webmockr/issues -LazyData: true Encoding: UTF-8 Language: en-US Roxygen: list(markdown = TRUE) From 803e9af710a5cee99cc0ab5f1d2aef18890eb32e Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sat, 20 Aug 2022 21:21:33 -0700 Subject: [PATCH 32/66] update gh actions --- .github/workflows/R-CMD-check.yaml | 54 ++++++------------------------ 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 214445c..4638a77 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -12,52 +12,26 @@ jobs: fail-fast: false matrix: config: - - { os: windows-latest, r: 'latest'} - - { os: windows-latest, r: 'devel'} - - { os: macOS-latest, r: 'latest'} - - { os: macOS-latest, r: 'devel'} - - { os: ubuntu-16.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} - - { os: ubuntu-16.04, r: 'latest', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} + - { os: windows-latest, r: 'release'} + - { os: ubuntu-20.04, r: 'release'} + - { os: ubuntu-20.04, r: 'devel'} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - CRAN: ${{ matrix.config.rspm }} steps: - uses: actions/checkout@v2 - - uses: r-lib/actions/setup-r@v1 + - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} - - uses: r-lib/actions/setup-pandoc@v1 + - uses: r-lib/actions/setup-pandoc@v2 - - name: Cache R packages - uses: actions/cache@v2 + - uses: r-lib/actions/setup-r-dependencies@v2 with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-r-${{ matrix.config.r }}-${{ hashFiles('DESCRIPTION') }} - - - name: Install pak - run: | - install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/") - shell: Rscript {0} - - - name: Install system dependencies - if: runner.os == 'Linux' - env: - RHUB_PLATFORM: linux-x86_64-ubuntu-gcc - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(pak::local_system_requirements("ubuntu", "16.04"))') - - - name: Install dependencies - run: | - pak::local_install_dev_deps() - pak::pkg_install("rcmdcheck") - shell: Rscript {0} + extra-packages: any::rcmdcheck + needs: check - name: Session info run: | @@ -66,17 +40,11 @@ jobs: sessioninfo::session_info(pkgs, include_base = TRUE) shell: Rscript {0} - - name: Check - run: Rscript -e "rcmdcheck::rcmdcheck(args = '--no-manual', error_on = 'warning', check_dir = 'check')" - - - name: Upload check results - if: failure() - uses: actions/upload-artifact@v2 + - uses: r-lib/actions/check-r-package@v2 with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results - path: check + upload-snapshots: true - name: Test coverage - if: matrix.config.os == 'macOS-latest' && matrix.config.r == 'latest' + if: matrix.config.os == 'ubuntu-20.04' && matrix.config.r == 'release' run: | Rscript -e 'install.packages("covr")' -e 'covr::codecov(token = "${{secrets.CODECOV_TOKEN}}")' From d599457381f452c129dc48c829a472471547b68b Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sat, 20 Aug 2022 21:30:44 -0700 Subject: [PATCH 33/66] also check windows R-devel --- .github/workflows/R-CMD-check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 4638a77..8cf5397 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -13,6 +13,7 @@ jobs: matrix: config: - { os: windows-latest, r: 'release'} + - { os: windows-latest, r: 'devel'} - { os: ubuntu-20.04, r: 'release'} - { os: ubuntu-20.04, r: 'devel'} From 8be95f2b99b1c2f5dbb475d951cf03f583c8f46c Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 24 Aug 2022 11:27:31 -0700 Subject: [PATCH 34/66] add webmockr_reset to _pkgdown.yml #116 --- _pkgdown.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index 1a67381..b78e9ce 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -14,15 +14,14 @@ reference: - starts_with("enable") - disable - httr_mock - - title: "Stub registry" + - title: "Stub and Request registries" contents: - stub_registry - stub_registry_clear - StubRegistry - - title: "Request registry" - contents: - starts_with("request_registry") - RequestRegistry + - webmockr_reset - title: "Stubbing requests" contents: - StubbedRequest From 7c68b503f6568d689fe80956e9d8f65fadd2322d Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sun, 28 Aug 2022 06:59:34 -0700 Subject: [PATCH 35/66] update news --- NEWS.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS.md b/NEWS.md index af668b7..3b1c416 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,11 @@ +webmockr 0.8.2 +============== + +### BUG FIXES + +* change to `UriPattern` to make sure regex matching is working as intended (#114) thanks @kenahoo + + webmockr 0.8.0 ============== From 3b3806ab1154d8a072292b2b736481460085db0a Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sun, 28 Aug 2022 12:45:22 -0700 Subject: [PATCH 36/66] update license year, bump version, update codemeta.json --- DESCRIPTION | 2 +- LICENSE | 2 +- LICENSE.md | 2 +- codemeta.json | 81 +++++++++++++++++++++------------------------------ 4 files changed, 37 insertions(+), 50 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 84695cf..5e2ba11 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.1.91 +Version: 0.8.2 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/LICENSE b/LICENSE index d99b2f4..f2a8228 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,2 @@ -YEAR: 2020 +YEAR: 2022 COPYRIGHT HOLDER: Scott Chamberlain diff --git a/LICENSE.md b/LICENSE.md index be4c2ca..f7f1cdf 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ # MIT License -Copyright (c) 2020 Scott Chamberlain +Copyright (c) 2022 Scott Chamberlain Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codemeta.json b/codemeta.json index 7798477..9b40994 100644 --- a/codemeta.json +++ b/codemeta.json @@ -1,26 +1,24 @@ { - "@context": [ - "http://purl.org/codemeta/2.0", - "http://schema.org" - ], + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "identifier": "webmockr", - "description": "Stubbing and setting expectations on 'HTTP' requests.\n Includes tools for stubbing 'HTTP' requests, including expected\n request conditions and response conditions. Match on\n 'HTTP' method, query parameters, request body, headers and\n more. Can be used for unit tests or outside of a testing \n context.", + "description": "Stubbing and setting expectations on 'HTTP' requests. Includes tools for stubbing 'HTTP' requests, including expected request conditions and response conditions. Match on 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context.", "name": "webmockr: Stubbing and Setting Expectations on 'HTTP' Requests", + "relatedLink": ["https://books.ropensci.org/http-testing/", "https://docs.ropensci.org/webmockr/"], "codeRepository": "https://github.com/ropensci/webmockr", "issueTracker": "https://github.com/ropensci/webmockr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.8.0", + "version": "0.8.2", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.0.4 Patched (2021-02-17 r80031)", + "runtimePlatform": "R version 4.2.1 (2022-06-23)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", - "name": "Central R Archive Network (CRAN)", + "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, "author": [ @@ -32,6 +30,20 @@ "@id": "https://orcid.org/0000-0003-1444-9135" } ], + "contributor": [ + { + "@type": "Person", + "givenName": "Aaron", + "familyName": "Wolen", + "@id": "https://orcid.org/0000-0003-2542-2202" + } + ], + "funder": [ + { + "@type": "Organization", + "name": "rOpenSci" + } + ], "maintainer": [ { "@type": "Person", @@ -91,8 +103,8 @@ "sameAs": "https://CRAN.R-project.org/package=httr" } ], - "softwareRequirements": [ - { + "softwareRequirements": { + "1": { "@type": "SoftwareApplication", "identifier": "curl", "name": "curl", @@ -104,7 +116,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=curl" }, - { + "2": { "@type": "SoftwareApplication", "identifier": "jsonlite", "name": "jsonlite", @@ -116,7 +128,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=jsonlite" }, - { + "3": { "@type": "SoftwareApplication", "identifier": "magrittr", "name": "magrittr", @@ -129,7 +141,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=magrittr" }, - { + "4": { "@type": "SoftwareApplication", "identifier": "R6", "name": "R6", @@ -142,7 +154,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=R6" }, - { + "5": { "@type": "SoftwareApplication", "identifier": "urltools", "name": "urltools", @@ -155,7 +167,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=urltools" }, - { + "6": { "@type": "SoftwareApplication", "identifier": "fauxpas", "name": "fauxpas", @@ -167,7 +179,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=fauxpas" }, - { + "7": { "@type": "SoftwareApplication", "identifier": "crul", "name": "crul", @@ -180,7 +192,7 @@ }, "sameAs": "https://CRAN.R-project.org/package=crul" }, - { + "8": { "@type": "SoftwareApplication", "identifier": "base64enc", "name": "base64enc", @@ -191,36 +203,11 @@ "url": "https://cran.r-project.org" }, "sameAs": "https://CRAN.R-project.org/package=base64enc" - } - ], + }, + "SystemRequirements": null + }, "applicationCategory": "Web", "isPartOf": "https://ropensci.org", - "keywords": ["http", "https", "API", "web-services", "curl", "mock", "mocking", "fakeweb", "http-mocking", "testing", "testing-tools", "tdd", "rstats", "http-mock", "r", "r-package"], - "contIntegration": "https://codecov.io/gh/ropensci/webmockr", - "developmentStatus": "https://www.repostatus.org/#active", - "releaseNotes": "https://github.com/ropensci/webmockr/blob/master/NEWS.md", - "readme": "https://github.com/ropensci/webmockr/blob/master/README.md", - "fileSize": "342.582KB", - "relatedLink": [ - "https://ropenscilabs.github.io/http-testing-book/", - "https://books.ropensci.org/http-testing", - "https://docs.ropensci.org/webmockr", - "https://books.ropensci.org/http-testing/", - "https://docs.ropensci.org/webmockr/" - ], - "funder": [ - { - "@type": "Organization", - "name": "rOpenSci" - } - ], - "contributor": [ - { - "@type": "Person", - "givenName": "Aaron", - "familyName": "Wolen", - "@id": "https://orcid.org/0000-0003-2542-2202" - } - ], - "copyrightHolder": {} + "keywords": ["http", "https", "API", "web-services", "curl", "mock", "mocking", "fakeweb", "http-mocking", "testing", "testing-tools", "tdd"], + "fileSize": "346.38KB" } From ce0ffedac532a433fa5da41567fc0d37d31bbab1 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sun, 28 Aug 2022 12:50:15 -0700 Subject: [PATCH 37/66] update cran comments --- cran-comments.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cran-comments.md b/cran-comments.md index 9280bf3..a6c2501 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,7 +1,7 @@ ## Test environments -* local macOS install, R 4.0.4 Patched -* ubuntu (on GitHub Actions), R 4.0.4 +* local macOS install, R 4.2.1 +* ubuntu (on GitHub Actions), R 4.2.1 * win-builder (devel and release) ## R CMD check results @@ -10,11 +10,11 @@ ## Reverse dependencies -I have checked the 13 reverse dependencies, and no problems were found. See (). +I have checked the 18 reverse dependencies, and no problems were found. --- -In this version, a function gains a parameter, a fix is made and documentation is improved. +In this version I've fixed problems with man files. Thanks! Scott Chamberlain From 039f8ad2c37f51198c3b27cd88cf65ea32df8476 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sat, 14 Jan 2023 20:59:22 -0800 Subject: [PATCH 38/66] update roxygen note --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5e2ba11..aafcf0d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -36,7 +36,7 @@ Suggests: xml2, vcr, httr -RoxygenNote: 7.2.1 +RoxygenNote: 7.2.3 X-schema.org-applicationCategory: Web X-schema.org-keywords: http, https, API, web-services, curl, mock, mocking, fakeweb, http-mocking, testing, testing-tools, tdd X-schema.org-isPartOf: https://ropensci.org From 5feed7ca34c261aa3d40413ce6e3cb262331f34a Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sun, 15 Jan 2023 07:05:31 -0800 Subject: [PATCH 39/66] gitignore local notes file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 37654da..ef44e0d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ revdep/data.sqlite revdep/library.noindex write_disk_path-changes.R ^docs$ +notes.R From 8a20a6edde5428983c920b21133c95409a2ce9eb Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Sun, 15 Jan 2023 07:07:52 -0800 Subject: [PATCH 40/66] fix private$matching_hashes method within BodyPattern class fix #118 - I was returning FALSE when there was a match (wrong) instead of when there was not (right) --- DESCRIPTION | 2 +- R/RequestPattern.R | 13 ++++--------- man/BodyPattern.Rd | 2 +- tests/testthat/test-HttrAdapter.R | 32 +++++++++++++++++++++++++++---- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index aafcf0d..3ea09a5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.2 +Version: 0.8.2.92 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/RequestPattern.R b/R/RequestPattern.R index 8bff27c..3f502bd 100644 --- a/R/RequestPattern.R +++ b/R/RequestPattern.R @@ -372,7 +372,7 @@ BodyPattern <- R6::R6Class( self$pattern <- pattern }, - #' @description Match a list of headers against that stored + #' @description Match a request body pattern against a pattern #' @param body (list) the body #' @param content_type (character) content type #' @return a boolean @@ -381,8 +381,7 @@ BodyPattern <- R6::R6Class( if (length(self$pattern) == 0) return(TRUE) private$matching_hashes(private$body_as_hash(body, content_type), self$pattern) } else { - private$empty_string(self$pattern) && private$empty_string(body) || - self$pattern == body + (private$empty_string(self$pattern) && private$empty_string(body)) || all(self$pattern == body) } }, @@ -392,12 +391,8 @@ BodyPattern <- R6::R6Class( ), private = list( - empty_headers = function(headers) { - is.null(headers) || length(headers) == 0 - }, - empty_string = function(string) { - is.null(string) || nchar(string) == 0 + is.null(string) || !nzchar(string) }, matching_hashes = function(z, pattern) { @@ -408,7 +403,7 @@ BodyPattern <- R6::R6Class( expected <- pattern[[names(z)[i]]] actual <- z[[i]] if (inherits(actual, "list") && inherits(expected, "list")) { - if (private$matching_hashes(actual, expected)) return(FALSE) + if (!private$matching_hashes(actual, expected)) return(FALSE) } else { if (!identical(as.character(actual), as.character(expected))) return(FALSE) } diff --git a/man/BodyPattern.Rd b/man/BodyPattern.Rd index 407521a..ed1c03c 100644 --- a/man/BodyPattern.Rd +++ b/man/BodyPattern.Rd @@ -88,7 +88,7 @@ A new \code{BodyPattern} object \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-BodyPattern-matches}{}}} \subsection{Method \code{matches()}}{ -Match a list of headers against that stored +Match a request body pattern against a pattern \subsection{Usage}{ \if{html}{\out{
}}\preformatted{BodyPattern$matches(body, content_type = "")}\if{html}{\out{
}} } diff --git a/tests/testthat/test-HttrAdapter.R b/tests/testthat/test-HttrAdapter.R index bbb273a..aafdc6b 100644 --- a/tests/testthat/test-HttrAdapter.R +++ b/tests/testthat/test-HttrAdapter.R @@ -41,20 +41,20 @@ test_that("build_httr_request/response fail well", { test_that("HttrAdapter: works when vcr is loaded but no cassette is inserted", { skip_on_cran() skip_if_not_installed("vcr") - + webmockr::enable(adapter = "httr") on.exit({ webmockr::disable(adapter = "httr") unloadNamespace("vcr") }) - + stub_request("get", "https://httpbin.org/get") library("vcr") - + # works when no cassette is loaded expect_silent(x <- httr::GET("https://httpbin.org/get")) expect_is(x, "response") - + # # works when empty cassette is loaded vcr::vcr_configure(dir = tempdir()) vcr::insert_cassette("empty") @@ -327,6 +327,30 @@ test_that("httr requests with bodies work", { webmockr_disable_net_connect() }) +test_that("httr requests with nested list bodies work", { + skip_on_cran() + + httr_mock() + stub_registry_clear() + body = list(id = ' ', method = 'x', params = list(pwd = 'p', user = 'a')) + z <- stub_request("post", uri = "https://httpbin.org/post") %>% + wi_th(body = body) %>% + to_return(body = "asdffsdsdf") + x <- httr::POST("https://httpbin.org/post", body = body) + expect_true(httr::content(x, "text", encoding="UTF-8") == "asdffsdsdf") + + # now with allow net connect + stub_registry_clear() + webmockr_allow_net_connect() + x <- httr::POST("https://httpbin.org/post", + body = jsonlite::toJSON(body), httr::content_type_json()) + expect_equal( + jsonlite::fromJSON(rawToChar(x$content))$json, + body) + + webmockr_disable_net_connect() +}) + test_that("httr requests with JSON-encoded bodies work", { skip_on_cran() From 9716a05f7d8d700e55258e97518f709bafc9b811 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 19 Jan 2023 10:02:44 -0800 Subject: [PATCH 41/66] gitnore notes file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ef44e0d..3a8b440 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .Rhistory .RData notes.md +notes.R .DS_Store revdep/checks.noindex revdep/data.sqlite From 6317f978fa197ad197c3b03bec084170b1c3bceb Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 19 Jan 2023 10:03:32 -0800 Subject: [PATCH 42/66] rbuildignore notes file --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index cec22ea..a31ab12 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -7,6 +7,7 @@ man-roxygen/ ^CODE_OF_CONDUCT\.md$ Makefile notes.md +notes.R .github cran-comments.md ^codemeta\.json$ From 07a16c11633ef0ae892ff1c0509d53ddbd927f30 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 19 Jan 2023 10:14:26 -0800 Subject: [PATCH 43/66] #115 fix for multiple responses from a stub, bump patch version --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/StubRegistry.R | 16 ++++++-- R/StubbedRequest.R | 55 +++++++++++++++++++++++++++- R/adapter.R | 12 +----- man/StubCounter.Rd | 85 +++++++++++++++++++++++++++++++++++++++++++ man/StubRegistry.Rd | 4 +- man/StubbedRequest.Rd | 16 ++++++++ 8 files changed, 174 insertions(+), 17 deletions(-) create mode 100644 man/StubCounter.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 3ea09a5..6b10248 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.2.92 +Version: 0.8.3.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/NAMESPACE b/NAMESPACE index 4fb12df..4b52e0f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,6 +15,7 @@ export(RequestPattern) export(RequestRegistry) export(RequestSignature) export(Response) +export(StubCounter) export(StubRegistry) export(StubbedRequest) export(UriPattern) diff --git a/R/StubRegistry.R b/R/StubRegistry.R index a0d256f..6ab5163 100644 --- a/R/StubRegistry.R +++ b/R/StubRegistry.R @@ -62,15 +62,22 @@ StubRegistry <- R6::R6Class( #' @description Find a stubbed request #' @param request_signature an object of class [RequestSignature] + #' @param count (bool) iterate counter or not. default: `TRUE` #' @return logical, 1 or more - request_stub_for = function(request_signature) { + request_stub_for = function(request_signature, count = TRUE) { stubs <- c(self$global_stubs, self$request_stubs) - vapply(stubs, function(z) { + mtchs <- vapply(stubs, function(z) { tmp <- RequestPattern$new(method = z$method, uri = z$uri, uri_regex = z$uri_regex, query = z$query, body = z$body, headers = z$request_headers) tmp$matches(request_signature) }, logical(1)) + if (count) { + for (i in seq_along(stubs)) { + if (mtchs[i]) stubs[[i]]$counter$put(request_signature) + } + } + return(mtchs) }, #' @description Remove a stubbed request by matching request signature @@ -93,13 +100,16 @@ StubRegistry <- R6::R6Class( #' @description Remove all request stubs #' @return nothing returned; removes all request stubs remove_all_request_stubs = function() { + for (stub in self$request_stubs) { + if (inherits(stub, "StubbedRequest")) stub$reset() + } self$request_stubs <- list() }, #' @description Find a stubbed request #' @param x an object of class [RequestSignature] #' @return nothing returned; registers the stub - is_registered = function(x) any(self$request_stub_for(x)) + is_registered = function(x) any(self$request_stub_for(x, count = FALSE)) ) ) diff --git a/R/StubbedRequest.R b/R/StubbedRequest.R index 293763e..5f81a6b 100644 --- a/R/StubbedRequest.R +++ b/R/StubbedRequest.R @@ -1,3 +1,45 @@ +#' @title StubCounter +#' @description hash with counter to store requests and count number +#' of requests made against the stub +#' @export +#' @examples +#' x <- StubCounter$new() +#' x +#' x$hash +#' x$count() +#' z <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get") +#' x$put(z) +#' x$count() +#' x$put(z) +#' x$count() +StubCounter <- R6::R6Class( + 'StubCounter', + public = list( + #' @field hash (list) a list for internal use only, with elements + #' `key`, `sig`, and `count` + hash = list(), + + #' @description Register a request by it's key + #' @param x an object of class `RequestSignature` + #' @return nothing returned; registers request & iterates internal counter + put = function(x) { + assert(x, "RequestSignature") + key <- x$to_s() + self$hash[[key]] <- list(key = key, sig = x) + private$total <- private$total + 1 + }, + + #' @description Get the count of number of times any matching request has + #' been made against this stub + count = function() { + private$total + } + ), + private = list( + total = 0 + ) +) + #' @title StubbedRequest #' @description stubbed request class underlying [stub_request()] #' @export @@ -29,7 +71,7 @@ #' headers = list(a = 5)) #' x$to_s() #' x -#' +#' #' # basic auth #' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org") #' x$with(basic_auth = c("foo", "bar")) @@ -102,6 +144,8 @@ StubbedRequest <- R6::R6Class( responses_sequences = NULL, #' @field status_code (xx) xx status_code = NULL, + #' @field counter a StubCounter object + counter = NULL, #' @description Create a new `StubbedRequest` object #' @param method the HTTP method (any, head, get, post, put, @@ -124,6 +168,7 @@ StubbedRequest <- R6::R6Class( self$uri <- uri self$uri_regex <- uri_regex if (!is.null(uri)) self$uri_parts <- parseurl(self$uri) + self$counter <- StubCounter$new() }, #' @description print method for the `StubbedRequest` class @@ -140,7 +185,7 @@ StubbedRequest <- R6::R6Class( else cat(sprintf(" body (class: %s): %s", class(self$body)[1L], hdl_lst(self$body)), sep = "\n") - cat(paste0(" request_headers: ", + cat(paste0(" request_headers: ", hdl_lst(self$request_headers)), sep = "\n") cat(" to_return: ", sep = "\n") @@ -292,6 +337,12 @@ StubbedRequest <- R6::R6Class( "" } )) + }, + + #' @description Reset the counter for the stub + #' @return nothing returned; resets stub counter to no requests + reset = function() { + self$counter <- StubCounter$new() } ), diff --git a/R/adapter.R b/R/adapter.R index b994287..9bc4ae3 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -267,11 +267,7 @@ Adapter <- R6::R6Class("Adapter", resp$set_response_headers(stub$response_headers) resp$set_status(as.integer(stub$status_code %||% 200)) - req_pat <- RequestPattern$new(method = stub$method, - uri = stub$uri, uri_regex = stub$uri_regex, query = stub$query, - body = stub$body, headers = stub$request_headers) - times_req <- webmockr_request_registry$times_executed(req_pat) - 1 - stub_num_get <- times_req + 1 + stub_num_get <- stub$counter$count() if (stub_num_get > length(stub$responses_sequences)) { stub_num_get <- length(stub$responses_sequences) } @@ -303,11 +299,7 @@ Adapter <- R6::R6Class("Adapter", # to decide which responses_sequence entry to use # choose which response to return - req_pat <- RequestPattern$new(method = stub$method, - uri = stub$uri, uri_regex = stub$uri_regex, query = stub$query, - body = stub$body, headers = stub$request_headers) - times_req <- webmockr_request_registry$times_executed(req_pat) - 1 - stub_num_get <- times_req + 1 + stub_num_get <- stub$counter$count() if (stub_num_get > length(stub$responses_sequences)) { stub_num_get <- length(stub$responses_sequences) } diff --git a/man/StubCounter.Rd b/man/StubCounter.Rd new file mode 100644 index 0000000..72239a8 --- /dev/null +++ b/man/StubCounter.Rd @@ -0,0 +1,85 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/StubbedRequest.R +\name{StubCounter} +\alias{StubCounter} +\title{StubCounter} +\description{ +hash with counter to store requests and count number +of requests made against the stub +} +\examples{ +x <- StubCounter$new() +x +x$hash +x$count() +z <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get") +x$put(z) +x$count() +x$put(z) +x$count() +} +\section{Public fields}{ +\if{html}{\out{
}} +\describe{ +\item{\code{hash}}{(list) a list for internal use only, with elements +\code{key}, \code{sig}, and \code{count}} +} +\if{html}{\out{
}} +} +\section{Methods}{ +\subsection{Public methods}{ +\itemize{ +\item \href{#method-StubCounter-put}{\code{StubCounter$put()}} +\item \href{#method-StubCounter-count}{\code{StubCounter$count()}} +\item \href{#method-StubCounter-clone}{\code{StubCounter$clone()}} +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubCounter-put}{}}} +\subsection{Method \code{put()}}{ +Register a request by it's key +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{StubCounter$put(x)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{x}}{an object of class \code{RequestSignature}} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +nothing returned; registers request & iterates internal counter +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubCounter-count}{}}} +\subsection{Method \code{count()}}{ +Get the count of number of times any matching request has +been made against this stub +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{StubCounter$count()}\if{html}{\out{
}} +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubCounter-clone}{}}} +\subsection{Method \code{clone()}}{ +The objects of this class are cloneable with this method. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{StubCounter$clone(deep = FALSE)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{deep}}{Whether to make a deep clone.} +} +\if{html}{\out{
}} +} +} +} diff --git a/man/StubRegistry.Rd b/man/StubRegistry.Rd index cbb146d..d6b1681 100644 --- a/man/StubRegistry.Rd +++ b/man/StubRegistry.Rd @@ -120,13 +120,15 @@ an object of type \link{StubbedRequest}, if matched \subsection{Method \code{request_stub_for()}}{ Find a stubbed request \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{StubRegistry$request_stub_for(request_signature)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{StubRegistry$request_stub_for(request_signature, count = TRUE)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{request_signature}}{an object of class \link{RequestSignature}} + +\item{\code{count}}{(bool) iterate counter or not. default: \code{TRUE}} } \if{html}{\out{
}} } diff --git a/man/StubbedRequest.Rd b/man/StubbedRequest.Rd index c0f0ad7..568e277 100644 --- a/man/StubbedRequest.Rd +++ b/man/StubbedRequest.Rd @@ -110,6 +110,8 @@ x \item{\code{responses_sequences}}{(xx) xx} \item{\code{status_code}}{(xx) xx} + +\item{\code{counter}}{a StubCounter object} } \if{html}{\out{}} } @@ -123,6 +125,7 @@ x \item \href{#method-StubbedRequest-to_timeout}{\code{StubbedRequest$to_timeout()}} \item \href{#method-StubbedRequest-to_raise}{\code{StubbedRequest$to_raise()}} \item \href{#method-StubbedRequest-to_s}{\code{StubbedRequest$to_s()}} +\item \href{#method-StubbedRequest-reset}{\code{StubbedRequest$reset()}} \item \href{#method-StubbedRequest-clone}{\code{StubbedRequest$clone()}} } } @@ -278,6 +281,19 @@ Response as a character string } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-StubbedRequest-reset}{}}} +\subsection{Method \code{reset()}}{ +Reset the counter for the stub +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{StubbedRequest$reset()}\if{html}{\out{
}} +} + +\subsection{Returns}{ +nothing returned; resets stub counter to no requests +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-StubbedRequest-clone}{}}} \subsection{Method \code{clone()}}{ From 1d17416fb63591a141d1ccabb1cbf21cfd305c12 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Thu, 19 Jan 2023 10:31:13 -0800 Subject: [PATCH 44/66] update cran checks badge --- README.Rmd | 2 +- README.md | 57 +++++++++++++++++++++++++++++++++++++--------------- details.html | 49 +++++++++++++++++++++++++++++++++----------- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/README.Rmd b/README.Rmd index 55a6d93..f1ef4bd 100644 --- a/README.Rmd +++ b/README.Rmd @@ -11,7 +11,7 @@ knitr::opts_chunk$set( -[![cran checks](https://cranchecks.info/badges/worst/webmockr)](https://cranchecks.info/pkgs/webmockr) +[![cran checks](https://badges.cranchecks.info/worst/webmockr.svg)](https://cloud.r-project.org/web/checks/check_results_webmockr.html) [![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![R-CMD-check](https://github.com/ropensci/webmockr/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/webmockr/actions/) [![codecov](https://codecov.io/gh/ropensci/webmockr/branch/master/graph/badge.svg)](https://codecov.io/gh/ropensci/webmockr) diff --git a/README.md b/README.md index 8829899..28e3d85 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ webmockr -[![cran checks](https://cranchecks.info/badges/worst/webmockr)](https://cranchecks.info/pkgs/webmockr) +[![cran checks](https://badges.cranchecks.info/worst/webmockr.svg)](https://cloud.r-project.org/web/checks/check_results_webmockr.html) [![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![R-CMD-check](https://github.com/ropensci/webmockr/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/webmockr/actions/) [![codecov](https://codecov.io/gh/ropensci/webmockr/branch/master/graph/badge.svg)](https://codecov.io/gh/ropensci/webmockr) @@ -23,10 +23,18 @@ Port of the Ruby gem [webmock](https://github.com/bblimke/webmock) -

The very very short version is: webmockr helps you stub HTTP requests so you don’t have to repeat yourself.

+

The very very short version is: webmockr helps you stub +HTTP requests so you don’t have to repeat yourself.

More details

-

You tell webmockr what HTTP request you want to match against and if it sees a request matching your criteria it doesn’t actually do the HTTP request. Instead, it gives back the same object you would have gotten back with a real request, but only with the bits it knows about. For example, we can’t give back the actual data you’d get from a real HTTP request as the request wasn’t performed.

-

In addition, if you set an expectation of what webmockr should return, we return that. For example, if you expect a request to return a 418 error (I’m a Teapot), then that’s what you’ll get.

+

You tell webmockr what HTTP request you want to match +against and if it sees a request matching your criteria it doesn’t +actually do the HTTP request. Instead, it gives back the same object you +would have gotten back with a real request, but only with the bits it +knows about. For example, we can’t give back the actual data you’d get +from a real HTTP request as the request wasn’t performed.

+

In addition, if you set an expectation of what webmockr +should return, we return that. For example, if you expect a request to +return a 418 error (I’m a Teapot), then that’s what you’ll get.

What you can match against

  • HTTP method (required)
  • @@ -35,16 +43,22 @@ Port of the Ruby gem [webmock](https://github.com/bblimke/webmock)
    • URI
        -
      • Right now, we can match directly against URI’s, and with regex URI patterns. Eventually, we will support RFC 6570 URI templates.
      • -
      • We normalize URI paths so that URL encoded things match URL un-encoded things (e.g. hello world to hello%20world)
      • +
      • Right now, we can match directly against URI’s, and with regex URI +patterns. Eventually, we will support RFC 6570 URI templates.
      • +
      • We normalize URI paths so that URL encoded things match URL +un-encoded things (e.g. hello world to +hello%20world)
    • Query parameters
        -
      • We normalize query parameter values so that URL encoded things match URL un-encoded things (e.g. message = hello world to message = hello%20world)
      • +
      • We normalize query parameter values so that URL encoded things match +URL un-encoded things (e.g. message = hello world to +message = hello%20world)
    • Request headers
        -
      • We normalize headers and treat all forms of same headers as equal. For example, the following two sets of headers are equal: +
      • We normalize headers and treat all forms of same headers as equal. +For example, the following two sets of headers are equal:
        • list(H1 = "value1", content_length = 123, X_CuStOm_hEAder = "foo")
        • list(h1 = "value1", "Content-Length" = 123, "x-cuSTOM-HeAder" = "foo")
        • @@ -53,15 +67,26 @@ Port of the Ruby gem [webmock](https://github.com/bblimke/webmock)
        • Request body

        Real HTTP requests

        -

        There’s a few scenarios to think about when using webmockr:

        +

        There’s a few scenarios to think about when using +webmockr:

        After doing

        library(webmockr)
        -

        webmockr is loaded but not turned on. At this point webmockr doesn’t change anythning.

        +

        webmockr is loaded but not turned on. At this point +webmockr doesn’t change anythning.

        Once you turn on webmockr like

        webmockr::enable()
        -

        webmockr will now by default not allow real HTTP requests from the http libraries that adapters are loaded for (right now only crul).

        -

        You can optionally allow real requests via webmockr_allow_net_connect(), and disallow real requests via webmockr_disable_net_connect(). You can check whether you are allowing real requests with webmockr_net_connect_allowed().

        -

        Certain kinds of real HTTP requests allowed: We don’t suppoprt this yet, but you can allow localhost HTTP requests with the allow_localhost parameter in the webmockr_configure() function.

        +

        webmockr will now by default not allow real HTTP +requests from the http libraries that adapters are loaded for (right now +only crul).

        +

        You can optionally allow real requests via +webmockr_allow_net_connect(), and disallow real requests +via webmockr_disable_net_connect(). You can check whether +you are allowing real requests with +webmockr_net_connect_allowed().

        +

        Certain kinds of real HTTP requests allowed: We don’t suppoprt this +yet, but you can allow localhost HTTP requests with the +allow_localhost parameter in the +webmockr_configure() function.

        Storing actual HTTP responses

        webmockr doesn’t do that. Check out vcr

        @@ -181,7 +206,7 @@ x$get('get') #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.64.1 r-curl/4.3 crul/1.1.0 +#> User-Agent: libcurl/7.79.1 r-curl/5.0.0 crul/1.3 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -217,7 +242,7 @@ x$get('get', query = list(hello = "world")) #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.64.1 r-curl/4.3 crul/1.1.0 +#> User-Agent: libcurl/7.79.1 r-curl/5.0.0 crul/1.3 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -259,7 +284,7 @@ x$get('get', query = list(hello = "world")) #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.64.1 r-curl/4.3 crul/1.1.0 +#> User-Agent: libcurl/7.79.1 r-curl/5.0.0 crul/1.3 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: diff --git a/details.html b/details.html index 1823d62..318a8fe 100644 --- a/details.html +++ b/details.html @@ -1,10 +1,18 @@ -

        The very very short version is: webmockr helps you stub HTTP requests so you don’t have to repeat yourself.

        +

        The very very short version is: webmockr helps you stub +HTTP requests so you don’t have to repeat yourself.

        More details

        -

        You tell webmockr what HTTP request you want to match against and if it sees a request matching your criteria it doesn’t actually do the HTTP request. Instead, it gives back the same object you would have gotten back with a real request, but only with the bits it knows about. For example, we can’t give back the actual data you’d get from a real HTTP request as the request wasn’t performed.

        -

        In addition, if you set an expectation of what webmockr should return, we return that. For example, if you expect a request to return a 418 error (I’m a Teapot), then that’s what you’ll get.

        +

        You tell webmockr what HTTP request you want to match +against and if it sees a request matching your criteria it doesn’t +actually do the HTTP request. Instead, it gives back the same object you +would have gotten back with a real request, but only with the bits it +knows about. For example, we can’t give back the actual data you’d get +from a real HTTP request as the request wasn’t performed.

        +

        In addition, if you set an expectation of what webmockr +should return, we return that. For example, if you expect a request to +return a 418 error (I’m a Teapot), then that’s what you’ll get.

        What you can match against

        • HTTP method (required)
        • @@ -13,16 +21,22 @@
          • URI
              -
            • Right now, we can match directly against URI’s, and with regex URI patterns. Eventually, we will support RFC 6570 URI templates.
            • -
            • We normalize URI paths so that URL encoded things match URL un-encoded things (e.g. hello world to hello%20world)
            • +
            • Right now, we can match directly against URI’s, and with regex URI +patterns. Eventually, we will support RFC 6570 URI templates.
            • +
            • We normalize URI paths so that URL encoded things match URL +un-encoded things (e.g. hello world to +hello%20world)
          • Query parameters
              -
            • We normalize query parameter values so that URL encoded things match URL un-encoded things (e.g. message = hello world to message = hello%20world)
            • +
            • We normalize query parameter values so that URL encoded things match +URL un-encoded things (e.g. message = hello world to +message = hello%20world)
          • Request headers
              -
            • We normalize headers and treat all forms of same headers as equal. For example, the following two sets of headers are equal: +
            • We normalize headers and treat all forms of same headers as equal. +For example, the following two sets of headers are equal:
              • list(H1 = "value1", content_length = 123, X_CuStOm_hEAder = "foo")
              • list(h1 = "value1", "Content-Length" = 123, "x-cuSTOM-HeAder" = "foo")
              • @@ -31,14 +45,25 @@
              • Request body

              Real HTTP requests

              -

              There’s a few scenarios to think about when using webmockr:

              +

              There’s a few scenarios to think about when using +webmockr:

              After doing

              library(webmockr)
              -

              webmockr is loaded but not turned on. At this point webmockr doesn’t change anythning.

              +

              webmockr is loaded but not turned on. At this point +webmockr doesn’t change anythning.

              Once you turn on webmockr like

              webmockr::enable()
              -

              webmockr will now by default not allow real HTTP requests from the http libraries that adapters are loaded for (right now only crul).

              -

              You can optionally allow real requests via webmockr_allow_net_connect(), and disallow real requests via webmockr_disable_net_connect(). You can check whether you are allowing real requests with webmockr_net_connect_allowed().

              -

              Certain kinds of real HTTP requests allowed: We don’t suppoprt this yet, but you can allow localhost HTTP requests with the allow_localhost parameter in the webmockr_configure() function.

              +

              webmockr will now by default not allow real HTTP +requests from the http libraries that adapters are loaded for (right now +only crul).

              +

              You can optionally allow real requests via +webmockr_allow_net_connect(), and disallow real requests +via webmockr_disable_net_connect(). You can check whether +you are allowing real requests with +webmockr_net_connect_allowed().

              +

              Certain kinds of real HTTP requests allowed: We don’t suppoprt this +yet, but you can allow localhost HTTP requests with the +allow_localhost parameter in the +webmockr_configure() function.

              Storing actual HTTP responses

              webmockr doesn’t do that. Check out vcr

              From af4c22d433d95063d8770146cc054b89f9ac579c Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 24 Jan 2023 13:32:55 -0800 Subject: [PATCH 45/66] fix #117 make to_return actually accept integer or numeric for status value, thanks @maelle ! --- DESCRIPTION | 2 +- R/to_return.R | 2 +- tests/testthat/test-to_return.R | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6b10248..8ff8d7d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.3.91 +Version: 0.8.4.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/to_return.R b/R/to_return.R index 77f1200..637a64e 100644 --- a/R/to_return.R +++ b/R/to_return.R @@ -85,7 +85,7 @@ to_return <- function(.data, ..., .list = list(), times = 1) { ) { stop("'to_return' only accepts status, body, headers") } - assert(z$status, "numeric") + assert(z$status, c("numeric", "integer")) assert(z$headers, "list") if (!all(hz_namez(z$headers))) stop("'headers' must be a named list") replicate(times, diff --git a/tests/testthat/test-to_return.R b/tests/testthat/test-to_return.R index 6226540..68b041b 100644 --- a/tests/testthat/test-to_return.R +++ b/tests/testthat/test-to_return.R @@ -154,3 +154,21 @@ context("to_return_: defunct") test_that("to_return_: defunct", { expect_error(to_return_(), "to_return", class = "error") }) + + +stub_to_return_status_code <- function() { + stub_registry()$request_stubs[[1]]$responses_sequences[[1]]$status +} +stub_registry_clear() +enable() +test_that("stub_request status accepts numeric or integer values", { + stub_status_type_a <- stub_request("get", "https://httpbin.org/get") + expect_s3_class(to_return(stub_status_type_a, status = 200), "StubbedRequest") + expect_type(stub_to_return_status_code(), "double") # numeric = double + + stub_registry_clear() + stub_status_type_b <- stub_request("get", "https://httpbin.org/get") + expect_s3_class(to_return(stub_status_type_b, status = 200L), "StubbedRequest") + expect_type(stub_to_return_status_code(), "integer") +}) +disable() From 20f58a1c2e4fa720de8200e2b950d088e6ed36a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlle=20Salmon?= Date: Tue, 7 Feb 2023 12:36:49 +0100 Subject: [PATCH 46/66] add StubCounter to pkgdown config --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index b78e9ce..5d82cce 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -27,6 +27,7 @@ reference: - StubbedRequest - stub_request - remove_request_stub + - StubCounter - to_raise - to_return - to_timeout From 1f122dfc0a8dfd8f0cd1514ebfa7eed54749b646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlle=20Salmon?= Date: Tue, 7 Feb 2023 15:52:00 +0100 Subject: [PATCH 47/66] Update _pkgdown.yml --- _pkgdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index 5d82cce..1a661a1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -27,7 +27,6 @@ reference: - StubbedRequest - stub_request - remove_request_stub - - StubCounter - to_raise - to_return - to_timeout @@ -52,3 +51,4 @@ reference: - HashCounter - pluck_body - Response + - StubCounter From 2d87886973bbf474676e357e542de17affcac62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlle=20Salmon?= Date: Tue, 7 Feb 2023 15:52:24 +0100 Subject: [PATCH 48/66] Update _pkgdown.yml --- _pkgdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index 1a661a1..2f679b2 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -49,6 +49,6 @@ reference: - RequestPattern - RequestSignature - HashCounter + - StubCounter - pluck_body - Response - - StubCounter From 2298c8e21aec95df952b262460efc4a8708d8973 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 7 Feb 2023 21:02:35 -0800 Subject: [PATCH 49/66] #27 remove 3 config options that are not used and were not implemented --- R/webmockr-opts.R | 20 ++------------------ man/webmockr_configure.Rd | 14 +------------- tests/testthat/test-zutils.R | 3 +-- 3 files changed, 4 insertions(+), 33 deletions(-) diff --git a/R/webmockr-opts.R b/R/webmockr-opts.R index 9b8fe70..b7ad889 100644 --- a/R/webmockr-opts.R +++ b/R/webmockr-opts.R @@ -5,14 +5,8 @@ #' @param allow_localhost (logical) Default: `FALSE` #' @param allow (character) one or more URI/URL to allow (and by extension #' all others are not allowed) -#' @param net_http_connect_on_start (logical) Default: `FALSE`. ignored for -#' now #' @param show_stubbing_instructions (logical) Default: `FALSE`. ignored for #' now -#' @param query_values_notation (logical) Default: `FALSE`. ignored for -#' now -#' @param show_body_diff (logical) Default: `FALSE`. ignored for -#' now #' @param uri (character) a URI/URL as a character string - to determine #' whether or not it is allowed #' @@ -46,19 +40,13 @@ webmockr_configure <- function( allow_net_connect = FALSE, allow_localhost = FALSE, allow = NULL, - net_http_connect_on_start = FALSE, - show_stubbing_instructions = FALSE, - query_values_notation = FALSE, - show_body_diff = FALSE) { + show_stubbing_instructions = FALSE) { opts <- list( allow_net_connect = allow_net_connect, allow_localhost = allow_localhost, allow = allow, - net_http_connect_on_start = net_http_connect_on_start, - show_stubbing_instructions = show_stubbing_instructions, - query_values_notation = query_values_notation, - show_body_diff = show_body_diff + show_stubbing_instructions = show_stubbing_instructions ) for (i in seq_along(opts)) { assign(names(opts)[i], opts[[i]], envir = webmockr_conf_env) @@ -135,12 +123,8 @@ print.webmockr_config <- function(x, ...) { cat(paste0(" allow_net_connect?: ", x$allow_net_connect), sep = "\n") cat(paste0(" allow_localhost?: ", x$allow_localhost), sep = "\n") cat(paste0(" allow: ", x$allow %||% ""), sep = "\n") - cat(paste0(" net_http_connect_on_start: ", x$net_http_connect_on_start), - sep = "\n") cat(paste0(" show_stubbing_instructions: ", x$show_stubbing_instructions), sep = "\n") - cat(paste0(" query_values_notation: ", x$query_values_notation), sep = "\n") - cat(paste0(" show_body_diff: ", x$show_body_diff), sep = "\n") } webmockr_conf_env <- new.env() diff --git a/man/webmockr_configure.Rd b/man/webmockr_configure.Rd index 6ec0db1..59b70d0 100644 --- a/man/webmockr_configure.Rd +++ b/man/webmockr_configure.Rd @@ -13,10 +13,7 @@ webmockr_configure( allow_net_connect = FALSE, allow_localhost = FALSE, allow = NULL, - net_http_connect_on_start = FALSE, - show_stubbing_instructions = FALSE, - query_values_notation = FALSE, - show_body_diff = FALSE + show_stubbing_instructions = FALSE ) webmockr_configure_reset() @@ -37,18 +34,9 @@ webmockr_net_connect_allowed(uri = NULL) \item{allow}{(character) one or more URI/URL to allow (and by extension all others are not allowed)} -\item{net_http_connect_on_start}{(logical) Default: \code{FALSE}. ignored for -now} - \item{show_stubbing_instructions}{(logical) Default: \code{FALSE}. ignored for now} -\item{query_values_notation}{(logical) Default: \code{FALSE}. ignored for -now} - -\item{show_body_diff}{(logical) Default: \code{FALSE}. ignored for -now} - \item{uri}{(character) a URI/URL as a character string - to determine whether or not it is allowed} } diff --git a/tests/testthat/test-zutils.R b/tests/testthat/test-zutils.R index fbea1e1..f33153b 100644 --- a/tests/testthat/test-zutils.R +++ b/tests/testthat/test-zutils.R @@ -117,8 +117,7 @@ test_that("webmockr_configuration", { expect_is(webmockr_configuration(), "webmockr_config") expect_named( webmockr_configuration(), - c('show_stubbing_instructions', 'show_body_diff', 'query_values_notation', - 'allow', 'net_http_connect_on_start', 'allow_net_connect', + c('show_stubbing_instructions', 'allow', 'allow_net_connect', 'allow_localhost') ) From 0454a1e7ff3a17313118cb42285685e59e83b495 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 8 Feb 2023 06:30:59 -0800 Subject: [PATCH 50/66] add support for show_stubbing_instructions config option in webmockr_configure --- DESCRIPTION | 2 +- R/adapter.R | 5 ++++- R/webmockr-opts.R | 6 +++--- man/webmockr_configure.Rd | 6 +++--- tests/testthat/test-zutils.R | 18 ++++++++++++++++++ 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8ff8d7d..e69f70c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.4.91 +Version: 0.8.5.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/R/adapter.R b/R/adapter.R index 9bc4ae3..6871210 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -194,7 +194,10 @@ Adapter <- R6::R6Class("Adapter", y <- "\n\nYou can stub this request with the following snippet:\n\n " z <- "\n\nregistered request stubs:\n\n" msgx <- paste(x, request_signature$to_s()) - msgy <- paste(y, private$make_stub_request_code(request_signature)) + msgy <- "" + if (webmockr_conf_env$show_stubbing_instructions) { + msgy <- paste(y, private$make_stub_request_code(request_signature)) + } if (length(webmockr_stub_registry$request_stubs)) { msgz <- paste( z, diff --git a/R/webmockr-opts.R b/R/webmockr-opts.R index b7ad889..6c746b5 100644 --- a/R/webmockr-opts.R +++ b/R/webmockr-opts.R @@ -5,8 +5,8 @@ #' @param allow_localhost (logical) Default: `FALSE` #' @param allow (character) one or more URI/URL to allow (and by extension #' all others are not allowed) -#' @param show_stubbing_instructions (logical) Default: `FALSE`. ignored for -#' now +#' @param show_stubbing_instructions (logical) Default: `TRUE`. If `FALSE`, +#' stubbing instructions are not shown #' @param uri (character) a URI/URL as a character string - to determine #' whether or not it is allowed #' @@ -40,7 +40,7 @@ webmockr_configure <- function( allow_net_connect = FALSE, allow_localhost = FALSE, allow = NULL, - show_stubbing_instructions = FALSE) { + show_stubbing_instructions = TRUE) { opts <- list( allow_net_connect = allow_net_connect, diff --git a/man/webmockr_configure.Rd b/man/webmockr_configure.Rd index 59b70d0..621bdce 100644 --- a/man/webmockr_configure.Rd +++ b/man/webmockr_configure.Rd @@ -13,7 +13,7 @@ webmockr_configure( allow_net_connect = FALSE, allow_localhost = FALSE, allow = NULL, - show_stubbing_instructions = FALSE + show_stubbing_instructions = TRUE ) webmockr_configure_reset() @@ -34,8 +34,8 @@ webmockr_net_connect_allowed(uri = NULL) \item{allow}{(character) one or more URI/URL to allow (and by extension all others are not allowed)} -\item{show_stubbing_instructions}{(logical) Default: \code{FALSE}. ignored for -now} +\item{show_stubbing_instructions}{(logical) Default: \code{TRUE}. If \code{FALSE}, +stubbing instructions are not shown} \item{uri}{(character) a URI/URL as a character string - to determine whether or not it is allowed} diff --git a/tests/testthat/test-zutils.R b/tests/testthat/test-zutils.R index f33153b..9cd468d 100644 --- a/tests/testthat/test-zutils.R +++ b/tests/testthat/test-zutils.R @@ -112,6 +112,24 @@ test_that("webmockr_allow_net_connect", { expect_error(webmockr_allow_net_connect(5), "unused argument") }) +context("config options: show_stubbing_instructions") +test_that("show_stubbing_instructions", { + x = crul::HttpClient$new("https://httpbin.org/get") + + # DO show stubbing instructions + webmockr_configure(show_stubbing_instructions = TRUE) + err_mssg <- as.character(tryCatch(x$get(), error = function(e) e)) + expect_true(grepl("snippet", err_mssg, perl = TRUE)) + + # DO NOT show stubbing instructions + webmockr_configure(show_stubbing_instructions = FALSE) + err_mssg <- as.character(tryCatch(x$get(), error = function(e) e)) + expect_false(grepl("^((?!snippet).)*$", err_mssg, perl = TRUE)) + + # reset to default + webmockr_configure(show_stubbing_instructions = TRUE) +}) + context("util fxns: webmockr_configuration") test_that("webmockr_configuration", { expect_is(webmockr_configuration(), "webmockr_config") From 9608a5059388fbb42154cb7ce52157bee95bc6a3 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 8 Feb 2023 06:44:06 -0800 Subject: [PATCH 51/66] actions/checkout@v2 -> actions/checkout@v3 --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 8cf5397..671c859 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -21,7 +21,7 @@ jobs: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 with: From e5b618cbf7a123eec2c22a10272ae76851de53c4 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 8 Feb 2023 07:31:44 -0800 Subject: [PATCH 52/66] update revdep results --- revdep/README.md | 45 ++++++++---- revdep/cran.md | 7 ++ revdep/failures.md | 166 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 202 insertions(+), 16 deletions(-) create mode 100644 revdep/cran.md diff --git a/revdep/README.md b/revdep/README.md index b69e5b8..6df90ee 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -1,23 +1,38 @@ # Platform -|field |value | -|:--------|:-------------------------------------------| -|version |R version 4.0.4 Patched (2021-02-17 r80031) | -|os |macOS Big Sur 10.16 | -|system |x86_64, darwin17.0 | -|ui |X11 | -|language |(EN) | -|collate |en_US.UTF-8 | -|ctype |en_US.UTF-8 | -|tz |US/Pacific | -|date |2021-03-12 | +|field |value | +|:--------|:--------------------------------| +|version |R version 4.2.2 (2022-10-31) | +|os |macOS Monterey 12.6.3 | +|system |aarch64, darwin21.6.0 | +|ui |unknown | +|language |(EN) | +|collate |en_US.UTF-8 | +|ctype |en_US.UTF-8 | +|tz |America/Los_Angeles | +|date |2023-02-08 | +|pandoc |3.0.1 @ /opt/homebrew/bin/pandoc | # Dependencies -|package |old |new |Δ | -|:--------|:-----|:--------|:--| -|webmockr |0.7.4 |0.7.5.95 |* | -|crul |NA |1.1.0 |* | +|package |old |new |Δ | +|:--------|:-----|:------|:--| +|webmockr |0.8.2 |0.9.0 |* | +|crul |NA |1.3 |* | +|curl |NA |5.0.0 |* | +|jsonlite |NA |1.8.4 |* | +|Rcpp |NA |1.0.10 |* | +|whisker |NA |0.4.1 |* | # Revdeps +## Failed to check (5) + +|package |version |error |warning |note | +|:----------|:-------|:-----|:-------|:----| +|biomaRt |? | | | | +|magmaR |? | | | | +|riskmetric |? | | | | +|rnoaa |? | | | | +|RTD |? | | | | + diff --git a/revdep/cran.md b/revdep/cran.md new file mode 100644 index 0000000..e77d64f --- /dev/null +++ b/revdep/cran.md @@ -0,0 +1,7 @@ +## revdepcheck results + +We checked 18 reverse dependencies (13 from CRAN + 5 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. + + * We saw 0 new problems + * We failed to check 0 packages + diff --git a/revdep/failures.md b/revdep/failures.md index 9a20736..4d780e9 100644 --- a/revdep/failures.md +++ b/revdep/failures.md @@ -1 +1,165 @@ -*Wow, no problems at all. :)* \ No newline at end of file +# biomaRt + +
              + +* Version: +* GitHub: https://github.com/ropensci/webmockr +* Source code: NA +* Number of recursive dependencies: 0 + +
              + +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# magmaR + +
              + +* Version: +* GitHub: https://github.com/ropensci/webmockr +* Source code: NA +* Number of recursive dependencies: 0 + +
              + +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# riskmetric + +
              + +* Version: +* GitHub: https://github.com/ropensci/webmockr +* Source code: NA +* Number of recursive dependencies: 0 + +
              + +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# rnoaa + +
              + +* Version: +* GitHub: https://github.com/ropensci/webmockr +* Source code: NA +* Number of recursive dependencies: 0 + +
              + +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` +# RTD + +
              + +* Version: +* GitHub: https://github.com/ropensci/webmockr +* Source code: NA +* Number of recursive dependencies: 0 + +
              + +## Error before installation + +### Devel + +``` + + + + + + +``` +### CRAN + +``` + + + + + + +``` From 5299cbbed8911701f7efea50a29228a4be40cbc4 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Mon, 27 Feb 2023 20:33:42 -0800 Subject: [PATCH 53/66] update news, cran comments, bump version --- DESCRIPTION | 2 +- NEWS.md | 17 +++++++++++++++++ codemeta.json | 6 +++--- cran-comments.md | 14 +++++++++----- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index e69f70c..0485e8e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.8.5.91 +Version: 0.9.0 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), diff --git a/NEWS.md b/NEWS.md index 3b1c416..8cf04a9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,20 @@ +webmockr 0.9.0 +============== + +### BUG FIXES + +* `to_return()` supports returning multiple responses to match many requests to the same matching stub. however, the internals were broken for this, but is now fixed (#115) thanks @kenahoo for the report +* matching stubs with specifying a request body to match on (e.g., `stub_request('post', 'https://httpbin.org/post') %>% wi_th(body = list(a=5))`) was not working in some cases; internal matching logic was borked. now fixed. (#118) thanks @konradoberwimmer for the report +* The `status` parameter in `to_return()` was documented to accept an integer, but it errored when an integer was passed (e.g., `to_return(status=200L)`). This bug is now fixed (#117) thanks @maelle for the report + +### MINOR IMPROVEMENTS + +* Config options changes (see `webmockr_configure()`). Three options that were presentg but not implemented are now removed: `show_body_diff`, ` query_values_notation`, ` net_http_connect_on_start`. One option that was present but not implemented yet is now implemented: ` show_stubbing_instructions` (#27) (#120) + +### DOCUMENTATION + +* `StubCounter` added to pkgdown docs page at (#119) @maelle + webmockr 0.8.2 ============== diff --git a/codemeta.json b/codemeta.json index 9b40994..f4ebc58 100644 --- a/codemeta.json +++ b/codemeta.json @@ -8,13 +8,13 @@ "codeRepository": "https://github.com/ropensci/webmockr", "issueTracker": "https://github.com/ropensci/webmockr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.8.2", + "version": "0.9.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.2.1 (2022-06-23)", + "runtimePlatform": "R version 4.2.2 (2022-10-31)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -209,5 +209,5 @@ "applicationCategory": "Web", "isPartOf": "https://ropensci.org", "keywords": ["http", "https", "API", "web-services", "curl", "mock", "mocking", "fakeweb", "http-mocking", "testing", "testing-tools", "tdd"], - "fileSize": "346.38KB" + "fileSize": "352.901KB" } diff --git a/cran-comments.md b/cran-comments.md index a6c2501..8647970 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,20 +1,24 @@ ## Test environments -* local macOS install, R 4.2.1 -* ubuntu (on GitHub Actions), R 4.2.1 +* local macOS install, R 4.2.2 +* ubuntu (on GitHub Actions), R 4.2.2 * win-builder (devel and release) ## R CMD check results 0 errors | 0 warnings | 0 notes -## Reverse dependencies +## revdepcheck results + +We checked 18 reverse dependencies (13 from CRAN + 5 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. + + * We saw 0 new problems + * We failed to check 0 packages -I have checked the 18 reverse dependencies, and no problems were found. --- -In this version I've fixed problems with man files. +In this version fixes some bugs Thanks! Scott Chamberlain From 70e94f493450d1e969ddb102f0362c840c05fcbd Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 24 May 2023 10:21:10 -0700 Subject: [PATCH 54/66] change all httpbin urls to hb.opencpu.org --- tests/testthat/helper-webmockr.R | 3 + tests/testthat/httr_obj.rda | Bin 323 -> 325 bytes tests/testthat/httr_obj_auth.rda | Bin 367 -> 367 bytes tests/testthat/test-CrulAdapter.R | 6 +- tests/testthat/test-HashCounter.R | 2 +- tests/testthat/test-HttrAdapter.R | 64 +++++++++--------- tests/testthat/test-RequestPattern.R | 16 ++--- tests/testthat/test-RequestSignature.R | 8 +-- tests/testthat/test-Response.R | 10 +-- tests/testthat/test-StubRegistry.R | 2 +- tests/testthat/test-StubbedRequest.R | 32 ++++----- tests/testthat/test-b-no-cassette-in-use.R | 2 +- tests/testthat/test-remove_request_stub.R | 4 +- tests/testthat/test-request_registry.R | 4 +- tests/testthat/test-stub_registry.R | 6 +- tests/testthat/test-stub_request.R | 6 +- tests/testthat/test-stub_requests_crul.R | 16 ++--- tests/testthat/test-to_raise.R | 6 +- tests/testthat/test-to_return.R | 32 ++++----- tests/testthat/test-to_return_then.R | 10 +-- tests/testthat/test-to_timeout.R | 4 +- tests/testthat/test-uri_regex.R | 8 +-- tests/testthat/test-wi_th.R | 22 +++--- tests/testthat/test-within_test_that_blocks.R | 12 ++-- .../test-writing-to-disk-write_disk_path.R | 12 ++-- tests/testthat/test-writing-to-disk.R | 16 ++--- tests/testthat/test-zutils.R | 2 +- 27 files changed, 154 insertions(+), 151 deletions(-) diff --git a/tests/testthat/helper-webmockr.R b/tests/testthat/helper-webmockr.R index 067c925..84b5dfe 100644 --- a/tests/testthat/helper-webmockr.R +++ b/tests/testthat/helper-webmockr.R @@ -16,3 +16,6 @@ re_escape <- function(strings){ } strings } + +base_url = "https://hb.opencpu.org" +hb <- function(x = NULL) if (is.null(x)) base_url else paste0(base_url, x) diff --git a/tests/testthat/httr_obj.rda b/tests/testthat/httr_obj.rda index fe826a38e0a55b4c9017475d8972cad3f6e065ed..abcb36d2d2ef8a46a46c960e6fb635387ac39c4b 100644 GIT binary patch literal 325 zcmV-L0lNMliwFP!0000016@%)Ps1<}HAzcogb-3UrVLC~i3iT$3HTFZ>*TZF=? z%_AF^2j}72>&F1PvR8z9!nn*+E`U*CQZ6@m1KR2wG8aUMx}ro^oZ+qFY@TS?X}oLd zd2)E>fEk_*nj16HfEvi3K2r+9N}=B_f~oU&%(||toEll<$MoSoT_p0xWy^G#w2tv@ zYEjFJ-5nRLbnw^L3Hmw{qd<}kFx>-D>)u)Q`fhH+xsGW~lo}K}|CW`8tbvBhpB}sD zu(e^d9Yt62sFlbYZ$Ov4@DF3{>{|j-fK;BhuAnY_7ID$m^=V9uE;G$Pfq6PNS{U7l XhF;YBh$MU&Pl literal 323 zcmV-J0lfYniwFP!0000016@(EPQx$|HBL)tgb-2()(lKli324K~zh*+7c3s zZ64aVJh%_uUO#%!m3>m{C&rq6Rft5YoaKO4 zI|oeIq}SO{@CtNK`}8?Yp|(=ww+%XW=8jp{WwA<)D%oT5aG%U#b>p&mGLIX(>^8Be zkoC9mR##zvGZ?LAS^4Wr26TwixyiO zkv5}fOCGfndgB$~MrQt3tet&Jz%x+V^VS8Fna@I5H+6j)af6#B%1;oUE)CWOThYLa VdLN;JF9UjS_aEM$b4X7D000$Elez!^ diff --git a/tests/testthat/httr_obj_auth.rda b/tests/testthat/httr_obj_auth.rda index 7d5096e13ccedc9f3acdea3c5bd5bf50c8760461..05026045a022851f4032823ab3dd11e7706406ae 100644 GIT binary patch literal 367 zcmV-#0g(P5iwFP!0000016@(wPJ=KQt-!<)T}*rdF1&Hs=(fz5?8caAyfzSmcwidsYKuJnjuVC`kcTZ<_NbN`Xtg`)QVAWOzN+ZJs4mi5x#f z_seJ&ifJ7!qeW<3!1HJs%{6aiavr~teY!P#t!h=PLO~7J%S`C}wp6dB4pjw$?4Xn_ zaA7>FZEkBB0zYhQXpVQ#QBhw;|HcM1CzUn8X~D~b$RxxC6ij@z*r>s_s-(uyrsTg# zeQRF7r41!}vkWNyh_dr!~3J@@qOdA$hM0Yb<@?x2SpS8qK>|9Z$nL%m+@rQ|V7 z4lz-3kC1z#L0`vpi{!q(ymz2GT~~?~#%4`u5i!2Q2@y1%nnrlbm`=IAxLq|PWtq{G zNXiO)5UiMl5>67I@@x{e&njSy#~ro(g5)4NQU*_H3Z>M$pE?{fgV$l*W;96^&+tQZ zzl>%fpH|s2T7<>|Jdc*qT=Pbz=J6ZZuieAfQ?070P*A~D7=_MnL-iW!Pzm5<2Zd~a z3u9E}ans8X%HzhS=6DCKDXN#~K36}@h_D7Y4S0FvR6?9X&iGe@jcRPGiqsfdxBREn zxAx^A_sr^WwJB#wKsLaI% + invisible(stub_request("get", uri = hb("/get")) %>% to_return( body = list(foo = "bar"), headers = list("Content-Type" = "application/json") )) - x <- httr::GET("https://httpbin.org/get") + x <- httr::GET(hb("/get")) expect_equal(x$headers[["content-type"]], "application/json") expect_is(httr::content(x), "list") @@ -129,10 +129,10 @@ test_that("HttrAdapter insensitive headers work, vcr flow", { path <- file.path(tempdir(), "helloworld") vcr::vcr_configure(dir = path) - vcr::use_cassette("test-date", GET("https://httpbin.org/get")) + vcr::use_cassette("test-date", GET(hb("/get"))) vcr::insert_cassette("test-date") - x <- httr::GET("https://httpbin.org/get") + x <- httr::GET(hb("/get")) expect_equal(x$headers[["content-type"]], "application/json") expect_is(httr::content(x), "list") @@ -167,17 +167,17 @@ test_that("HttrAdapter works", { unloadNamespace("vcr") expect_error( res$handle_request(httr_obj), - "Real HTTP connections are disabled.\nUnregistered request:\n GET: https://httpbin.org/get" + "Real HTTP connections are disabled.\nUnregistered request:\n GET: https://hb.opencpu.org/get" ) - invisible(stub_request("get", "https://httpbin.org/get")) + invisible(stub_request("get", hb("/get"))) aa <- res$handle_request(httr_obj) expect_is(res, "HttrAdapter") expect_is(aa, "response") expect_equal(aa$request$method, "GET") - expect_equal(aa$url, "https://httpbin.org/get") + expect_equal(aa$url, hb("/get")) # no response headers expect_equal(length(aa$headers), 0) @@ -189,7 +189,7 @@ test_that("HttrAdapter works", { stub_registry_clear() # stub with headers - x <- stub_request("get", "https://httpbin.org/get") + x <- stub_request("get", hb("/get")) x <- to_return(x, headers = list("User-Agent" = "foo-bar")) aa <- res$handle_request(httr_obj) @@ -197,7 +197,7 @@ test_that("HttrAdapter works", { expect_is(res, "HttrAdapter") expect_is(aa, "response") expect_equal(aa$request$method, "GET") - expect_equal(aa$url, "https://httpbin.org/get") + expect_equal(aa$url, hb("/get")) # has headers and all_headers expect_equal(length(aa$headers), 1) @@ -250,7 +250,7 @@ test_that("HttrAdapter works with httr::authenticate", { stub_registry_clear() # stub_registry() # request_registry() - z <- stub_request("get", uri = "https://httpbin.org/basic-auth/foo/bar") %>% + z <- stub_request("get", uri = hb("/basic-auth/foo/bar")) %>% to_return( body = list(foo = "bar"), headers = list("Content-Type" = "application/json") @@ -264,7 +264,7 @@ test_that("HttrAdapter works with httr::authenticate", { # mocked httr requests with auth work # before the fixes in HttrAdapter: a real request through webmockr would # not work with authenticate - x <- httr::GET("https://httpbin.org/basic-auth/foo/bar", httr::authenticate("foo", "bar")) + x <- httr::GET(hb("/basic-auth/foo/bar"), httr::authenticate("foo", "bar")) expect_is(x, "response") expect_equal(httr::content(x), list(foo = "bar")) expect_equal(x$headers, structure(list(`content-type` = "application/json"), @@ -287,24 +287,24 @@ test_that("httr works with webmockr_allow_net_connect", { httr_mock() stub_registry_clear() - z <- stub_request("get", uri = "https://httpbin.org/get?stuff=things") %>% + z <- stub_request("get", uri = hb("/get?stuff=things")) %>% to_return(body = "yum=cheese") - x <- httr::GET("https://httpbin.org/get?stuff=things") + x <- httr::GET(hb("/get?stuff=things")) expect_true(httr::content(x, "text", encoding="UTF-8") == "yum=cheese") # allow net connect - stub still exists though - so not a real request webmockr_allow_net_connect() - z <- httr::GET("https://httpbin.org/get?stuff=things") + z <- httr::GET(hb("/get?stuff=things")) expect_true(httr::content(z, "text", encoding="UTF-8") == "yum=cheese") # allow net connect - stub now gone - so real request should happen stub_registry_clear() - w <- httr::GET("https://httpbin.org/get?stuff=things") + w <- httr::GET(hb("/get?stuff=things")) expect_false(httr::content(w, "text", encoding="UTF-8") == "yum=cheese") # disable net connect - now real requests can't be made webmockr_disable_net_connect() - expect_error(httr::GET("https://httpbin.org/get?stuff=things"), + expect_error(httr::GET(hb("/get?stuff=things")), "Real HTTP connections are disabled") }) @@ -313,15 +313,15 @@ test_that("httr requests with bodies work", { httr_mock() stub_registry_clear() - z <- stub_request("post", uri = "https://httpbin.org/post") %>% + z <- stub_request("post", uri = hb("/post")) %>% to_return(body = "asdffsdsdf") - x <- httr::POST("https://httpbin.org/post", body = list(stuff = "things")) + x <- httr::POST(hb("/post"), body = list(stuff = "things")) expect_true(httr::content(x, "text", encoding="UTF-8") == "asdffsdsdf") # now with allow net connect stub_registry_clear() webmockr_allow_net_connect() - x <- httr::POST("https://httpbin.org/post", body = list(stuff = "things")) + x <- httr::POST(hb("/post"), body = list(stuff = "things")) expect_identical(httr::content(x)$form, list(stuff = "things")) webmockr_disable_net_connect() @@ -333,16 +333,16 @@ test_that("httr requests with nested list bodies work", { httr_mock() stub_registry_clear() body = list(id = ' ', method = 'x', params = list(pwd = 'p', user = 'a')) - z <- stub_request("post", uri = "https://httpbin.org/post") %>% + z <- stub_request("post", uri = hb("/post")) %>% wi_th(body = body) %>% to_return(body = "asdffsdsdf") - x <- httr::POST("https://httpbin.org/post", body = body) + x <- httr::POST(hb("/post"), body = body) expect_true(httr::content(x, "text", encoding="UTF-8") == "asdffsdsdf") # now with allow net connect stub_registry_clear() webmockr_allow_net_connect() - x <- httr::POST("https://httpbin.org/post", + x <- httr::POST(hb("/post"), body = jsonlite::toJSON(body), httr::content_type_json()) expect_equal( jsonlite::fromJSON(rawToChar(x$content))$json, @@ -359,22 +359,22 @@ test_that("httr requests with JSON-encoded bodies work", { stub_registry_clear() body <- list(foo = "bar") - z <- stub_request("post", uri = "https://httpbin.org/post") %>% + z <- stub_request("post", uri = hb("/post")) %>% wi_th(body = jsonlite::toJSON(body, auto_unbox = TRUE)) # encoded body works - res <- httr::POST("https://httpbin.org/post", body = body, encode = "json") + res <- httr::POST(hb("/post"), body = body, encode = "json") expect_is(res, "response") # encoded but modified body fails expect_error( - httr::POST("https://httpbin.org/post", body = list(foo = "bar1"), encode = "json"), + httr::POST(hb("/post"), body = list(foo = "bar1"), encode = "json"), "Unregistered request" ) # unencoded body fails expect_error( - httr::POST("https://httpbin.org/post", body = body), + httr::POST(hb("/post"), body = body), "Unregistered request" ) }) diff --git a/tests/testthat/test-RequestPattern.R b/tests/testthat/test-RequestPattern.R index 1893ed9..9db87e0 100644 --- a/tests/testthat/test-RequestPattern.R +++ b/tests/testthat/test-RequestPattern.R @@ -3,7 +3,7 @@ context("RequestPattern") test_that("RequestPattern: structure is correct", { expect_is(RequestPattern, "R6ClassGenerator") - aa <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get") + aa <- RequestPattern$new(method = "get", uri = hb("/get")) expect_is(aa, "RequestPattern") expect_null(aa$body_pattern) @@ -17,12 +17,12 @@ test_that("RequestPattern: structure is correct", { }) test_that("RequestPattern: behaves as expected", { - aa <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get") - rs1 <- RequestSignature$new(method = "get", uri = "https://httpbin.org/get") - rs2 <- RequestSignature$new(method = "post", uri = "https://httpbin.org/get") + aa <- RequestPattern$new(method = "get", uri = hb("/get")) + rs1 <- RequestSignature$new(method = "get", uri = hb("/get")) + rs2 <- RequestSignature$new(method = "post", uri = hb("/get")) rs3 <- RequestSignature$new( method = "get", - uri = "https:/httpbin.org/get", + uri = "https:/hb.opencpu.org", options = list(headers = list(`User-Agent` = "foobar", stuff = "things")) ) @@ -32,7 +32,7 @@ test_that("RequestPattern: behaves as expected", { expect_is(aa$to_s(), "character") expect_match(aa$to_s(), "GET") - expect_match(aa$to_s(), "httpbin.org/get") + expect_match(aa$to_s(), "hb.opencpu.org/get") }) test_that("RequestPattern: uri_regex", { @@ -44,7 +44,7 @@ test_that("RequestPattern: uri_regex", { test_that("RequestPattern fails well", { expect_error(RequestPattern$new(), "one of uri or uri_regex is required") - x <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get") + x <- RequestPattern$new(method = "get", uri = hb("/get")) expect_error(x$matches(), "argument \"request_signature\" is missing") expect_error(x$matches("adfadf"), "request_signature must be of class RequestSignature") @@ -103,7 +103,7 @@ test_that("BodyPattern: structure is correct", { bb <- RequestSignature$new( method = "get", - uri = "https:/httpbin.org/get", + uri = hb("/get"), options = list( body = list(foo = "bar", a = 5) ) diff --git a/tests/testthat/test-RequestSignature.R b/tests/testthat/test-RequestSignature.R index 0e997ac..4e4b577 100644 --- a/tests/testthat/test-RequestSignature.R +++ b/tests/testthat/test-RequestSignature.R @@ -3,7 +3,7 @@ context("RequestSignature") test_that("RequestSignature: works", { expect_is(RequestSignature, "R6ClassGenerator") - aa <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get") + aa <- RequestSignature$new(method = "get", uri = hb("/get")) expect_is(aa, "RequestSignature") @@ -18,14 +18,14 @@ test_that("RequestSignature: works", { expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https:/httpbin.org/get") + expect_equal(aa$uri, hb("/get")) expect_is(aa$to_s, "function") - expect_equal(aa$to_s(), "GET: https:/httpbin.org/get") + expect_equal(aa$to_s(), "GET: https://hb.opencpu.org/get") }) test_that("RequestSignature: different methods work", { - aa <- RequestSignature$new(method = "post", uri = "https:/httpbin.org/post", + aa <- RequestSignature$new(method = "post", uri = hb("/post"), options = list(fields = list(foo = "bar"))) aa$headers <- list(Accept = "application/json") aa$body <- list(foo = "bar") diff --git a/tests/testthat/test-Response.R b/tests/testthat/test-Response.R index 8c98f64..07fb642 100644 --- a/tests/testthat/test-Response.R +++ b/tests/testthat/test-Response.R @@ -35,9 +35,9 @@ test_that("Response: bits are correct prior to having data", { test_that("Response: bits are correct after having data", { aa <- Response$new() - aa$set_url("https://httpbin.org/get") + aa$set_url(hb("/get")) aa$set_request_headers(list('Content-Type' = "application/json")) - aa$set_response_headers(list('Host' = "httpbin.org")) + aa$set_response_headers(list('Host' = "hb.opencpu.org")) aa$set_status(404) aa$set_body("hello world") aa$set_exception("exception") @@ -54,7 +54,7 @@ test_that("Response: bits are correct after having data", { expect_null(aa$response_headers_all) expect_equal(aa$status_code, 404) - expect_equal(aa$url, "https://httpbin.org/get") + expect_equal(aa$url, hb("/get")) expect_null(aa$name) expect_equal(aa$body, "hello world") @@ -63,9 +63,9 @@ test_that("Response: bits are correct after having data", { expect_equal(aa$get_body(), "hello world") expect_equal(aa$get_exception(), "exception") expect_equal(aa$get_request_headers()[[1]], "application/json") - expect_equal(aa$get_respone_headers()[[1]], "httpbin.org") + expect_equal(aa$get_respone_headers()[[1]], "hb.opencpu.org") expect_equal(aa$get_status(), 404) - expect_equal(aa$get_url(), "https://httpbin.org/get") + expect_equal(aa$get_url(), hb("/get")) expect_output(aa$print(), "") expect_output(aa$print(), "headers") diff --git a/tests/testthat/test-StubRegistry.R b/tests/testthat/test-StubRegistry.R index 0d9b376..ab6bb9d 100644 --- a/tests/testthat/test-StubRegistry.R +++ b/tests/testthat/test-StubRegistry.R @@ -31,7 +31,7 @@ test_that("StubRegistry: bits are correct after having data", { stub1$with(headers = list('User-Agent' = 'R')) stub1$to_return(status = 200, body = "foobar", headers = list()) - stub2 <- StubbedRequest$new(method = "get", uri = "https://httpbin.org") + stub2 <- StubbedRequest$new(method = "get", uri = hb()) aa <- StubRegistry$new() expect_is(aa$register_stub(stub = stub1), "list") diff --git a/tests/testthat/test-StubbedRequest.R b/tests/testthat/test-StubbedRequest.R index 8f7e864..e943b92 100644 --- a/tests/testthat/test-StubbedRequest.R +++ b/tests/testthat/test-StubbedRequest.R @@ -3,7 +3,7 @@ context("StubbedRequest") test_that("StubbedRequest: works", { expect_is(StubbedRequest, "R6ClassGenerator") - aa <- StubbedRequest$new(method = "get", uri = "https:/httpbin.org/get") + aa <- StubbedRequest$new(method = "get", uri = "https:/hb.opencpu.org/get") expect_is(aa, "StubbedRequest") @@ -19,14 +19,14 @@ test_that("StubbedRequest: works", { expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https:/httpbin.org/get") + expect_equal(aa$uri, "https:/hb.opencpu.org/get") expect_is(aa$uri_parts, "list") expect_equal(aa$uri_parts$domain, "https") - expect_equal(aa$uri_parts$path, "httpbin.org/get") + expect_equal(aa$uri_parts$path, "hb.opencpu.org/get") expect_is(aa$to_s, "function") - expect_equal(aa$to_s(), "GET: https:/httpbin.org/get") + expect_equal(aa$to_s(), "GET: https:/hb.opencpu.org/get") # with expect_is(aa$with, "function") @@ -34,12 +34,12 @@ test_that("StubbedRequest: works", { aa$with(query = list(foo = "bar")) expect_is(aa$query, "list") expect_named(aa$query, "foo") - expect_equal(aa$to_s(), "GET: https:/httpbin.org/get?foo=bar") + expect_equal(aa$to_s(), "GET: https:/hb.opencpu.org/get?foo=bar") ## >1 query param gets combined with "&" and not "," aa$with(query = list(foo = "bar", stuff = 567)) expect_equal(sort(names(aa$query)), c("foo", "stuff")) - expect_equal(aa$to_s(), "GET: https:/httpbin.org/get?foo=bar&stuff=567") + expect_equal(aa$to_s(), "GET: https:/hb.opencpu.org/get?foo=bar&stuff=567") # to_return expect_is(aa$to_return, "function") @@ -56,7 +56,7 @@ test_that("StubbedRequest: works", { test_that("StubbedRequest: to_timeout", { - x <- StubbedRequest$new(method = "get", uri = "https:/httpbin.org/get") + x <- StubbedRequest$new(method = "get", uri = "https:/hb.opencpu.org/get") expect_false(grepl("should_timeout: TRUE", x$to_s())) x$to_timeout() expect_true(grepl("should_timeout: TRUE", x$to_s())) @@ -64,7 +64,7 @@ test_that("StubbedRequest: to_timeout", { library("fauxpas") test_that("StubbedRequest: to_raise", { - x <- StubbedRequest$new(method = "get", uri = "https:/httpbin.org/get") + x <- StubbedRequest$new(method = "get", uri = "https:/hb.opencpu.org/get") expect_false(grepl("to_raise: HTTPBadGateway", x$to_s())) x$to_raise(HTTPBadGateway) expect_true(grepl("to_raise: HTTPBadGateway", x$to_s())) @@ -79,37 +79,37 @@ test_that("StubbedRequest: to_raise", { test_that("StubbedRequest: different methods work", { expect_equal( StubbedRequest$new(method = "any", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "any" ) expect_equal( StubbedRequest$new(method = "get", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "get" ) expect_equal( StubbedRequest$new(method = "head", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "head" ) expect_equal( StubbedRequest$new(method = "post", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "post" ) expect_equal( StubbedRequest$new(method = "put", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "put" ) expect_equal( StubbedRequest$new(method = "patch", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "patch" ) expect_equal( StubbedRequest$new(method = "delete", - uri = "https:/httpbin.org/get")$method, + uri = "https:/hb.opencpu.org/get")$method, "delete" ) }) @@ -191,7 +191,7 @@ test_that("StubbedRequest nested lists in body", { test_that("StubbedRequest w/ >1 to_return()", { stub_registry_clear() - x <- StubbedRequest$new(method = "get", uri = "httpbin.org") + x <- StubbedRequest$new(method = "get", uri = "hb.opencpu.org") x$to_return(status = 200, body = "foobar", headers = list(a = 5)) x$to_return(status = 200, body = "bears", headers = list(b = 6)) x$to_s() diff --git a/tests/testthat/test-b-no-cassette-in-use.R b/tests/testthat/test-b-no-cassette-in-use.R index 7626ed2..54536d6 100644 --- a/tests/testthat/test-b-no-cassette-in-use.R +++ b/tests/testthat/test-b-no-cassette-in-use.R @@ -7,7 +7,7 @@ test_that("no cassette in use behaves as expected", { invisible(vcr_configure(dir = dir)) crul::mock() - x <- crul::HttpClient$new(url = "https://httpbin.org") + x <- crul::HttpClient$new(url = hb()) # when no cassette in use, we get expected vcr error expect_error( diff --git a/tests/testthat/test-remove_request_stub.R b/tests/testthat/test-remove_request_stub.R index 4d8cacb..1122963 100644 --- a/tests/testthat/test-remove_request_stub.R +++ b/tests/testthat/test-remove_request_stub.R @@ -7,7 +7,7 @@ test_that("remove_request_stub", { expect_equal(length(stub_registry()$request_stubs), 0) # make a stub - x <- stub_request("get", "https://httpbin.org/get") + x <- stub_request("get", hb("/get")) # no there's a stub expect_equal(length(stub_registry()$request_stubs), 1) @@ -27,7 +27,7 @@ test_that("remove_request_stub: removes the stub upon an error", { expect_equal(length(stub_registry()$request_stubs), 0) expect_error( - stub_request("post", uri = "https://httpbin.org/post") %>% + stub_request("post", uri = hb("/post")) %>% to_return(body = 5) ) expect_equal(length(stub_registry()$request_stubs), 0) diff --git a/tests/testthat/test-request_registry.R b/tests/testthat/test-request_registry.R index c31d4d7..f7f7a08 100644 --- a/tests/testthat/test-request_registry.R +++ b/tests/testthat/test-request_registry.R @@ -7,10 +7,10 @@ test_that("request_registry: structure", { expect_is(request_registry(), "RequestRegistry") enable() - stub_request("get", "https://httpbin.org/get") %>% + stub_request("get", hb("/get")) %>% to_return(body = "success!", status = 200) invisible( - crul::HttpClient$new(url = "https://httpbin.org")$get("get") + crul::HttpClient$new(url = hb())$get("get") ) disable() diff --git a/tests/testthat/test-stub_registry.R b/tests/testthat/test-stub_registry.R index b769b2a..892b0de 100644 --- a/tests/testthat/test-stub_registry.R +++ b/tests/testthat/test-stub_registry.R @@ -12,13 +12,13 @@ test_that("stub_registry: works", { "GET: https://scottchamberlain.info") # stub with body - stub_request('post', uri = 'https://httpbin.org/post') %>% + stub_request('post', uri = hb('/post')) %>% wi_th( body = list(y=crul::upload(system.file("CITATION"))) ) expect_equal(length(stub_registry()$request_stubs), 2) expect_match(stub_registry()$request_stubs[[2]]$to_s(), - "POST: https://httpbin.org/post") + "POST: https://hb.opencpu.org/post") expect_match(stub_registry()$request_stubs[[2]]$to_s(), "CITATION") expect_match(stub_registry()$request_stubs[[2]]$to_s(), @@ -27,7 +27,7 @@ test_that("stub_registry: works", { stub_registry_clear() # stub with > 1 to_return() - s <- stub_request("get", "https://httpbin.org/get") + s <- stub_request("get", hb("/get")) to_return(s, status = 200, body = "foobar", headers = list(a = 5)) to_return(s, status = 200, body = "bears", headers = list(b = 6)) expect_equal(length(stub_registry()$request_stubs), 1) diff --git a/tests/testthat/test-stub_request.R b/tests/testthat/test-stub_request.R index 52c30b4..9a738fc 100644 --- a/tests/testthat/test-stub_request.R +++ b/tests/testthat/test-stub_request.R @@ -6,7 +6,7 @@ test_that("no stubs exist before stub_request called", { expect_equal(length(stub_registry()$request_stubs), 0) }) -aa <- stub_request("get", "https://httpbin.org/get") +aa <- stub_request("get", hb("/get")) test_that("stub_request bits are correct", { @@ -22,7 +22,7 @@ test_that("stub_request bits are correct", { expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https://httpbin.org/get") + expect_equal(aa$uri, hb("/get")) expect_is(aa$print, "function") expect_output(aa$print(), "") @@ -31,7 +31,7 @@ test_that("stub_request bits are correct", { expect_error(aa$to_return(), "argument \"body\" is missing") expect_is(aa$to_s, "function") - expect_equal(aa$to_s(), "GET: https://httpbin.org/get") + expect_equal(aa$to_s(), "GET: https://hb.opencpu.org/get") expect_is(aa$with, "function") expect_null(aa$with()) diff --git a/tests/testthat/test-stub_requests_crul.R b/tests/testthat/test-stub_requests_crul.R index 889c512..bf18710 100644 --- a/tests/testthat/test-stub_requests_crul.R +++ b/tests/testthat/test-stub_requests_crul.R @@ -13,7 +13,7 @@ test_that("stub_request works well: get requests", { ## 0 stubs expect_equal(length(stub_registry()$request_stubs), 0) - x <- crul::HttpClient$new(url = "https://httpbin.org") + x <- crul::HttpClient$new(url = hb()) ms1 <- get_err_mssg(x$get('get', query = list(foo = "bar", a = 5))) expect_error( @@ -34,7 +34,7 @@ test_that("stub_request works well: get requests", { ) # after a stub made - stub_request("get", "https://httpbin.org/get?foo=bar&a=5") %>% + stub_request("get", hb("/get?foo=bar&a=5")) %>% wi_th(headers = list( 'Accept-Encoding' = 'gzip, deflate', 'Accept' = 'application/json, text/xml, application/xml, */*') @@ -45,7 +45,7 @@ test_that("stub_request works well: get requests", { # the matching request works z <- x$get('get', query = list(foo = "bar", a = 5)) expect_is(z, "HttpResponse") - expect_equal(z$url, "https://httpbin.org/get?foo=bar&a=5") + expect_equal(z$url, hb("/get?foo=bar&a=5")) # but the others still do not work cause they dont match the stub ms2 <- get_err_mssg(x$get('get', query = list(foo = "bar", stuff = FALSE))) @@ -54,7 +54,7 @@ test_that("stub_request works well: get requests", { expect_error(x$get('get', query = list(foo = "bar")), re_escape(ms3)) # a stub for the second request - stub_request("get", "https://httpbin.org/get?foo=bar&stuff=FALSE") %>% + stub_request("get", hb("/get?foo=bar&stuff=FALSE")) %>% wi_th(headers = list( 'Accept-Encoding' = 'gzip, deflate', 'Accept' = 'application/json, text/xml, application/xml, */*') @@ -65,7 +65,7 @@ test_that("stub_request works well: get requests", { # the other request now works w <- x$get('get', query = list(foo = "bar", stuff = FALSE)) expect_is(w, "HttpResponse") - expect_equal(w$url, "https://httpbin.org/get?foo=bar&stuff=FALSE") + expect_equal(w$url, hb("/get?foo=bar&stuff=FALSE")) # but the others still do not work cause they dont match the stub ms4 <- get_err_mssg(x$get('get', query = list(foo = "bar"))) @@ -84,7 +84,7 @@ test_that("stub_request works well: post requests", { ## 0 stubs expect_equal(length(stub_registry()$request_stubs), 0) - x <- crul::HttpClient$new(url = "https://httpbin.org") + x <- crul::HttpClient$new(url = hb()) ms1 <- get_err_mssg(x$post('post', body = list(foo = "bar", a = 5))) expect_error( @@ -93,7 +93,7 @@ test_that("stub_request works well: post requests", { ) # after a stub made - stub_request("post", "https://httpbin.org/post") %>% + stub_request("post", hb("/post")) %>% wi_th(headers = list( 'Accept-Encoding' = 'gzip, deflate', 'Accept' = 'application/json, text/xml, application/xml, */*'), @@ -105,7 +105,7 @@ test_that("stub_request works well: post requests", { # the matching request works z <- x$post('post', body = list(foo = "bar", a = 5)) expect_is(z, "HttpResponse") - expect_equal(z$url, "https://httpbin.org/post") + expect_equal(z$url, hb("/post")) # but the others still do not work cause they dont match the stub ms2 <- get_err_mssg(x$post('post', query = list(foo = "bar", stuff = FALSE))) diff --git a/tests/testthat/test-to_raise.R b/tests/testthat/test-to_raise.R index abb766c..676a682 100644 --- a/tests/testthat/test-to_raise.R +++ b/tests/testthat/test-to_raise.R @@ -7,7 +7,7 @@ test_that("no stubs exist before stub_request called", { }) library(fauxpas) -aa <- stub_request("get", "https://httpbin.org/get") %>% to_raise(HTTPAccepted) +aa <- stub_request("get", hb("/get")) %>% to_raise(HTTPAccepted) test_that("stub_request bits are correct", { @@ -23,7 +23,7 @@ test_that("stub_request bits are correct", { expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https://httpbin.org/get") + expect_equal(aa$uri, hb("/get")) # to_raise expected stuff rr <- aa$responses_sequences[[1]] @@ -43,7 +43,7 @@ test_that("stub_request fails well", { expect_error(to_raise(5), ".data must be of class StubbedRequest") # exception clases - zzz <- stub_request("get", "https://httpbin.org/get") + zzz <- stub_request("get", hb("/get")) expect_error(to_raise(zzz, "foo"), "all objects must be error classes from fauxpas") }) diff --git a/tests/testthat/test-to_return.R b/tests/testthat/test-to_return.R index 68b041b..c3cfb45 100644 --- a/tests/testthat/test-to_return.R +++ b/tests/testthat/test-to_return.R @@ -5,7 +5,7 @@ test_that("no stubs exist before stub_request called", { expect_equal(length(stub_registry()$request_stubs), 0) }) -aa <- stub_request("get", "https://httpbin.org/get") %>% +aa <- stub_request("get", hb("/get")) %>% to_return(status = 200, body = "stuff", headers = list(a = 5)) test_that("stub_request bits are correct", { @@ -20,7 +20,7 @@ test_that("stub_request bits are correct", { expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https://httpbin.org/get") + expect_equal(aa$uri, hb("/get")) # to_return expected stuff expect_is(aa$response_headers, "list") @@ -45,7 +45,7 @@ test_that("stub_request fails well", { expect_error(to_return(), "argument \".data\" is missing") expect_error(to_return(5), ".data must be of class StubbedRequest") - zzz <- stub_request("get", "https://httpbin.org/get") + zzz <- stub_request("get", hb("/get")) # status expect_error(to_return(zzz, status = "foo"), "must be of class numeric") @@ -62,9 +62,9 @@ stub_registry_clear() enable() context("to_return: response headers returned all lowercase") test_that("to_return (response) headers are all lowercase, crul", { - stub <- stub_request(uri = "http://httpbin.org/get") %>% + stub <- stub_request(uri = hb("/get")) %>% to_return(headers = list("Foo-Bar" = "baz")) - cli <- crul::HttpClient$new(url = "http://httpbin.org/") + cli <- crul::HttpClient$new(url = hb()) x <- cli$get("get") expect_is(x$response_headers, "list") @@ -74,9 +74,9 @@ test_that("to_return (response) headers are all lowercase, crul", { stub_registry_clear() test_that("to_return (response) headers are all lowercase, httr", { loadNamespace("httr") - stub <- stub_request(uri = "http://httpbin.org/get") %>% + stub <- stub_request(uri = hb("/get")) %>% to_return(headers = list("Foo-Bar" = "baz")) - x <- httr::GET("http://httpbin.org/get") + x <- httr::GET(hb("/get")) expect_is(x$headers, "list") expect_named(x$headers, "foo-bar") @@ -89,9 +89,9 @@ stub_registry_clear() enable() context("to_return: response header values are all character") test_that("to_return response header values are all character, crul", { - cli <- crul::HttpClient$new(url = "http://httpbin.org/") + cli <- crul::HttpClient$new(url = hb()) - stub_request(uri = "http://httpbin.org/get") %>% + stub_request(uri = hb("/get")) %>% to_return(headers = list("Foo-Bar" = 10)) x <- cli$get("get") @@ -101,7 +101,7 @@ test_that("to_return response header values are all character, crul", { expect_equal(x$response_headers$`foo-bar`, "10") stub_registry_clear() - stub_request(uri = "http://httpbin.org/get") %>% + stub_request(uri = hb("/get")) %>% to_return(headers = list( a = 10, b = 234233434, c = 2344.342342, d = "brown", e = as.factor("blue") @@ -122,9 +122,9 @@ stub_registry_clear() test_that("to_return response header values are all character, httr", { loadNamespace("httr") - stub_request(uri = "http://httpbin.org/get") %>% + stub_request(uri = hb("/get")) %>% to_return(headers = list("Foo-Bar" = 10)) - x <- httr::GET("http://httpbin.org/get") + x <- httr::GET(hb("/get")) expect_is(x$headers, "list") expect_named(x$headers, "foo-bar") @@ -132,12 +132,12 @@ test_that("to_return response header values are all character, httr", { expect_equal(x$headers$`foo-bar`, "10") stub_registry_clear() - stub_request(uri = "http://httpbin.org/get") %>% + stub_request(uri = hb("/get")) %>% to_return(headers = list( a = 10, b = 234233434, c = 2344.342342, d = "brown", e = as.factor("blue") )) - z <- httr::GET("http://httpbin.org/get") + z <- httr::GET(hb("/get")) expect_is(z$headers, "list") expect_named(z$headers, letters[1:5]) @@ -162,12 +162,12 @@ stub_to_return_status_code <- function() { stub_registry_clear() enable() test_that("stub_request status accepts numeric or integer values", { - stub_status_type_a <- stub_request("get", "https://httpbin.org/get") + stub_status_type_a <- stub_request("get", hb("/get")) expect_s3_class(to_return(stub_status_type_a, status = 200), "StubbedRequest") expect_type(stub_to_return_status_code(), "double") # numeric = double stub_registry_clear() - stub_status_type_b <- stub_request("get", "https://httpbin.org/get") + stub_status_type_b <- stub_request("get", hb("/get")) expect_s3_class(to_return(stub_status_type_b, status = 200L), "StubbedRequest") expect_type(stub_to_return_status_code(), "integer") }) diff --git a/tests/testthat/test-to_return_then.R b/tests/testthat/test-to_return_then.R index 3e9ed35..7938f3e 100644 --- a/tests/testthat/test-to_return_then.R +++ b/tests/testthat/test-to_return_then.R @@ -4,11 +4,11 @@ enable() webmockr_reset() test_that("to_return: then", { - stub <- stub_request("get", "https://httpbin.org/get?stuff=things") + stub <- stub_request("get", hb("/get?stuff=things")) to_return(stub, status = 200, body = "foobar", headers = list(a = 5)) to_return(stub, status = 200, body = "bears", headers = list(b = 6)) - cli <- crul::HttpClient$new(url = "https://httpbin.org/") + cli <- crul::HttpClient$new(url = hb()) x1 <- cli$get("get", query = list(stuff="things")) x2 <- cli$get("get", query = list(stuff="things")) x3 <- cli$get("get", query = list(stuff="things")) @@ -24,11 +24,11 @@ test_that("to_return: then", { webmockr_reset() test_that("to_return: webmockr_reset allows multiple requests to start from beginning", { - stub <- stub_request("get", "https://httpbin.org/get?stuff=things") + stub <- stub_request("get", hb("/get?stuff=things")) to_return(stub, status = 200, body = "foobar", headers = list(a = 5)) to_return(stub, status = 200, body = "bears", headers = list(b = 6)) - cli <- crul::HttpClient$new(url = "https://httpbin.org/") + cli <- crul::HttpClient$new(url = hb()) x1 <- cli$get("get", query = list(stuff="things")) x2 <- cli$get("get", query = list(stuff="things")) @@ -44,7 +44,7 @@ test_that("to_return: webmockr_reset allows multiple requests to start from begi # RESET - requests give back expected body (have to make stub again) webmockr_reset() - stub <- stub_request("get", "https://httpbin.org/get?stuff=things") + stub <- stub_request("get", hb("/get?stuff=things")) to_return(stub, status = 200, body = "foobar", headers = list(a = 5)) to_return(stub, status = 200, body = "bears", headers = list(b = 6)) diff --git a/tests/testthat/test-to_timeout.R b/tests/testthat/test-to_timeout.R index 38b9cd4..1d45d58 100644 --- a/tests/testthat/test-to_timeout.R +++ b/tests/testthat/test-to_timeout.R @@ -6,7 +6,7 @@ test_that("no stubs exist before stub_request called", { expect_equal(length(stub_registry()$request_stubs), 0) }) -aa <- stub_request("get", "https://httpbin.org/get") %>% to_timeout() +aa <- stub_request("get", hb("/get")) %>% to_timeout() test_that("stub_request bits are correct", { @@ -22,7 +22,7 @@ test_that("stub_request bits are correct", { expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https://httpbin.org/get") + expect_equal(aa$uri, hb("/get")) # to_timeout expected stuff expect_true(aa$responses_sequences[[1]]$timeout) diff --git a/tests/testthat/test-uri_regex.R b/tests/testthat/test-uri_regex.R index a21132f..5493ae2 100644 --- a/tests/testthat/test-uri_regex.R +++ b/tests/testthat/test-uri_regex.R @@ -1,7 +1,7 @@ context("uri_regex") test_that("uri_regex with crul", { - stub_request("get", uri_regex = "httpbin.org/.+") %>% + stub_request("get", uri_regex = "hb.opencpu.org/.+") %>% to_return(body = list(foo = "bar")) library(crul) @@ -9,7 +9,7 @@ test_that("uri_regex with crul", { invisible( lapply(c('elephants', 'bears', 'leaves', 'foo', 'bar'), function(z) { - expect_true(HttpClient$new("https://httpbin.org")$get(z)$success()) + expect_true(HttpClient$new(hb())$get(z)$success()) }) ) @@ -41,14 +41,14 @@ test_that("uri_regex with crul", { stub_registry_clear() test_that("uri_regex with httr", { - stub_request("get", uri_regex = "httpbin.org/.+") %>% + stub_request("get", uri_regex = "hb.opencpu.org/.+") %>% to_return(body = list(foo = "bar")) library(httr) enable(adapter = "httr") invisible( lapply(c('elephants', 'bears', 'leaves', 'foo', 'bar'), function(z) { - expect_false(http_error(GET(file.path("https://httpbin.org", z)))) + expect_false(http_error(GET(file.path(hb(), z)))) }) ) diff --git a/tests/testthat/test-wi_th.R b/tests/testthat/test-wi_th.R index b1b5217..85772e7 100644 --- a/tests/testthat/test-wi_th.R +++ b/tests/testthat/test-wi_th.R @@ -1,7 +1,7 @@ context("wi_th") test_that("wi_th: with just headers", { - aa <- stub_request("get", "https://httpbin.org/get") %>% + aa <- stub_request("get", hb("/get")) %>% wi_th(headers = list("User-Agent" = "R")) expect_is(aa, "StubbedRequest") @@ -16,12 +16,12 @@ test_that("wi_th: with just headers", { expect_is(aa$method, "character") expect_equal(aa$method, "get") expect_is(aa$uri, "character") - expect_equal(aa$uri, "https://httpbin.org/get") + expect_equal(aa$uri, hb("/get")) expect_equal(aa$request_headers, list("User-Agent" = "R")) }) test_that("wi_th: with headers and query", { - aa <- stub_request("get", "https://httpbin.org/get") %>% + aa <- stub_request("get", hb("/get")) %>% wi_th( query = list(hello = "world"), headers = list("User-Agent" = "R")) @@ -34,36 +34,36 @@ test_that("wi_th: with headers and query", { }) test_that("wi_th: bodies", { - aa <- stub_request("post", "https://httpbin.org/post") %>% + aa <- stub_request("post", hb("/post")) %>% wi_th(body = list(foo = "bar")) expect_is(aa$body, "list") expect_output(print(aa), "body \\(class: list\\): foo=bar") - bb <- stub_request("post", "https://httpbin.org/post") %>% + bb <- stub_request("post", hb("/post")) %>% wi_th(body = '{"foo": "bar"}') expect_is(bb$body, "character") expect_output(print(bb), "body \\(class: character\\): \\{\"foo\": \"bar\"\\}") - cc <- stub_request("post", "https://httpbin.org/post") %>% + cc <- stub_request("post", hb("/post")) %>% wi_th(body = charToRaw('{"foo": "bar"}')) expect_is(cc$body, "raw") expect_output(print(cc), "body \\(class: raw\\): raw bytes, length: 14") - dd <- stub_request("post", "https://httpbin.org/post") %>% + dd <- stub_request("post", hb("/post")) %>% wi_th(body = 5) expect_is(dd$body, "numeric") expect_output(print(dd), "body \\(class: numeric\\): 5") - ee <- stub_request("post", "https://httpbin.org/post") %>% + ee <- stub_request("post", hb("/post")) %>% wi_th(body = crul::upload(system.file("CITATION"))) expect_is(ee$body, "form_file") expect_output(print(ee), "body \\(class: form_file\\): crul::upload") # FIXME: ideally (maybe?) we have a upload within a list look like # the above when not in a list? - ff <- stub_request("post", "https://httpbin.org/post") %>% + ff <- stub_request("post", hb("/post")) %>% wi_th(body = list(y = crul::upload(system.file("CITATION")))) expect_is(ff$body, "list") expect_is(ff$body$y, "form_file") @@ -74,7 +74,7 @@ test_that("wi_th fails well", { expect_error(wi_th(), "argument \".data\" is missing") expect_error(wi_th(5), ".data must be of class StubbedRequest") - zzz <- stub_request("get", "https://httpbin.org/get") + zzz <- stub_request("get", hb("/get")) # query expect_error(wi_th(zzz, query = list(5, 6)), @@ -94,7 +94,7 @@ test_that("wi_th fails well", { }) test_that("wi_th .list works", { - req <- stub_request("post", "https://httpbin.org/post") + req <- stub_request("post", hb("/post")) expect_equal( wi_th(req, .list = list(body = list(foo = "bar"))), wi_th(req, body = list(foo = "bar")) diff --git a/tests/testthat/test-within_test_that_blocks.R b/tests/testthat/test-within_test_that_blocks.R index 80db751..b812810 100644 --- a/tests/testthat/test-within_test_that_blocks.R +++ b/tests/testthat/test-within_test_that_blocks.R @@ -5,12 +5,12 @@ test_that("httr: without pipe", { enable() dat_json <- '{"foo":"bar"}' - stub <- stub_request("get", uri = "https://httpbin.org/get") + stub <- stub_request("get", uri = hb("/get")) to_return(stub, body = dat_json, headers = list("Content-Type" = "application/json; charset=utf-8") ) - res <- GET("https://httpbin.org/get") + res <- GET(hb("/get")) expect_true(inherits(res, "response")) expect_is(content(res), "list") expect_named(content(res), "foo") @@ -22,10 +22,10 @@ test_that("httr: without pipe", { test_that("httr: with pipe", { enable() dat_json <- '{"foo":"bar"}' - stub <- stub_request("get", uri = "https://httpbin.org/get") %>% + stub <- stub_request("get", uri = hb("/get")) %>% to_return(body = dat_json, headers = list("Content-Type" = "application/json; charset=utf-8")) - res <- GET("https://httpbin.org/get") + res <- GET(hb("/get")) expect_true(inherits(res, "response")) expect_is(content(res), "list") expect_named(content(res), "foo") @@ -40,12 +40,12 @@ test_that("crul works", { enable() dat_json <- '{"foo":"bar"}' - stub <- stub_request("get", uri = "https://httpbin.org/get") + stub <- stub_request("get", uri = hb("/get")) to_return(stub, body = dat_json, headers = list("Content-Type" = "application/json; howdy") ) - res <- crul::HttpClient$new("https://httpbin.org")$get("get") + res <- crul::HttpClient$new(hb())$get("get") expect_true(inherits(res, "HttpResponse")) expect_is(res$parse("UTF-8"), "character") expect_is(jsonlite::fromJSON(res$parse("UTF-8")), "list") diff --git a/tests/testthat/test-writing-to-disk-write_disk_path.R b/tests/testthat/test-writing-to-disk-write_disk_path.R index 375c96e..cc65bd0 100644 --- a/tests/testthat/test-writing-to-disk-write_disk_path.R +++ b/tests/testthat/test-writing-to-disk-write_disk_path.R @@ -16,7 +16,7 @@ test_that("with crul", { # path not set expect_error( suppressWarnings(use_cassette("write_disk_path_not_set_crul_error", { - out <- HttpClient$new("https://httpbin.org/get")$get(disk = f) + out <- HttpClient$new(hb("/get"))$get(disk = f) })), "write_disk_path must be given" ) @@ -26,7 +26,7 @@ test_that("with crul", { invisible(vcr_configure(dir = dir, write_disk_path = wdp)) expect_error( use_cassette("write_disk_path_not_set_crul_noerror", { - out <- HttpClient$new("https://httpbin.org/get")$get(disk = f) + out <- HttpClient$new(hb("/get"))$get(disk = f) }), NA ) @@ -55,7 +55,7 @@ test_that("if relative path set its not expanded to full path anymore", { expect_error( use_cassette("write_disk_path_is_relative", { - out <- HttpClient$new("https://httpbin.org/get?foo=foo")$get(disk = f) + out <- HttpClient$new(hb("/get?foo=foo"))$get(disk = f) }), NA ) @@ -86,7 +86,7 @@ test_that("with httr", { # path not set expect_error( suppressWarnings(use_cassette("write_disk_path_not_set_crul_error", { - out <- GET("https://httpbin.org/get", write_disk(f)) + out <- GET(hb("/get"), write_disk(f)) })), "write_disk_path must be given" ) @@ -97,7 +97,7 @@ test_that("with httr", { invisible(vcr_configure(dir = dir, write_disk_path = wdp)) expect_error( use_cassette("write_disk_path_not_set_crul_noerror", { - out <- GET("https://httpbin.org/get", write_disk(f)) + out <- GET(hb("/get"), write_disk(f)) }), NA ) @@ -126,7 +126,7 @@ test_that("if relative path set its not expanded to full path anymore: httr", { expect_error( use_cassette("write_disk_path_is_relative", { - out <- GET("https://httpbin.org/get?foo=foo", write_disk(f)) + out <- GET(hb("/get?foo=foo"), write_disk(f)) }), NA ) diff --git a/tests/testthat/test-writing-to-disk.R b/tests/testthat/test-writing-to-disk.R index 114d797..cc19394 100644 --- a/tests/testthat/test-writing-to-disk.R +++ b/tests/testthat/test-writing-to-disk.R @@ -13,10 +13,10 @@ test_that("Write to a file before mocked request: crul", { expect_is(readLines(f), "character") expect_match(readLines(f), "world") ## make the stub - stub_request("get", "https://httpbin.org/get") %>% + stub_request("get", hb("/get")) %>% to_return(body = file(f)) ## make a request - out <- HttpClient$new("https://httpbin.org/get")$get(disk = f) + out <- HttpClient$new(hb("/get"))$get(disk = f) expect_is(out$content, "character") expect_equal(attr(out$content, "type"), "file") expect_is(readLines(out$content), "character") @@ -38,12 +38,12 @@ test_that("Write to a file before mocked request: httr", { expect_is(readLines(f), "character") expect_match(readLines(f), "world") ## make the stub - stub_request("get", "https://httpbin.org/get") %>% + stub_request("get", hb("/get")) %>% to_return(body = file(f), headers = list('content-type' = "application/json")) ## make a request ## with httr, you must set overwrite=TRUE or you'll get an errror - out <- GET("https://httpbin.org/get", write_disk(f, overwrite=TRUE)) + out <- GET(hb("/get"), write_disk(f, overwrite=TRUE)) content(out) expect_is(out$content, "path") expect_equal(attr(out$content, "class"), "path") @@ -62,10 +62,10 @@ test_that("Use mock_file to have webmockr handle file and contents: crul", { ## make a temp file f <- tempfile(fileext = ".json") ## make the stub - stub_request("get", "https://httpbin.org/get") %>% + stub_request("get", hb("/get")) %>% to_return(body = mock_file(f, "{\"hello\":\"mars\"}\n")) ## make a request - out <- crul::HttpClient$new("https://httpbin.org/get")$get(disk = f) + out <- crul::HttpClient$new(hb("/get"))$get(disk = f) out$content expect_is(out$content, "character") expect_match(out$content, "json") @@ -84,13 +84,13 @@ test_that("Use mock_file to have webmockr handle file and contents: httr", { ## make a temp file f <- tempfile(fileext = ".json") ## make the stub - stub_request("get", "https://httpbin.org/get") %>% + stub_request("get", hb("/get")) %>% to_return( body = mock_file(path = f, payload = "{\"foo\": \"bar\"}"), headers = list('content-type' = "application/json") ) ## make a request - out <- GET("https://httpbin.org/get", write_disk(f)) + out <- GET(hb("/get"), write_disk(f)) ## view stubbed file content expect_is(out$content, "path") expect_match(out$content, "json") diff --git a/tests/testthat/test-zutils.R b/tests/testthat/test-zutils.R index 9cd468d..a9aadf7 100644 --- a/tests/testthat/test-zutils.R +++ b/tests/testthat/test-zutils.R @@ -114,7 +114,7 @@ test_that("webmockr_allow_net_connect", { context("config options: show_stubbing_instructions") test_that("show_stubbing_instructions", { - x = crul::HttpClient$new("https://httpbin.org/get") + x = crul::HttpClient$new("https://hb.opencpu.org/get") # DO show stubbing instructions webmockr_configure(show_stubbing_instructions = TRUE) From ac890882dc0204c88210cdb586431f415376bcd6 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 12:30:14 -0700 Subject: [PATCH 55/66] httr2 done (#125) * started httr2 adapter * bump roxygen version * update man files for new roxygen2 version, bump dev version * remove lazydata true in description * update gh actions * also check windows R-devel * add webmockr_reset to _pkgdown.yml #116 * update news * update license year, bump version, update codemeta.json * update cran comments * working on it, not useable yet * httr2 more work, getting closer * a few tweaks for new httr2 adapter * use https://hb.opencpu.org throughout httr2 tests, comment out ex that uses internal fxn * add change of body response s3 class for httr2 when writing to disk * add more httr2 tests * add httr2 to readme * egs make cmd, run_dontrun * changes to httr2 mocking * tweak tests for httr2 * fixed response headers class for httr2 reponses to match what real httr2 responses return * in Adapter class, set HTTP method if client is httr2 since httr2 doesnt set the method in the request object * fix headers httr2 collection; fix response gathering; fix typo * minor readme udpate * add request to output of the build_httr2_response function * change httr2::local_mock to httr2::local_mocked_responses * add cache slot in httr2 response object * change httr2 resp$response_headers to $headers * fix #123 add _PACKAGE thingy * roxygen2 version * bump version * minor comment change * a few changes for httr2 - change print for Response class - httr2 adapter: fix header representation; improve fetch of status codes, trying different things - httr2 adapter: more failure proof attempts at fetching response body - add some tests * roxygen version * fix req_method_get_w usage in adapter * add more httr2 tests; use base httpbin url from helper file and just use http version * resave test data fixtures to use version 2 to avoid R > 3.5 thingy bajingy * vcr dev version * update revdep checks * take out debugging lines * fix some tests to skip if httr2 not avil * mention httr2 * remove 3 unused fxns * fix revdep ignore in rbuildignore * add some missing topics in pkgdown config file * improve man files * fix #124 document that webmockr supports async with crul * bump to v1.0 * update news, cran comments and codemeta --- .Rbuildignore | 2 +- DESCRIPTION | 7 +- Makefile | 2 +- NAMESPACE | 4 + NEWS.md | 10 + R/HttpLibAdapterRegistry.R | 2 +- R/RequestPattern.R | 27 -- R/Response.R | 8 +- R/adapter-httr.R | 5 +- R/adapter-httr2.R | 135 ++++++++ R/adapter.R | 59 +++- R/flipswitch.R | 20 +- R/mocking-disk-writing.R | 67 +++- R/onload.R | 3 +- R/pluck_body.R | 2 +- R/to_raise.R | 4 +- R/webmockr-opts.R | 1 + R/{webmockr.R => webmockr-package.R} | 29 +- R/wi_th.R | 4 +- README.Rmd | 49 ++- README.md | 117 +++++-- _pkgdown.yml | 4 + codemeta.json | 18 +- cran-comments.md | 8 +- man/Adapter.Rd | 52 ++- man/StubRegistry.Rd | 4 +- man/build_httr2_request.Rd | 17 + man/build_httr2_response.Rd | 38 +++ man/enable.Rd | 10 +- man/httr2_mock.Rd | 17 + man/httr_mock.Rd | 6 +- man/mocking-disk-writing.Rd | 49 ++- man/pluck_body.Rd | 2 +- man/remove_request_stub.Rd | 4 +- man/to_raise.Rd | 4 +- man/to_return.Rd | 4 +- man/webmockr-package.Rd | 32 +- man/wi_th.Rd | 4 +- revdep/README.md | 60 ++-- revdep/cran.md | 9 +- revdep/failures.md | 188 ++--------- revdep/problems.md | 42 ++- tests/testthat/helper-webmockr.R | 2 +- tests/testthat/httr2_obj.rda | Bin 0 -> 192 bytes tests/testthat/httr2_obj_auth.rda | Bin 0 -> 261 bytes tests/testthat/httr_obj.rda | Bin 325 -> 328 bytes tests/testthat/httr_obj_auth.rda | Bin 367 -> 370 bytes tests/testthat/test-HttpLibAdapaterRegistry.R | 14 + tests/testthat/test-Httr2Adapter.R | 314 ++++++++++++++++++ tests/testthat/test-HttrAdapter.R | 7 +- tests/testthat/test-RequestSignature.R | 2 +- tests/testthat/test-Response.R | 9 +- tests/testthat/test-flipswitch.R | 12 +- tests/testthat/test-onload.R | 8 +- tests/testthat/test-stub_registry.R | 2 +- tests/testthat/test-stub_request.R | 2 +- tests/testthat/test-to_return.R | 63 ++++ tests/testthat/test-uri_regex.R | 48 +++ tests/testthat/test-wi_th.R | 25 +- tests/testthat/test-writing-to-disk.R | 57 ++++ 60 files changed, 1320 insertions(+), 374 deletions(-) create mode 100644 R/adapter-httr2.R rename R/{webmockr.R => webmockr-package.R} (61%) create mode 100644 man/build_httr2_request.Rd create mode 100644 man/build_httr2_response.Rd create mode 100644 man/httr2_mock.Rd create mode 100644 tests/testthat/httr2_obj.rda create mode 100644 tests/testthat/httr2_obj_auth.rda create mode 100644 tests/testthat/test-Httr2Adapter.R diff --git a/.Rbuildignore b/.Rbuildignore index a31ab12..f774847 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,7 +11,7 @@ notes.R .github cran-comments.md ^codemeta\.json$ -revdep/ +^revdep$ appveyor.yml ^docs$ write_disk_path-changes.R diff --git a/DESCRIPTION b/DESCRIPTION index 0485e8e..3603f3a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 0.9.0 +Version: 1.0.0 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), @@ -35,8 +35,9 @@ Suggests: testthat, xml2, vcr, - httr -RoxygenNote: 7.2.3 + httr, + httr2 +RoxygenNote: 7.3.2 X-schema.org-applicationCategory: Web X-schema.org-keywords: http, https, API, web-services, curl, mock, mocking, fakeweb, http-mocking, testing, testing-tools, tdd X-schema.org-isPartOf: https://ropensci.org diff --git a/Makefile b/Makefile index 23ccd9f..06c8cd1 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ doc: ${RSCRIPT} -e "devtools::document()" eg: - ${RSCRIPT} -e "devtools::run_examples(run = TRUE)" + ${RSCRIPT} -e "devtools::run_examples(run_dontrun = TRUE)" test: ${RSCRIPT} -e "devtools::test()" diff --git a/NAMESPACE b/NAMESPACE index 4b52e0f..5df9158 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,6 +9,7 @@ export(CrulAdapter) export(HashCounter) export(HeadersPattern) export(HttpLibAdapaterRegistry) +export(Httr2Adapter) export(HttrAdapter) export(MethodPattern) export(RequestPattern) @@ -21,11 +22,14 @@ export(StubbedRequest) export(UriPattern) export(build_crul_request) export(build_crul_response) +export(build_httr2_request) +export(build_httr2_response) export(build_httr_request) export(build_httr_response) export(disable) export(enable) export(enabled) +export(httr2_mock) export(httr_mock) export(mock_file) export(pluck_body) diff --git a/NEWS.md b/NEWS.md index 8cf04a9..69369cf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,12 @@ +webmockr 1.0.0 +============== + +### NEW FEATURES + +* `webmockr` now supports the `httr2` library, in addition to `httr` and `crul`. Note that you'll see different behavior from `httr2` relative to the other 2 http clients because it turns http errors (http statuses 400 and above) into R errors (#122) +* `webmockr` can now mock async http requests with `crul` (w/ `crul` v1.5 or greater). no change was required in `webmockr` for this to happen. a PR was merged in `crul` to hook into `webmockr`. there's no support for async in `httr` as that package does not do any async and no support in `httr2` because `req_perform_parallel` does not have a mocking hook as does `req_perform` (#124) + + webmockr 0.9.0 ============== @@ -15,6 +24,7 @@ webmockr 0.9.0 * `StubCounter` added to pkgdown docs page at (#119) @maelle + webmockr 0.8.2 ============== diff --git a/R/HttpLibAdapterRegistry.R b/R/HttpLibAdapterRegistry.R index 9fa70cf..7d4c13a 100644 --- a/R/HttpLibAdapterRegistry.R +++ b/R/HttpLibAdapterRegistry.R @@ -29,7 +29,7 @@ HttpLibAdapaterRegistry <- R6::R6Class( #' @return nothing, registers the library adapter register = function(x) { # FIXME: when other adapters supported, change this inherits test - if (!inherits(x, c("CrulAdapter", "HttrAdapter"))) { + if (!inherits(x, c("CrulAdapter", "HttrAdapter", "Httr2Adapter"))) { stop("'x' must be an adapter, such as CrulAdapter", call. = FALSE) } self$adapters <- c(self$adapters, x) diff --git a/R/RequestPattern.R b/R/RequestPattern.R index 3f502bd..6d75617 100644 --- a/R/RequestPattern.R +++ b/R/RequestPattern.R @@ -668,30 +668,3 @@ drop_query_params <- function(x) { # prune trailing slash sub("\\/$", "", x) } - -## http method -get_method <- function(x) { - x <- as.character(x) - tmp <- grep( - "(get)$|(post)$|(put)$|(delete)$|(options)$|(patch)$|(head)$", - tolower(x), value = TRUE) - tmp <- sub("httr::", "", tmp) - if (length(tmp) == 0) NULL else tmp -} - -## query and body stuff -get_query <- function(x) { - if ("query" %in% names(x)) { - x[["query"]] - } else { - NULL - } -} - -get_body <- function(x) { - if ("body" %in% names(x)) { - x[["body"]] - } else { - NULL - } -} diff --git a/R/Response.R b/R/Response.R index cd900a3..50fbf4a 100644 --- a/R/Response.R +++ b/R/Response.R @@ -72,14 +72,14 @@ Response <- R6::R6Class( cat(paste0(" url: ", self$url), sep = "\n") cat(paste0(" status: ", self$status_code), sep = "\n") cat(" headers: ", sep = "\n") + cat(" request headers: ", sep = "\n") for (i in seq_along(self$request_headers)) { - cat(" request headers: ", sep = "\n") cat(paste0(" ", paste(names(self$request_headers)[i], self$request_headers[[i]], sep = ": ")), sep = "\n") } + cat(" response headers: ", sep = "\n") for (i in seq_along(self$response_headers)) { - cat(" response headers: ", sep = "\n") cat(paste0(" ", paste(names(self$response_headers)[i], self$response_headers[[i]], sep = ": ")), sep = "\n") @@ -127,8 +127,8 @@ Response <- R6::R6Class( #' @param disk (logical) whether its on disk; default: `FALSE` #' @return nothing returned; sets body on the response set_body = function(body, disk = FALSE) { - self$body <- body - self$content <- if (is.character(body)) { + # self$body <- body + self$body <- self$content <- if (is.character(body)) { stopifnot(length(body) <= 1) if (disk) body else charToRaw(body) } else if (is.raw(body)) { diff --git a/R/adapter-httr.R b/R/adapter-httr.R index 9be1476..64b2d50 100644 --- a/R/adapter-httr.R +++ b/R/adapter-httr.R @@ -96,8 +96,9 @@ build_httr_request = function(x) { ) } -#' Turn on httr mocking -#' Sets a callback that routes httr request through webmockr +#' Turn on `httr` mocking +#' +#' Sets a callback that routes `httr` requests through `webmockr` #' #' @export #' @param on (logical) set to `TRUE` to turn on, and `FALSE` diff --git a/R/adapter-httr2.R b/R/adapter-httr2.R new file mode 100644 index 0000000..f431bb5 --- /dev/null +++ b/R/adapter-httr2.R @@ -0,0 +1,135 @@ +httr2_headers <- function(x) { + structure(x %||% list(), class = "httr2_headers") +} + +tryx <- function(exp, give = NULL) { + z <- tryCatch(exp, error = function(e) e) + if (inherits(z, "error")) give else z +} + +#' Build a httr2 response (`httr2_response`) +#' @export +#' @param req a request +#' @param resp a response +#' @return an httr2 response (`httr2_response`) +#' @examples \dontrun{ +#' # x <- Httr2Adapter$new() +#' # library(httr2) +#' # req <- request("https://r-project.org") +#' # req = req %>% req_body_json(list(x = 1, y = 2)) +#' # #req$method <- 'POST' +#' # stub_request("post", "https://r-project.org") %>% +#' # to_return(status = 418, body = list(a = 5)) +#' # stub = webmockr_stub_registry$request_stubs[[1]] +#' # stub$counter$.__enclos_env__$private$total <- 1 +#' # resp = x$.__enclos_env__$private$build_stub_response(stub) +#' # resp = x$.__enclos_env__$private$build_response(req, resp) +#' # resp = x$.__enclos_env__$private$add_response_sequences(stub, resp) +#' # out +#' # out$body +#' # out$content +#' } +build_httr2_response <- function(req, resp) { + bd <- resp$body %||% resp$content + lst <- list( + method = req_method_get_w(req), + url = tryCatch(resp$url, error = function(e) e) %|s|% req$url, + status_code = as.integer( + tryx(resp$status_code$status_code) %||% + tryx(resp$status_code) %||% + resp$status$status_code + ), + headers = { + if (grepl("^ftp://", resp$url %||% "")) { # in case uri_regex only + httr2_headers(list()) + } else { + httr2_headers(resp$headers %||% resp$response_headers) + } + }, + body = tryx(charToRaw(bd)) %||% bd, + request = req, + cache = new.env() + ) + structure(lst, class = "httr2_response") +} + +req_method_get_w <- function(req) { + if (!is.null(req$method)) { + req$method + } else if ("nobody" %in% names(req$options)) { + "HEAD" + } else if (!is.null(req$body)) { + "POST" + } else { + "GET" + } +} + +#' Build an httr2 request +#' @export +#' @param x an unexecuted httr2 request object +#' @return a `httr2_request` +build_httr2_request = function(x) { + headers <- as.list(x$headers) %||% NULL + auth <- check_user_pwd(x$options$userpwd) %||% NULL + if (!is.null(auth)) { + auth_header <- prep_auth(auth) + headers <- c(headers, auth_header) + } + RequestSignature$new( + method = req_method_get_w(x), + uri = x$url, + options = list( + body = x$body$data, + headers = headers, + proxies = x$proxies %||% NULL, + auth = auth, + disk = x$disk %||% NULL, + fields = x$fields %||% NULL, + output = x$output %||% NULL + ) + ) +} + +#' Turn on `httr2` mocking +#' +#' Sets a callback that routes `httr2` requests through `webmockr` +#' +#' @export +#' @param on (logical) `TRUE` to turn on, `FALSE` to turn off. default: `TRUE` +#' @return Silently returns `TRUE` when enabled and `FALSE` when disabled. +httr2_mock <- function(on = TRUE) { + check_for_pkg("httr2") + if (on) { + httr2::local_mocked_responses(~ Httr2Adapter$new()$handle_request(.x), env = .GlobalEnv) + } else { + httr2::local_mocked_responses(NULL, env = .GlobalEnv) + options(httr2_mock = NULL) + } + invisible(on) +} + +#' @rdname Adapter +#' @export +Httr2Adapter <- R6::R6Class("Httr2Adapter", + inherit = Adapter, + public = list( + #' @field client HTTP client package name + client = "httr2", + #' @field name adapter name + name = "Httr2Adapter" + ), + + private = list( + pluck_url = function(request) request$url, + + mock = function(on) httr2_mock(on), + + build_request = build_httr2_request, + build_response = build_httr2_response, + + request_handler = function(request) vcr::RequestHandlerHttr2$new(request), + + fetch_request = function(request) httr2::req_perform(request) + ) +) diff --git a/R/adapter.R b/R/adapter.R index 6871210..6c28cf8 100644 --- a/R/adapter.R +++ b/R/adapter.R @@ -5,6 +5,7 @@ #' currently provides: #' * `CrulAdapter` for \pkg{crul} #' * `HttrAdapter` for \pkg{httr} +#' * `Httr2Adapter` for \pkg{httr2} #' @details Note that the documented fields and methods are the same across all #' client-specific adapters. #' @export @@ -58,7 +59,8 @@ Adapter <- R6::R6Class("Adapter", "Adapter parent class should not be called directly.\n", "Use one of the following package-specific adapters instead:\n", " - CrulAdapter$new()\n", - " - HttrAdapter$new()", + " - HttrAdapter$new()\n", + " - Httr2Adapter$new()\n", call. = FALSE ) } @@ -71,10 +73,11 @@ Adapter <- R6::R6Class("Adapter", assert(quiet, "logical") if (!quiet) message(sprintf("%s enabled!", self$name)) webmockr_lightswitch[[self$client]] <- TRUE - + switch(self$client, crul = crul::mock(on = TRUE), - httr = httr_mock(on = TRUE) + httr = httr_mock(on = TRUE), + httr2 = httr2_mock(on = TRUE) ) }, @@ -89,7 +92,8 @@ Adapter <- R6::R6Class("Adapter", switch(self$client, crul = crul::mock(on = FALSE), - httr = httr_mock(on = FALSE) + httr = httr_mock(on = FALSE), + httr2 = httr2_mock(on = FALSE) ) }, @@ -98,6 +102,7 @@ Adapter <- R6::R6Class("Adapter", #' @return various outcomes handle_request = function(req) { # put request in request registry + # cat(req) request_signature <- private$build_request(req) webmockr_request_registry$register_request( request = request_signature @@ -108,7 +113,7 @@ Adapter <- R6::R6Class("Adapter", # if real requests NOT allowed # even if net connects allowed, we check if stubbed found first ss <- webmockr_stub_registry$find_stubbed_request(request_signature)[[1]] - + # if user wants to return a partial object # get stub with response and return that resp <- private$build_stub_response(ss) @@ -117,7 +122,6 @@ Adapter <- R6::R6Class("Adapter", # VCR: recordable/ignored if (vcr_cassette_inserted()) { - # req <- handle_separate_redirects(req) # use RequestHandler - gets current cassette & record interaction resp <- private$request_handler(req)$handle() @@ -125,7 +129,7 @@ Adapter <- R6::R6Class("Adapter", if (self$client == "crul" && is.character(resp$content)) { resp <- private$update_vcr_disk_path(resp) } - + # no vcr } else { resp <- private$build_response(req, resp) @@ -145,14 +149,18 @@ Adapter <- R6::R6Class("Adapter", # req <- handle_separate_redirects(req) # use RequestHandler instead? - which gets current cassette for us resp <- private$request_handler(req)$handle() - + # if written to disk, see if we should modify file path if (self$client == "crul" && is.character(resp$content)) { if (file.exists(resp$content)) { resp <- private$update_vcr_disk_path(resp) } } - + + if (self$client == "httr2") { + req$method <- req_method_get_w(req) + } + # stub request so next time we match it req_url <- private$pluck_url(req) urip <- crul::url_parse(req_url) @@ -180,7 +188,7 @@ Adapter <- R6::R6Class("Adapter", resp <- private$fetch_request(req) private$mock(on = TRUE) } - + # request is not in cache and connections are not allowed } else { # throw vcr error: should happen when user not using @@ -247,6 +255,7 @@ Adapter <- R6::R6Class("Adapter", bd_str <- hdl_lst2(bd) } + with_str <- "" if (all(nzchar(hd_str) && nzchar(bd_str))) { with_str <- sprintf(" wi_th(\n headers = list(%s),\n body = list(%s)\n )", hd_str, bd_str) @@ -259,7 +268,7 @@ Adapter <- R6::R6Class("Adapter", tmp <- paste0(tmp, " %>%\n ", with_str) } return(tmp) - }, + }, build_stub_response = function(stub) { stopifnot(inherits(stub, "StubbedRequest")) @@ -275,7 +284,7 @@ Adapter <- R6::R6Class("Adapter", stub_num_get <- length(stub$responses_sequences) } respx <- stub$responses_sequences[[stub_num_get]] - + # if user set to_timeout or to_raise, do that if (!is.null(respx)) { if (respx$timeout || respx$raise) { @@ -293,7 +302,7 @@ Adapter <- R6::R6Class("Adapter", } return(resp) }, - + add_response_sequences = function(stub, response) { # TODO: assert HttpResponse (is it ever a crul response?) stopifnot(inherits(stub, "StubbedRequest")) @@ -331,23 +340,39 @@ Adapter <- R6::R6Class("Adapter", if (self$client == "httr") { class(respx$body_raw) <- "path" } + if (self$client == "httr2") { + class(respx$body_raw) <- "httr2_path" + } } - + body_type <- attr(respx$body_raw, "type") %||% "" + if (self$client == "httr" && body_type == "file") { attr(respx$body_raw, "type") <- NULL class(respx$body_raw) <- "path" } - response$content <- respx$body_raw + + if (self$client == "httr2" && body_type == "file") { + attr(respx$body_raw, "type") <- NULL + class(respx$body_raw) <- "httr2_path" + } + + if (self$client == "httr2") { + response$body <- respx$body_raw + } else { + response$content <- respx$body_raw + } } - + if (names(toadd)[i] == "headers") { headers <- names_to_lower(as_character(toadd[[i]])) if (self$client == "crul") { response$response_headers <- headers response$response_headers_all <- list(headers) - } else { + } else if (self$client == "httr") { response$headers <- httr::insensitive(headers) + } else { # client == "httr2" + response$headers <- httr2_headers(headers) } } } diff --git a/R/flipswitch.R b/R/flipswitch.R index 923de2c..3b2a913 100644 --- a/R/flipswitch.R +++ b/R/flipswitch.R @@ -1,19 +1,21 @@ webmockr_lightswitch <- new.env() webmockr_lightswitch$httr <- FALSE +webmockr_lightswitch$httr2 <- FALSE webmockr_lightswitch$crul <- FALSE -webmockr_adapters <- c('crul', 'httr') +webmockr_adapters <- c('crul', 'httr', 'httr2') #' Enable or disable webmockr #' #' @export -#' @param adapter (character) the adapter name, 'crul' or 'httr'. +#' @param adapter (character) the adapter name, 'crul', 'httr', or 'httr2'. #' one or the other. if none given, we attempt to enable both #' adapters #' @param options list of options - ignored for now. #' @param quiet (logical) suppress messages? default: `FALSE` -#' @details `enable()` enables \pkg{webmockr} for all adapters. -#' `disable()` disables \pkg{webmockr} for all adapters. `enabled()` -#' answers whether \pkg{webmockr} is enabled for a given adapter +#' @details +#' - `enable()` enables \pkg{webmockr} for all adapters +#' - `disable()` disables \pkg{webmockr} for all adapters +#' - `enabled()` answers whether \pkg{webmockr} is enabled for a given adapter #' @return `enable()` and `disable()` invisibly returns booleans for #' each adapter, as a result of running enable or disable, respectively, #' on each [HttpLibAdapaterRegistry] object. `enabled` returns a @@ -22,13 +24,13 @@ enable <- function(adapter = NULL, options = list(), quiet = FALSE) { adnms <- vapply(http_lib_adapter_registry$adapters, function(w) w$client, "") if (!is.null(adapter)) { if (!adapter %in% webmockr_adapters) { - stop("adapter must be one of 'crul' or 'httr'") + stop("adapter must be one of 'crul', 'httr', or 'httr2'") } if (!requireNamespace(adapter, quietly = TRUE)) { message(adapter, " not installed, skipping enable") return(invisible(FALSE)) } - http_lib_adapter_registry$adapters[[grep(adapter, adnms)]]$enable(quiet) + http_lib_adapter_registry$adapters[[which(adnms == adapter)]]$enable(quiet) } else { invisible(vapply(http_lib_adapter_registry$adapters, function(z) { pkgname <- z$client @@ -60,13 +62,13 @@ disable <- function(adapter = NULL, options = list(), quiet = FALSE) { adnms <- vapply(http_lib_adapter_registry$adapters, function(w) w$client, "") if (!is.null(adapter)) { if (!adapter %in% webmockr_adapters) { - stop("adapter must be one of 'crul' or 'httr'") + stop("adapter must be one of 'crul', 'httr', or 'httr2'") } if (!requireNamespace(adapter, quietly = TRUE)) { message(adapter, " not installed, skipping disable") return(invisible(FALSE)) } - http_lib_adapter_registry$adapters[[grep(adapter, adnms)]]$disable(quiet) + http_lib_adapter_registry$adapters[[which(adnms == adapter)]]$disable(quiet) } else { invisible(vapply(http_lib_adapter_registry$adapters, function(z) { pkgname <- z$client diff --git a/R/mocking-disk-writing.R b/R/mocking-disk-writing.R index c1058bc..84554ba 100644 --- a/R/mocking-disk-writing.R +++ b/R/mocking-disk-writing.R @@ -1,12 +1,13 @@ #' Mocking writing to disk -#' +#' #' @name mocking-disk-writing #' @examples \dontrun{ #' # enable mocking #' enable() -#' +#' # getOption('httr2_mock') +#' #' # Write to a file before mocked request -#' +#' #' # crul #' library(crul) #' ## make a temp file @@ -15,13 +16,13 @@ #' cat("{\"hello\":\"world\"}\n", file = f) #' readLines(f) #' ## make the stub -#' stub_request("get", "https://httpbin.org/get") %>% +#' stub_request("get", "https://httpbin.org/get") %>% #' to_return(body = file(f)) #' ## make a request #' (out <- HttpClient$new("https://httpbin.org/get")$get(disk = f)) #' out$content #' readLines(out$content) -#' +#' #' # httr #' library(httr) #' ## make a temp file @@ -30,8 +31,8 @@ #' cat("{\"hello\":\"world\"}\n", file = f) #' readLines(f) #' ## make the stub -#' stub_request("get", "https://httpbin.org/get") %>% -#' to_return(body = file(f), +#' stub_request("get", "https://httpbin.org/get") %>% +#' to_return(body = file(f), #' headers = list('content-type' = "application/json")) #' ## make a request #' ## with httr, you must set overwrite=TRUE or you'll get an errror @@ -39,27 +40,47 @@ #' out #' out$content #' content(out, "text", encoding = "UTF-8") -#' -#' +#' +#' # httr2 +#' library(httr2) +#' ## make a temp file +#' f <- tempfile(fileext = ".json") +#' ## write something to the file +#' cat("{\"hello\":\"world\"}\n", file = f) +#' readLines(f) +#' ## make the stub +#' stub_request("get", "https://httpbin.org/get") %>% +#' to_return(body = file(f), +#' headers = list('content-type' = "application/json")) +#' ## make a request +#' req <- request("https://httpbin.org/get") +#' out <- req_perform(req, path = f) +#' out +#' out$body +#' out +#' out$headers +#' readLines(out$body) +#' +#' #' # Use mock_file to have webmockr handle file and contents -#' +#' #' # crul #' library(crul) #' f <- tempfile(fileext = ".json") #' ## make the stub -#' stub_request("get", "https://httpbin.org/get") %>% +#' stub_request("get", "https://httpbin.org/get") %>% #' to_return(body = mock_file(f, "{\"hello\":\"mars\"}\n")) #' ## make a request #' (out <- crul::HttpClient$new("https://httpbin.org/get")$get(disk = f)) #' out$content #' readLines(out$content) -#' +#' #' # httr #' library(httr) #' ## make a temp file #' f <- tempfile(fileext = ".json") #' ## make the stub -#' stub_request("get", "https://httpbin.org/get") %>% +#' stub_request("get", "https://httpbin.org/get") %>% #' to_return( #' body = mock_file(path = f, payload = "{\"foo\": \"bar\"}"), #' headers = list('content-type' = "application/json") @@ -71,7 +92,25 @@ #' out$content #' readLines(out$content) #' content(out, "text", encoding = "UTF-8") -#' +#' +#' # httr2 +#' library(httr2) +#' ## make a temp file +#' f <- tempfile(fileext = ".json") +#' ## make the stub +#' stub_request("get", "https://httpbin.org/get") %>% +#' to_return( +#' body = mock_file(path = f, payload = "{\"foo\": \"bar\"}"), +#' headers = list('content-type' = "application/json") +#' ) +#' ## make a request +#' req <- request("https://httpbin.org/get") +#' out <- req_perform(req, path = f) +#' out +#' ## view stubbed file content +#' out$body +#' readLines(out$body) +#' #' # disable mocking #' disable() #' } diff --git a/R/onload.R b/R/onload.R index fa9f39a..f5b973a 100644 --- a/R/onload.R +++ b/R/onload.R @@ -6,11 +6,12 @@ webmockr_request_registry <- NULL # set defaults for webmockr webmockr_configure() - # assign crul and httr adapters + # assign crul, httr, and httr2 adapters # which doesn't require those packages loaded yet x <- HttpLibAdapaterRegistry$new() x$register(CrulAdapter$new()) x$register(HttrAdapter$new()) + x$register(Httr2Adapter$new()) http_lib_adapter_registry <<- x # initialize empty stub registry on package load diff --git a/R/pluck_body.R b/R/pluck_body.R index 5646480..e6a5ae9 100644 --- a/R/pluck_body.R +++ b/R/pluck_body.R @@ -4,7 +4,7 @@ #' body based on its encoding. #' #' @export -#' @param x an unexecuted crul *or* httr request object +#' @param x an unexecuted crul, httr *or* httr2 request object #' @return one of the following: #' - `NULL` if the request is not associated with a body #' - `NULL` if an upload is used not in a list diff --git a/R/to_raise.R b/R/to_raise.R index bb48073..7ceb97d 100644 --- a/R/to_raise.R +++ b/R/to_raise.R @@ -15,8 +15,8 @@ #' don't want to raise a stop condition use `to_return()`. Use cases for each #' vary. For example, in a unit test you may have a test expecting a 503 error; #' in this case `to_raise()` makes sense. In another case, if a unit test -#' expects to test some aspect of an HTTP response object that httr or crul -#' typically returns, then you'll want `to_return()`. +#' expects to test some aspect of an HTTP response object that httr, httr2, +#' or crul typically returns, then you'll want `to_return()`. #' #' @details The behavior in the future will be: #' diff --git a/R/webmockr-opts.R b/R/webmockr-opts.R index 6c746b5..92aec8a 100644 --- a/R/webmockr-opts.R +++ b/R/webmockr-opts.R @@ -120,6 +120,7 @@ print.webmockr_config <- function(x, ...) { cat("", sep = "\n") cat(paste0(" crul enabled?: ", webmockr_lightswitch$crul), sep = "\n") cat(paste0(" httr enabled?: ", webmockr_lightswitch$httr), sep = "\n") + cat(paste0(" httr2 enabled?: ", webmockr_lightswitch$httr2), sep = "\n") cat(paste0(" allow_net_connect?: ", x$allow_net_connect), sep = "\n") cat(paste0(" allow_localhost?: ", x$allow_localhost), sep = "\n") cat(paste0(" allow: ", x$allow %||% ""), sep = "\n") diff --git a/R/webmockr.R b/R/webmockr-package.R similarity index 61% rename from R/webmockr.R rename to R/webmockr-package.R index 445083c..4dd522b 100644 --- a/R/webmockr.R +++ b/R/webmockr-package.R @@ -1,29 +1,26 @@ -#' @title webmockr -#' @description Stubbing and setting expectations on HTTP requests -#' -#' @importFrom R6 R6Class -#' @importFrom fauxpas HTTPRequestTimeout -#' @importFrom crul mock -#' @importFrom base64enc base64encode -#' @name webmockr-package -#' @aliases webmockr -#' @docType package -#' @keywords package -#' @author Scott Chamberlain \email{myrmecocystus+r@@gmail.com} -#' @author Aaron Wolen -#' #' @section Features: #' #' - Stubbing HTTP requests at low http client lib level #' - Setting and verifying expectations on HTTP requests #' - Matching requests based on method, URI, headers and body -#' - Supports multiple HTTP libraries, including \pkg{crul} and -#' \pkg{httr} +#' - Supports multiple HTTP libraries, including \pkg{crul}, +#' \pkg{httr}, and \pkg{httr2} #' - Integration with HTTP test caching library \pkg{vcr} +#' - Supports async http request mocking with \pkg{crul} only #' #' @examples #' library(webmockr) #' stub_request("get", "https://httpbin.org/get") #' stub_request("post", "https://httpbin.org/post") #' stub_registry() +#' +#' @keywords internal +"_PACKAGE" + +## usethis namespace: start +#' @importFrom R6 R6Class +#' @importFrom fauxpas HTTPRequestTimeout +#' @importFrom crul mock +#' @importFrom base64enc base64encode +## usethis namespace: end NULL diff --git a/R/wi_th.R b/R/wi_th.R index 231676b..708fec2 100644 --- a/R/wi_th.R +++ b/R/wi_th.R @@ -23,8 +23,8 @@ #' class in the recorded stub. You can pass numeric, integer, etc., but #' all will be coerced to character. #' - body: various, including character string, list, raw, numeric, -#' upload (`crul::upload` or `httr::upload_file`, they both create the -#' same object in the end) +#' upload ([crul::upload()], [httr::upload_file()], [curl::form_file()], or +#' [curl::form_data()] they both create the same object in the end) #' - headers: (list) a named list #' - basic_auth: (character) a length two vector, username and password. #' authentication type (basic/digest/ntlm/etc.) is ignored. that is, diff --git a/README.Rmd b/README.Rmd index f1ef4bd..b9d744d 100644 --- a/README.Rmd +++ b/README.Rmd @@ -37,11 +37,13 @@ Port of the Ruby gem [webmock](https://github.com/bblimke/webmock) * Matching requests based on method, URI, headers and body * Support for `testthat` via [vcr][] * Can be used for testing or outside of a testing context +* Supports async http request mocking with `crul` only ## Supported HTTP libraries * [crul](https://github.com/ropensci/crul) * [httr](https://github.com/r-lib/httr) +* [httr2](https://github.com/r-lib/httr2) ## Install @@ -207,6 +209,51 @@ res$headers #> [1] "teapot" ``` +## httr2 integration + +```{r message=FALSE} +library(webmockr) +library(httr2) + +# turn on httr2 mocking +enable() +``` + +```{r eval=FALSE} +# no stub found +req <- request("https://hb.opencpu.org/get") +req_perform(req) +#> Error: Real HTTP connections are disabled. +#> Unregistered request: +#> GET https://hb.opencpu.org/get +#> +#> You can stub this request with the following snippet: +#> +#> stub_request('get', uri = 'https://hb.opencpu.org/get') +#> ============================================================ +``` + +make a stub + +```{r} +stub_request('get', uri = 'https://hb.opencpu.org/get') %>% + to_return(status = 418, body = "I'm a teapot!!!", headers = list(im_a = "teapot")) +``` + +now returns mocked response + + +```{r eval=FALSE} +req <- request("https://hb.opencpu.org/get") +res <- req_perform(req) +res +res$status_code +#> [1] 418 +res$headers +#> +#> im_a: teapot +``` + ## Writing to disk Write to a file before mocked request @@ -242,7 +289,7 @@ out <- crul::HttpClient$new("https://httpbin.org/get")$get(disk = g) readLines(out$content) ``` -Writing to disk is supported in both `crul` and `httr` +Writing to disk is supported in `crul`, `httr`, and `httr2` ## Many requests in a row diff --git a/README.md b/README.md index 28e3d85..222d446 100644 --- a/README.md +++ b/README.md @@ -99,46 +99,49 @@ yet, but you can allow localhost HTTP requests with the * Matching requests based on method, URI, headers and body * Support for `testthat` via [vcr][] * Can be used for testing or outside of a testing context +* Supports async http request mocking with `crul` only ## Supported HTTP libraries * [crul](https://github.com/ropensci/crul) * [httr](https://github.com/r-lib/httr) +* [httr2](https://github.com/r-lib/httr2) ## Install from cran -```r +``` r install.packages("webmockr") ``` Dev version -```r +``` r remotes::install_github("ropensci/webmockr") ``` -```r +``` r library(webmockr) ``` ## Enable webmockr -```r +``` r webmockr::enable() #> CrulAdapter enabled! #> HttrAdapter enabled! +#> Httr2Adapter enabled! ``` ## Inside a test framework -```r +``` r library(crul) library(testthat) @@ -180,14 +183,14 @@ expect_equal(z$parse("UTF-8"), "success!") ## Outside a test framework -```r +``` r library(crul) ``` ### Stubbed request based on uri only and with the default response -```r +``` r stub_request("get", "https://httpbin.org/get") #> #> method: get @@ -200,13 +203,13 @@ stub_request("get", "https://httpbin.org/get") ``` -```r +``` r x <- HttpClient$new(url = "https://httpbin.org") x$get('get') #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.79.1 r-curl/5.0.0 crul/1.3 +#> User-Agent: libcurl/8.6.0 r-curl/5.2.1 crul/1.5.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -216,7 +219,7 @@ x$get('get') set return objects -```r +``` r stub_request("get", "https://httpbin.org/get") %>% wi_th( query = list(hello = "world")) %>% @@ -237,12 +240,12 @@ stub_request("get", "https://httpbin.org/get") %>% ``` -```r +``` r x$get('get', query = list(hello = "world")) #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.79.1 r-curl/5.0.0 crul/1.3 +#> User-Agent: libcurl/8.6.0 r-curl/5.2.1 crul/1.5.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -252,7 +255,7 @@ x$get('get', query = list(hello = "world")) ### Stubbing requests based on method, uri and query params -```r +``` r stub_request("get", "https://httpbin.org/get") %>% wi_th(query = list(hello = "world"), headers = list('User-Agent' = 'libcurl/7.51.0 r-curl/2.6 crul/0.3.6', @@ -268,7 +271,7 @@ stub_request("get", "https://httpbin.org/get") %>% ``` -```r +``` r stub_registry() #> #> Registered Stubs @@ -278,13 +281,13 @@ stub_registry() ``` -```r +``` r x <- HttpClient$new(url = "https://httpbin.org") x$get('get', query = list(hello = "world")) #> #> url: https://httpbin.org/get #> request_headers: -#> User-Agent: libcurl/7.79.1 r-curl/5.0.0 crul/1.3 +#> User-Agent: libcurl/8.6.0 r-curl/5.2.1 crul/1.5.0 #> Accept-Encoding: gzip, deflate #> Accept: application/json, text/xml, application/xml, */* #> response_headers: @@ -294,7 +297,7 @@ x$get('get', query = list(hello = "world")) ### Stubbing requests and set expectation of a timeout -```r +``` r stub_request("post", "https://httpbin.org/post") %>% to_timeout() #> #> method: post @@ -318,7 +321,7 @@ x$post('post') ### Stubbing requests and set HTTP error expectation -```r +``` r library(fauxpas) stub_request("get", "https://httpbin.org/get?a=b") %>% to_raise(HTTPBadRequest) #> @@ -343,7 +346,7 @@ x$get('get', query = list(a = "b")) ## httr integration -```r +``` r library(webmockr) library(httr) #> @@ -357,7 +360,7 @@ httr_mock() ``` -```r +``` r # no stub found GET("https://httpbin.org/get") #> Error: Real HTTP connections are disabled. @@ -376,7 +379,7 @@ GET("https://httpbin.org/get") make a stub -```r +``` r stub_request('get', uri = 'https://httpbin.org/get') %>% wi_th( headers = list('Accept' = 'application/json, text/xml, application/xml, */*') @@ -401,7 +404,7 @@ now returns mocked response -```r +``` r (res <- GET("https://httpbin.org/get")) res$status_code #> [1] 418 @@ -410,6 +413,68 @@ res$headers #> [1] "teapot" ``` +## httr2 integration + + +``` r +library(webmockr) +library(httr2) + +# turn on httr2 mocking +enable() +``` + + +``` r +# no stub found +req <- request("https://hb.opencpu.org/get") +req_perform(req) +#> Error: Real HTTP connections are disabled. +#> Unregistered request: +#> GET https://hb.opencpu.org/get +#> +#> You can stub this request with the following snippet: +#> +#> stub_request('get', uri = 'https://hb.opencpu.org/get') +#> ============================================================ +``` + +make a stub + + +``` r +stub_request('get', uri = 'https://hb.opencpu.org/get') %>% + to_return(status = 418, body = "I'm a teapot!!!", headers = list(im_a = "teapot")) +#> +#> method: get +#> uri: https://hb.opencpu.org/get +#> with: +#> query: +#> body: +#> request_headers: +#> to_return: +#> - status: 418 +#> body: I'm a teapot!!! +#> response_headers: im_a=teapot +#> should_timeout: FALSE +#> should_raise: FALSE +``` + +now returns mocked response + + + +``` r +req <- request("https://hb.opencpu.org/get") +res <- req_perform(req) +res +res$status_code +#> [1] 418 +res$headers +#> +#> im_a: teapot +``` + ## Writing to disk Write to a file before mocked request @@ -417,7 +482,7 @@ Write to a file before mocked request -```r +``` r ## make a temp file f <- tempfile(fileext = ".json") ## write something to the file @@ -436,7 +501,7 @@ readLines(file(f)) OR - you can use `mock_file()` to have `webmockr` handle file and contents -```r +``` r g <- tempfile(fileext = ".json") ## make the stub invisible(stub_request("get", "https://httpbin.org/get") %>% @@ -447,14 +512,14 @@ readLines(out$content) #> [1] "{\"hello\":\"world\"}" ``` -Writing to disk is supported in both `crul` and `httr` +Writing to disk is supported in `crul`, `httr`, and `httr2` ## Many requests in a row e.g., many redirects, then a final successful request -```r +``` r webmockr::enable() library(crul) library(fauxpas) diff --git a/_pkgdown.yml b/_pkgdown.yml index 2f679b2..4cdc996 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -14,6 +14,7 @@ reference: - starts_with("enable") - disable - httr_mock + - httr2_mock - title: "Stub and Request registries" contents: - stub_registry @@ -39,11 +40,14 @@ reference: contents: - CrulAdapter - HttrAdapter + - Httr2Adapter - HttpLibAdapaterRegistry - build_crul_request - build_crul_response - build_httr_request - build_httr_response + - build_httr2_request + - build_httr2_response - title: "Internal utilities" contents: - RequestPattern diff --git a/codemeta.json b/codemeta.json index f4ebc58..97f8463 100644 --- a/codemeta.json +++ b/codemeta.json @@ -8,13 +8,13 @@ "codeRepository": "https://github.com/ropensci/webmockr", "issueTracker": "https://github.com/ropensci/webmockr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.9.0", + "version": "1.0.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.2.2 (2022-10-31)", + "runtimePlatform": "R version 4.4.1 Patched (2024-06-25 r86831)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -101,6 +101,18 @@ "url": "https://cran.r-project.org" }, "sameAs": "https://CRAN.R-project.org/package=httr" + }, + { + "@type": "SoftwareApplication", + "identifier": "httr2", + "name": "httr2", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=httr2" } ], "softwareRequirements": { @@ -209,5 +221,5 @@ "applicationCategory": "Web", "isPartOf": "https://ropensci.org", "keywords": ["http", "https", "API", "web-services", "curl", "mock", "mocking", "fakeweb", "http-mocking", "testing", "testing-tools", "tdd"], - "fileSize": "352.901KB" + "fileSize": "378.631KB" } diff --git a/cran-comments.md b/cran-comments.md index 8647970..a27591a 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,7 +1,7 @@ ## Test environments -* local macOS install, R 4.2.2 -* ubuntu (on GitHub Actions), R 4.2.2 +* local macOS install, R 4.4.1 +* ubuntu (on GitHub Actions), R 4.4.1 * win-builder (devel and release) ## R CMD check results @@ -10,7 +10,7 @@ ## revdepcheck results -We checked 18 reverse dependencies (13 from CRAN + 5 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. +We checked 18 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. * We saw 0 new problems * We failed to check 0 packages @@ -18,7 +18,7 @@ We checked 18 reverse dependencies (13 from CRAN + 5 from Bioconductor), compari --- -In this version fixes some bugs +This version includes ... Thanks! Scott Chamberlain diff --git a/man/Adapter.Rd b/man/Adapter.Rd index b6276cb..646ac47 100644 --- a/man/Adapter.Rd +++ b/man/Adapter.Rd @@ -1,8 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/adapter-crul.R, R/adapter-httr.R, R/adapter.R +% Please edit documentation in R/adapter-crul.R, R/adapter-httr.R, +% R/adapter-httr2.R, R/adapter.R \name{CrulAdapter} \alias{CrulAdapter} \alias{HttrAdapter} +\alias{Httr2Adapter} \alias{Adapter} \title{Adapters for Modifying HTTP Requests} \description{ @@ -13,6 +15,7 @@ currently provides: \itemize{ \item \code{CrulAdapter} for \pkg{crul} \item \code{HttrAdapter} for \pkg{httr} +\item \code{Httr2Adapter} for \pkg{httr2} } } \details{ @@ -142,6 +145,53 @@ The objects of this class are cloneable with this method. \if{html}{\out{
              }}\preformatted{HttrAdapter$clone(deep = FALSE)}\if{html}{\out{
              }} } +\subsection{Arguments}{ +\if{html}{\out{
              }} +\describe{ +\item{\code{deep}}{Whether to make a deep clone.} +} +\if{html}{\out{
              }} +} +} +} +\section{Super class}{ +\code{\link[webmockr:Adapter]{webmockr::Adapter}} -> \code{Httr2Adapter} +} +\section{Public fields}{ +\if{html}{\out{
              }} +\describe{ +\item{\code{client}}{HTTP client package name} + +\item{\code{name}}{adapter name} +} +\if{html}{\out{
              }} +} +\section{Methods}{ +\subsection{Public methods}{ +\itemize{ +\item \href{#method-Httr2Adapter-clone}{\code{Httr2Adapter$clone()}} +} +} +\if{html}{\out{ +
              Inherited methods + +
              +}} +\if{html}{\out{
              }} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Httr2Adapter-clone}{}}} +\subsection{Method \code{clone()}}{ +The objects of this class are cloneable with this method. +\subsection{Usage}{ +\if{html}{\out{
              }}\preformatted{Httr2Adapter$clone(deep = FALSE)}\if{html}{\out{
              }} +} + \subsection{Arguments}{ \if{html}{\out{
              }} \describe{ diff --git a/man/StubRegistry.Rd b/man/StubRegistry.Rd index d6b1681..21c9d5b 100644 --- a/man/StubRegistry.Rd +++ b/man/StubRegistry.Rd @@ -29,8 +29,8 @@ reg$request_stubs \seealso{ Other stub-registry: \code{\link{remove_request_stub}()}, -\code{\link{stub_registry_clear}()}, -\code{\link{stub_registry}()} +\code{\link{stub_registry}()}, +\code{\link{stub_registry_clear}()} } \concept{stub-registry} \section{Public fields}{ diff --git a/man/build_httr2_request.Rd b/man/build_httr2_request.Rd new file mode 100644 index 0000000..9ed4436 --- /dev/null +++ b/man/build_httr2_request.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/adapter-httr2.R +\name{build_httr2_request} +\alias{build_httr2_request} +\title{Build an httr2 request} +\usage{ +build_httr2_request(x) +} +\arguments{ +\item{x}{an unexecuted httr2 request object} +} +\value{ +a \code{httr2_request} +} +\description{ +Build an httr2 request +} diff --git a/man/build_httr2_response.Rd b/man/build_httr2_response.Rd new file mode 100644 index 0000000..f6b75f7 --- /dev/null +++ b/man/build_httr2_response.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/adapter-httr2.R +\name{build_httr2_response} +\alias{build_httr2_response} +\title{Build a httr2 response (\code{httr2_response})} +\usage{ +build_httr2_response(req, resp) +} +\arguments{ +\item{req}{a request} + +\item{resp}{a response} +} +\value{ +an httr2 response (\code{httr2_response}) +} +\description{ +Build a httr2 response (\code{httr2_response}) +} +\examples{ +\dontrun{ +# x <- Httr2Adapter$new() +# library(httr2) +# req <- request("https://r-project.org") +# req = req \%>\% req_body_json(list(x = 1, y = 2)) +# #req$method <- 'POST' +# stub_request("post", "https://r-project.org") \%>\% +# to_return(status = 418, body = list(a = 5)) +# stub = webmockr_stub_registry$request_stubs[[1]] +# stub$counter$.__enclos_env__$private$total <- 1 +# resp = x$.__enclos_env__$private$build_stub_response(stub) +# resp = x$.__enclos_env__$private$build_response(req, resp) +# resp = x$.__enclos_env__$private$add_response_sequences(stub, resp) +# out +# out$body +# out$content +} +} diff --git a/man/enable.Rd b/man/enable.Rd index ac9f1e5..95fa373 100644 --- a/man/enable.Rd +++ b/man/enable.Rd @@ -13,7 +13,7 @@ enabled(adapter = "crul") disable(adapter = NULL, options = list(), quiet = FALSE) } \arguments{ -\item{adapter}{(character) the adapter name, 'crul' or 'httr'. +\item{adapter}{(character) the adapter name, 'crul', 'httr', or 'httr2'. one or the other. if none given, we attempt to enable both adapters} @@ -31,7 +31,9 @@ single boolean Enable or disable webmockr } \details{ -\code{enable()} enables \pkg{webmockr} for all adapters. -\code{disable()} disables \pkg{webmockr} for all adapters. \code{enabled()} -answers whether \pkg{webmockr} is enabled for a given adapter +\itemize{ +\item \code{enable()} enables \pkg{webmockr} for all adapters +\item \code{disable()} disables \pkg{webmockr} for all adapters +\item \code{enabled()} answers whether \pkg{webmockr} is enabled for a given adapter +} } diff --git a/man/httr2_mock.Rd b/man/httr2_mock.Rd new file mode 100644 index 0000000..45691c3 --- /dev/null +++ b/man/httr2_mock.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/adapter-httr2.R +\name{httr2_mock} +\alias{httr2_mock} +\title{Turn on \code{httr2} mocking} +\usage{ +httr2_mock(on = TRUE) +} +\arguments{ +\item{on}{(logical) \code{TRUE} to turn on, \code{FALSE} to turn off. default: \code{TRUE}} +} +\value{ +Silently returns \code{TRUE} when enabled and \code{FALSE} when disabled. +} +\description{ +Sets a callback that routes \code{httr2} requests through \code{webmockr} +} diff --git a/man/httr_mock.Rd b/man/httr_mock.Rd index b906a21..966fb05 100644 --- a/man/httr_mock.Rd +++ b/man/httr_mock.Rd @@ -2,8 +2,7 @@ % Please edit documentation in R/adapter-httr.R \name{httr_mock} \alias{httr_mock} -\title{Turn on httr mocking -Sets a callback that routes httr request through webmockr} +\title{Turn on \code{httr} mocking} \usage{ httr_mock(on = TRUE) } @@ -15,6 +14,5 @@ to turn off. default: \code{TRUE}} Silently returns \code{TRUE} when enabled and \code{FALSE} when disabled. } \description{ -Turn on httr mocking -Sets a callback that routes httr request through webmockr +Sets a callback that routes \code{httr} requests through \code{webmockr} } diff --git a/man/mocking-disk-writing.Rd b/man/mocking-disk-writing.Rd index e23b045..dd8ffda 100644 --- a/man/mocking-disk-writing.Rd +++ b/man/mocking-disk-writing.Rd @@ -10,6 +10,7 @@ Mocking writing to disk \dontrun{ # enable mocking enable() +# getOption('httr2_mock') # Write to a file before mocked request @@ -21,7 +22,7 @@ f <- tempfile(fileext = ".json") cat("{\"hello\":\"world\"}\n", file = f) readLines(f) ## make the stub -stub_request("get", "https://httpbin.org/get") \%>\% +stub_request("get", "https://httpbin.org/get") \%>\% to_return(body = file(f)) ## make a request (out <- HttpClient$new("https://httpbin.org/get")$get(disk = f)) @@ -36,8 +37,8 @@ f <- tempfile(fileext = ".json") cat("{\"hello\":\"world\"}\n", file = f) readLines(f) ## make the stub -stub_request("get", "https://httpbin.org/get") \%>\% - to_return(body = file(f), +stub_request("get", "https://httpbin.org/get") \%>\% + to_return(body = file(f), headers = list('content-type' = "application/json")) ## make a request ## with httr, you must set overwrite=TRUE or you'll get an errror @@ -46,6 +47,26 @@ out out$content content(out, "text", encoding = "UTF-8") +# httr2 +library(httr2) +## make a temp file +f <- tempfile(fileext = ".json") +## write something to the file +cat("{\"hello\":\"world\"}\n", file = f) +readLines(f) +## make the stub +stub_request("get", "https://httpbin.org/get") \%>\% + to_return(body = file(f), + headers = list('content-type' = "application/json")) +## make a request +req <- request("https://httpbin.org/get") +out <- req_perform(req, path = f) +out +out$body +out +out$headers +readLines(out$body) + # Use mock_file to have webmockr handle file and contents @@ -53,7 +74,7 @@ content(out, "text", encoding = "UTF-8") library(crul) f <- tempfile(fileext = ".json") ## make the stub -stub_request("get", "https://httpbin.org/get") \%>\% +stub_request("get", "https://httpbin.org/get") \%>\% to_return(body = mock_file(f, "{\"hello\":\"mars\"}\n")) ## make a request (out <- crul::HttpClient$new("https://httpbin.org/get")$get(disk = f)) @@ -65,7 +86,7 @@ library(httr) ## make a temp file f <- tempfile(fileext = ".json") ## make the stub -stub_request("get", "https://httpbin.org/get") \%>\% +stub_request("get", "https://httpbin.org/get") \%>\% to_return( body = mock_file(path = f, payload = "{\"foo\": \"bar\"}"), headers = list('content-type' = "application/json") @@ -78,6 +99,24 @@ out$content readLines(out$content) content(out, "text", encoding = "UTF-8") +# httr2 +library(httr2) +## make a temp file +f <- tempfile(fileext = ".json") +## make the stub +stub_request("get", "https://httpbin.org/get") \%>\% + to_return( + body = mock_file(path = f, payload = "{\"foo\": \"bar\"}"), + headers = list('content-type' = "application/json") + ) +## make a request +req <- request("https://httpbin.org/get") +out <- req_perform(req, path = f) +out +## view stubbed file content +out$body +readLines(out$body) + # disable mocking disable() } diff --git a/man/pluck_body.Rd b/man/pluck_body.Rd index 08f48d4..f35d43e 100644 --- a/man/pluck_body.Rd +++ b/man/pluck_body.Rd @@ -7,7 +7,7 @@ pluck_body(x) } \arguments{ -\item{x}{an unexecuted crul \emph{or} httr request object} +\item{x}{an unexecuted crul, httr \emph{or} httr2 request object} } \value{ one of the following: diff --git a/man/remove_request_stub.Rd b/man/remove_request_stub.Rd index 0550515..1f1ba5c 100644 --- a/man/remove_request_stub.Rd +++ b/man/remove_request_stub.Rd @@ -24,7 +24,7 @@ stub_registry() \seealso{ Other stub-registry: \code{\link{StubRegistry}}, -\code{\link{stub_registry_clear}()}, -\code{\link{stub_registry}()} +\code{\link{stub_registry}()}, +\code{\link{stub_registry_clear}()} } \concept{stub-registry} diff --git a/man/to_raise.Rd b/man/to_raise.Rd index 0f6a0e4..e02643a 100644 --- a/man/to_raise.Rd +++ b/man/to_raise.Rd @@ -41,7 +41,7 @@ raise a stop condition then \code{to_raise()} is what you want. But if you don't want to raise a stop condition use \code{to_return()}. Use cases for each vary. For example, in a unit test you may have a test expecting a 503 error; in this case \code{to_raise()} makes sense. In another case, if a unit test -expects to test some aspect of an HTTP response object that httr or crul -typically returns, then you'll want \code{to_return()}. +expects to test some aspect of an HTTP response object that httr, httr2, +or crul typically returns, then you'll want \code{to_return()}. } diff --git a/man/to_return.Rd b/man/to_return.Rd index ab8cdcf..5dd887d 100644 --- a/man/to_return.Rd +++ b/man/to_return.Rd @@ -74,8 +74,8 @@ raise a stop condition then \code{to_raise()} is what you want. But if you don't want to raise a stop condition use \code{to_return()}. Use cases for each vary. For example, in a unit test you may have a test expecting a 503 error; in this case \code{to_raise()} makes sense. In another case, if a unit test -expects to test some aspect of an HTTP response object that httr or crul -typically returns, then you'll want \code{to_return()}. +expects to test some aspect of an HTTP response object that httr, httr2, +or crul typically returns, then you'll want \code{to_return()}. } \examples{ diff --git a/man/webmockr-package.Rd b/man/webmockr-package.Rd index 6f251cf..259aa81 100644 --- a/man/webmockr-package.Rd +++ b/man/webmockr-package.Rd @@ -1,12 +1,12 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/webmockr.R +% Please edit documentation in R/webmockr-package.R \docType{package} \name{webmockr-package} -\alias{webmockr-package} \alias{webmockr} -\title{webmockr} +\alias{webmockr-package} +\title{webmockr: Stubbing and Setting Expectations on 'HTTP' Requests} \description{ -Stubbing and setting expectations on HTTP requests +Stubbing and setting expectations on 'HTTP' requests. Includes tools for stubbing 'HTTP' requests, including expected request conditions and response conditions. Match on 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. } \section{Features}{ @@ -14,8 +14,8 @@ Stubbing and setting expectations on HTTP requests \item Stubbing HTTP requests at low http client lib level \item Setting and verifying expectations on HTTP requests \item Matching requests based on method, URI, headers and body -\item Supports multiple HTTP libraries, including \pkg{crul} and -\pkg{httr} +\item Supports multiple HTTP libraries, including \pkg{crul}, +\pkg{httr}, and \pkg{httr2} \item Integration with HTTP test caching library \pkg{vcr} } } @@ -25,10 +25,24 @@ library(webmockr) stub_request("get", "https://httpbin.org/get") stub_request("post", "https://httpbin.org/post") stub_registry() + +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/ropensci/webmockr (devel) https://books.ropensci.org/http-testing/ (user manual) https://docs.ropensci.org/webmockr/ (documentation)} + \item Report bugs at \url{https://github.com/ropensci/webmockr/issues} +} + } \author{ -Scott Chamberlain \email{myrmecocystus+r@gmail.com} +\strong{Maintainer}: Scott Chamberlain \email{myrmecocystus+r@gmail.com} (\href{https://orcid.org/0000-0003-1444-9135}{ORCID}) + +Other contributors: +\itemize{ + \item Aaron Wolen (\href{https://orcid.org/0000-0003-2542-2202}{ORCID}) [contributor] + \item rOpenSci (https://ropensci.org) [funder] +} -Aaron Wolen } -\keyword{package} +\keyword{internal} diff --git a/man/wi_th.Rd b/man/wi_th.Rd index 94028dd..d72c2e3 100644 --- a/man/wi_th.Rd +++ b/man/wi_th.Rd @@ -35,8 +35,8 @@ Values for query, body, headers, and basic_auth: class in the recorded stub. You can pass numeric, integer, etc., but all will be coerced to character. \item body: various, including character string, list, raw, numeric, -upload (\code{crul::upload} or \code{httr::upload_file}, they both create the -same object in the end) +upload (\code{\link[crul:upload]{crul::upload()}}, \code{\link[httr:upload_file]{httr::upload_file()}}, \code{\link[curl:multipart]{curl::form_file()}}, or +\code{\link[curl:multipart]{curl::form_data()}} they both create the same object in the end) \item headers: (list) a named list \item basic_auth: (character) a length two vector, username and password. authentication type (basic/digest/ntlm/etc.) is ignored. that is, diff --git a/revdep/README.md b/revdep/README.md index 6df90ee..fd33408 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -1,38 +1,42 @@ # Platform -|field |value | -|:--------|:--------------------------------| -|version |R version 4.2.2 (2022-10-31) | -|os |macOS Monterey 12.6.3 | -|system |aarch64, darwin21.6.0 | -|ui |unknown | -|language |(EN) | -|collate |en_US.UTF-8 | -|ctype |en_US.UTF-8 | -|tz |America/Los_Angeles | -|date |2023-02-08 | -|pandoc |3.0.1 @ /opt/homebrew/bin/pandoc | +|field |value | +|:--------|:-------------------------------------------| +|version |R version 4.4.1 Patched (2024-06-25 r86831) | +|os |macOS Sonoma 14.5 | +|system |aarch64, darwin20 | +|ui |X11 | +|language |(EN) | +|collate |en_US.UTF-8 | +|ctype |en_US.UTF-8 | +|tz |America/Los_Angeles | +|date |2024-07-15 | +|pandoc |3.2.1 @ /opt/homebrew/bin/pandoc | # Dependencies -|package |old |new |Δ | -|:--------|:-----|:------|:--| -|webmockr |0.8.2 |0.9.0 |* | -|crul |NA |1.3 |* | -|curl |NA |5.0.0 |* | -|jsonlite |NA |1.8.4 |* | -|Rcpp |NA |1.0.10 |* | -|whisker |NA |0.4.1 |* | +|package |old |new |Δ | +|:---------|:------|:--------|:--| +|webmockr |0.9.0 |0.9.1.91 |* | +|base64enc |0.1-3 |0.1-3 | | +|crul |1.4.2 |1.4.2 | | +|curl |5.2.1 |5.2.1 | | +|fauxpas |0.5.2 |0.5.2 | | +|httpcode |0.3.0 |0.3.0 | | +|jsonlite |1.8.8 |1.8.8 | | +|magrittr |2.0.3 |2.0.3 | | +|mime |0.12 |0.12 | | +|R6 |2.5.1 |2.5.1 | | +|Rcpp |1.0.12 |1.0.12 | | +|triebeard |0.4.1 |0.4.1 | | +|urltools |1.7.3 |1.7.3 | | +|whisker |0.4.1 |0.4.1 | | # Revdeps -## Failed to check (5) +## Failed to check (1) -|package |version |error |warning |note | -|:----------|:-------|:-----|:-------|:----| -|biomaRt |? | | | | -|magmaR |? | | | | -|riskmetric |? | | | | -|rnoaa |? | | | | -|RTD |? | | | | +|package |version |error |warning |note | +|:----------|:-------|:---------|:-------|:----| +|[riskmetric](failures.md#riskmetric)|0.2.4 |-1 __+1__ | | | diff --git a/revdep/cran.md b/revdep/cran.md index e77d64f..53a05e4 100644 --- a/revdep/cran.md +++ b/revdep/cran.md @@ -1,7 +1,12 @@ ## revdepcheck results -We checked 18 reverse dependencies (13 from CRAN + 5 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. +We checked 18 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. * We saw 0 new problems - * We failed to check 0 packages + * We failed to check 1 packages +Issues with CRAN packages are summarised below. + +### Failed to check + +* riskmetric (NA) diff --git a/revdep/failures.md b/revdep/failures.md index 4d780e9..ff8b987 100644 --- a/revdep/failures.md +++ b/revdep/failures.md @@ -1,165 +1,41 @@ -# biomaRt - -
              - -* Version: -* GitHub: https://github.com/ropensci/webmockr -* Source code: NA -* Number of recursive dependencies: 0 - -
              - -## Error before installation - -### Devel - -``` - - - - - - -``` -### CRAN - -``` - - - - - - -``` -# magmaR - -
              - -* Version: -* GitHub: https://github.com/ropensci/webmockr -* Source code: NA -* Number of recursive dependencies: 0 - -
              - -## Error before installation - -### Devel - -``` - - - - - - -``` -### CRAN - -``` - - - - - - -``` # riskmetric
              -* Version: -* GitHub: https://github.com/ropensci/webmockr -* Source code: NA -* Number of recursive dependencies: 0 - -
              - -## Error before installation - -### Devel - -``` - - +* Version: 0.2.4 +* GitHub: https://github.com/pharmaR/riskmetric +* Source code: https://github.com/cran/riskmetric +* Date/Publication: 2024-01-09 15:50:02 UTC +* Number of recursive dependencies: 111 - - - -``` -### CRAN - -``` - - - - - - -``` -# rnoaa - -
              - -* Version: -* GitHub: https://github.com/ropensci/webmockr -* Source code: NA -* Number of recursive dependencies: 0 - -
              - -## Error before installation - -### Devel - -``` - - - - - - -``` -### CRAN - -``` - - - - - - -``` -# RTD - -
              - -* Version: -* GitHub: https://github.com/ropensci/webmockr -* Source code: NA -* Number of recursive dependencies: 0 +Run `revdepcheck::revdep_details(, "riskmetric")` for more info
              -## Error before installation - -### Devel - -``` - - - - - - -``` -### CRAN - -``` - - - - - +## Newly broken + +* R CMD check timed out + + +## Newly fixed + +* checking running R code from vignettes ... + ``` + ‘extending-riskmetric.Rmd’ using ‘UTF-8’... OK + ‘riskmetric.Rmd’ using ‘UTF-8’... failed + ERROR + Errors in running code in vignettes: + when running code in ‘riskmetric.Rmd’ + ... + > options(repos = "https://cran.rstudio.com") + + > knitr::opts_chunk$set(collapse = TRUE, comment = "#>", + + fig.path = "man/figures/") + + > knitr::include_graphics("../man/figures/core-workflow.svg") + + When sourcing ‘riskmetric.R’: + Error: Cannot find the file(s): "../man/figures/core-workflow.svg" + Execution halted + ``` -``` diff --git a/revdep/problems.md b/revdep/problems.md index 9a20736..ff8b987 100644 --- a/revdep/problems.md +++ b/revdep/problems.md @@ -1 +1,41 @@ -*Wow, no problems at all. :)* \ No newline at end of file +# riskmetric + +
              + +* Version: 0.2.4 +* GitHub: https://github.com/pharmaR/riskmetric +* Source code: https://github.com/cran/riskmetric +* Date/Publication: 2024-01-09 15:50:02 UTC +* Number of recursive dependencies: 111 + +Run `revdepcheck::revdep_details(, "riskmetric")` for more info + +
              + +## Newly broken + +* R CMD check timed out + + +## Newly fixed + +* checking running R code from vignettes ... + ``` + ‘extending-riskmetric.Rmd’ using ‘UTF-8’... OK + ‘riskmetric.Rmd’ using ‘UTF-8’... failed + ERROR + Errors in running code in vignettes: + when running code in ‘riskmetric.Rmd’ + ... + > options(repos = "https://cran.rstudio.com") + + > knitr::opts_chunk$set(collapse = TRUE, comment = "#>", + + fig.path = "man/figures/") + + > knitr::include_graphics("../man/figures/core-workflow.svg") + + When sourcing ‘riskmetric.R’: + Error: Cannot find the file(s): "../man/figures/core-workflow.svg" + Execution halted + ``` + diff --git a/tests/testthat/helper-webmockr.R b/tests/testthat/helper-webmockr.R index 84b5dfe..4c85d50 100644 --- a/tests/testthat/helper-webmockr.R +++ b/tests/testthat/helper-webmockr.R @@ -17,5 +17,5 @@ re_escape <- function(strings){ strings } -base_url = "https://hb.opencpu.org" +base_url = "http://hb.opencpu.org" hb <- function(x = NULL) if (is.null(x)) base_url else paste0(base_url, x) diff --git a/tests/testthat/httr2_obj.rda b/tests/testthat/httr2_obj.rda new file mode 100644 index 0000000000000000000000000000000000000000..0b134d3cddcf1728d622d2bb29d5abdb5af90baa GIT binary patch literal 192 zcmV;x06+g9iwFP!0000016@!-4uUWcT%cMZF){JvCm`YI*%x@?W`M3xQ)q3=!Os&* zY!l(IyEC)XOuF}5Rc2KN05K2}L7X^9V%H)dkh+=n*6Q*hn-@TGb^>*E`ZHXdL0yZY zZ*r+{Y}J%Y-4z{Lx13!qk6?p0bCtLo>j8~VpjO$;NyXF8%s1D80_=2-+OmYetBFi#;Y*^WWd-6vYd`AqiqBX&Z%DN5Zq#o)!UkP zK?CFyx-Dp3{b5asW$(&TS97$g6}FqB+m;pgM$DqF+#nKk#Qmmblb!a^UVz(7vVnYJ4BZh93>%KNA ziESKPw0|COoVaQ;yd>{@zI*rb@j9O+^8_IjpeX90Ahe%RU>`jcp@Ds$XIiU;SZ)yt zhqjMxT^*f=?{A+S=-OV9&xB-4EF^Fy4HjxcHlVG|F>ykrEOJJ5E;!j%f{#-TJ56?F zF;0)q955oI&TM^7OQ<^TXD^I_v{vZ1sbJ{how9C<86X9caQj@ZSI$~7!XD23X$ z*kyyQ4QKT(8p)GUVxPPOor#tIF*eX)B%mu$mFKN(g%)4HJzW?r ajc!asZ|ZZz3cd|ghq(XhBuo%e0ssKm$dzFL literal 325 zcmV-L0lNMliwFP!0000016@%)Ps1<}HAzcogb-3UrVLC~i3iT$3HTFZ>*TZF=? z%_AF^2j}72>&F1PvR8z9!nn*+E`U*CQZ6@m1KR2wG8aUMx}ro^oZ+qFY@TS?X}oLd zd2)E>fEk_*nj16HfEvi3K2r+9N}=B_f~oU&%(||toEll<$MoSoT_p0xWy^G#w2tv@ zYEjFJ-5nRLbnw^L3Hmw{qd<}kFx>-D>)u)Q`fhH+xsGW~lo}K}|CW`8tbvBhpB}sD zu(e^d9Yt62sFlbYZ$Ov4@DF3{>{|j-fK;BhuAnY_7ID$m^=V9uE;G$Pfq6PNS{U7l XhF;YBh$MU&Pl diff --git a/tests/testthat/httr_obj_auth.rda b/tests/testthat/httr_obj_auth.rda index 05026045a022851f4032823ab3dd11e7706406ae..5626ceb8f8ddf8fe510494e93ceca6024dc334f8 100644 GIT binary patch literal 370 zcmV-&0ge72iwFP!0000016@&FPlGTN?SP3Rx|sL}_F`g|jc$uy*^4pJ_-=gI8!6r0 zU;?eB&E~)M7pN`J1tBTzJw5l{b5Gx%SBqd3AcP#`c|GK~`sg|O*Fzo}=>0sAQba7? zMMTL2A@@>)zFrqClKcAd-hr-l9$#Xd#3AEQP_9BIwm2qbN~fk2-Y}*^p0M3K2Gynhq literal 367 zcmV-#0g(P5iwFP!0000016@(wPJ=KQt-!<)T}*rdF1&Hs=(fz5?8caAyfzSmcwidsYKuJnjuVC`kcTZ<_NbN`Xtg`)QVAWOzN+ZJs4mi5x#f z_seJ&ifJ7!qeW<3!1HJs%{6aiavr~teY!P#t!h=PLO~7J%S`C}wp6dB4pjw$?4Xn_ zaA7>FZEkBB0zYhQXpVQ#QBhw;|HcM1CzUn8X~D~b$RxxC6ij@z*r>s_s-(uyrsTg# zeQRF% req_perform())) + expect_s3_class(x, "httr2_response") + + # # works when empty cassette is loaded + vcr::vcr_configure(dir = tempdir()) + vcr::insert_cassette("empty") + expect_silent((x <- request(hb("/get")) %>% req_perform())) + vcr::eject_cassette("empty") + expect_s3_class(x, "httr2_response") +}) + + +context("Httr2Adapter: date slot") +test_that("Httr2Adapter date slot works", { + skip_on_cran() + skip_if_not_installed("vcr") + library("vcr") + + path <- file.path(tempdir(), "foobar") + vcr::vcr_configure(dir = path) + vcr::use_cassette("test-date", request(hb("/get")) %>% req_perform()) + # list.files(path) + # readLines(file.path(path, "test-date.yml")) + vcr::insert_cassette("test-date") + + x <- request(hb("/get")) %>% req_perform() + + # $headers$date is a different format + expect_is(x$headers$date, "character") + expect_error(format(x$headers$date, "%Y-%m-%d %H:%M"), "invalid 'trim'") + + vcr::eject_cassette("test-date") + + # cleanup + unlink(path, recursive = TRUE) +}) + + +# library(httr2) +# z <- request(hb("/get")) %>% req_perform() +# httr2_obj <- z$request +# save(httr2_obj, file = "tests/testthat/httr2_obj.rda", version = 2) + +context("Httr2Adapter: works with real data") +test_that("Httr2Adapter works", { + skip_on_cran() + skip_if_not_installed("vcr") + + load("httr2_obj.rda") + # load("tests/testthat/httr2_obj.rda") + res <- Httr2Adapter$new() + + # with vcr message + library("vcr") + expect_error( + res$handle_request(httr2_obj), + "There is currently no cassette in use" + ) + + # with webmockr message + # unload vcr + unloadNamespace("vcr") + expect_error( + res$handle_request(httr2_obj), + sprintf("Real HTTP connections are disabled.\nUnregistered request:\n GET: %s", hb("/get")) + ) + + invisible(stub_request("get", hb("/get"))) + + aa <- res$handle_request(httr2_obj) + + expect_is(res, "Httr2Adapter") + expect_is(aa, "httr2_response") + expect_null(aa$request$method) + expect_equal(aa$url, hb("/get")) + + # no response headers + expect_equal(length(aa$headers), 0) + + # with headers + # clear registry + stub_registry_clear() + + # stub with headers + x <- stub_request("get", hb("/get")) + x <- to_return(x, headers = list("User-Agent" = "foo-bar")) + + aa <- res$handle_request(httr2_obj) + + expect_is(res, "Httr2Adapter") + expect_is(aa, "httr2_response") + expect_null(aa$request$method) + expect_equal(aa$url, hb("/get")) + + # has headers and all_headers + expect_equal(length(aa$headers), 1) + expect_s3_class(aa$headers, "httr2_headers") + expect_named(aa$headers, "user-agent") + + # stub with redirect headers + my_url <- "https://doi.org/10.1007/978-3-642-40455-9_52-1" + x <- stub_request("get", my_url) + x <- to_return(x, status = 302, headers = + list( + status = 302, + location = "http://link.springer.com/10.1007/978-3-642-40455-9_52-1" + ) + ) + + httr2_obj$url <- my_url + res <- Httr2Adapter$new() + aa <- res$handle_request(httr2_obj) + + expect_null(aa$request$method) + expect_equal(aa$url, my_url) + expect_equal(aa$status_code, 302) + + # has headers and all_headers + expect_equal(length(aa$headers), 2) + expect_s3_class(aa$headers, "httr2_headers") + expect_equal(sort(names(aa$headers)), c("location", "status")) +}) + +test_that("Httr2Adapter works with req_auth_basic", { + skip_on_cran() + + unloadNamespace("vcr") + httr_mock() + # httr_mock(FALSE) + # webmockr_allow_net_connect() + stub_registry_clear() + # stub_registry() + # request_registry() + z <- stub_request("get", uri = hb("/basic-auth/foo/bar")) %>% + to_return( + body = list(foo = "bar"), + headers = list("Content-Type" = "application/json") + ) + + # mocked httr2 requests with auth work + x <- request(hb("/basic-auth/foo/bar")) %>% + req_auth_basic("foo", "bar") %>% + req_perform() + expect_is(x, "httr2_response") + expect_equal( + jsonlite::fromJSON(rawToChar(x$body)), + list(authenticated = TRUE, user = "foo") + ) + expect_s3_class(x$headers, "httr2_headers") + expect_equal(x$status_code, 200) + + # Httr2Adapter works on requests with auth + # x <- request(hb("/basic-auth/foo/bar")) %>% + # req_auth_basic("foo", "bar") %>% + # req_perform() + # httr2_obj_auth <- x$request + # save(httr2_obj_auth, file = "tests/testthat/httr2_obj_auth.rda", version = 2) + # load("tests/testthat/httr2_obj_auth.rda") + load("httr2_obj_auth.rda") + zz <- Httr2Adapter$new() + z <- zz$handle_request(httr2_obj_auth) + expect_is(z, "httr2_response") + expect_equal( + jsonlite::fromJSON(rawToChar(z$body)), + list(foo = "bar") + ) + expect_s3_class(z$headers, "httr2_headers") + expect_equal(z$status_code, 200) +}) + +test_that("httr2 works with webmockr_allow_net_connect", { + skip_on_cran() + + unloadNamespace("vcr") + + enable() + stub_registry_clear() + z <- stub_request("get", uri = hb("/get")) %>% + wi_th(query = list(stuff = "things")) %>% + to_return(body = "yum=cheese") + req <- request(hb("/get")) %>% req_url_query(stuff = "things") + x <- req_perform(req) + expect_true(resp_body_string(x) == "yum=cheese") + + # disable net connect - now real requests can't be made + webmockr_disable_net_connect() + stub_registry_clear() + expect_error(req_perform(req), + "Real HTTP connections are disabled") + + # allow net connect - stub still exists though - so not a real request + webmockr_allow_net_connect() + z <- stub_request("get", uri = hb("/get")) %>% + wi_th(query = list(stuff = "things")) %>% + to_return(body = "yum=cheese") + req <- request(hb("/get")) %>% req_url_query(stuff = "things") + z <- req_perform(req) + expect_true(resp_body_string(z) == "yum=cheese") + + # allow net connect - stub now gone - so real request should happen + stub_registry_clear() + req <- request(hb("/get")) %>% req_url_query(stuff = "things") + httr2::local_mocked_responses(NULL) + w <- req_perform(req) + expect_false(resp_body_string(w) == "yum=cheese") +}) + +test_that("httr2 requests with bodies work", { + skip_on_cran() + + enable() + stub_registry_clear() + z <- stub_request("post", uri = hb("/post")) %>% + to_return(body = "asdffsdsdf") + req <- request(hb("/post")) %>% + req_body_json(list(stuff = "things")) + x <- req_perform(req) + expect_true(httr2::resp_body_string(x) == "asdffsdsdf") + + # now with allow net connect + stub_registry_clear() + httr2_mock(FALSE) + webmockr_allow_net_connect() + req <- request(hb("/post")) %>% + req_body_json(list(stuff = "things")) + x <- req_perform(req) + expect_identical(httr2::resp_body_json(x)$json, list(stuff = "things")) + + webmockr_disable_net_connect() +}) + +disable() + +test_that("httr2 requests with nested list bodies work", { + skip_on_cran() + + enable() + # httr_mock() + stub_registry_clear() + body = list(id = ' ', method = 'x', params = list(pwd = 'p', user = 'a')) + z <- stub_request("post", uri = hb("/post")) %>% + wi_th(body = body) %>% + to_return(body = "asdffsdsdf") + x <- request(hb("/post")) %>% + req_body_json(body) %>% + req_perform() + expect_true(rawToChar(x$body) == "asdffsdsdf") + + # now with allow net connect + stub_registry_clear() + webmockr_allow_net_connect() + response_real <- request(hb("/post")) %>% + req_body_json(body) %>% + req_perform() + expect_equal( + jsonlite::fromJSON(rawToChar(response_real$body))$json, + body) + + webmockr_disable_net_connect() +}) diff --git a/tests/testthat/test-HttrAdapter.R b/tests/testthat/test-HttrAdapter.R index 1ee641b..a25e054 100644 --- a/tests/testthat/test-HttrAdapter.R +++ b/tests/testthat/test-HttrAdapter.R @@ -66,7 +66,7 @@ test_that("HttrAdapter: works when vcr is loaded but no cassette is inserted", { # library(httr) # z <- GET(hb("/get")) # httr_obj <- z$request -# save(httr_obj, file = "tests/testthat/httr_obj.rda") +# save(httr_obj, file = "tests/testthat/httr_obj.rda", version = 2) context("HttrAdapter: date slot") test_that("HttrAdapter date slot works", { @@ -167,7 +167,7 @@ test_that("HttrAdapter works", { unloadNamespace("vcr") expect_error( res$handle_request(httr_obj), - "Real HTTP connections are disabled.\nUnregistered request:\n GET: https://hb.opencpu.org/get" + sprintf("Real HTTP connections are disabled.\nUnregistered request:\n GET: %s", hb("/get")) ) invisible(stub_request("get", hb("/get"))) @@ -255,11 +255,10 @@ test_that("HttrAdapter works with httr::authenticate", { body = list(foo = "bar"), headers = list("Content-Type" = "application/json") ) - # x <- httr::GET("https://httpbin.org/basic-auth/foo/bar", httr::authenticate("foo", "bar")) + # x <- httr::GET(hb("/basic-auth/foo/bar"), httr::authenticate("foo", "bar")) # httr_obj_auth <- x$request # save(httr_obj_auth, file = "tests/testthat/httr_obj_auth.rda", version = 2) # load("tests/testthat/httr_obj_auth.rda") - # httr::content(x) # mocked httr requests with auth work # before the fixes in HttrAdapter: a real request through webmockr would diff --git a/tests/testthat/test-RequestSignature.R b/tests/testthat/test-RequestSignature.R index 4e4b577..58fe444 100644 --- a/tests/testthat/test-RequestSignature.R +++ b/tests/testthat/test-RequestSignature.R @@ -21,7 +21,7 @@ test_that("RequestSignature: works", { expect_equal(aa$uri, hb("/get")) expect_is(aa$to_s, "function") - expect_equal(aa$to_s(), "GET: https://hb.opencpu.org/get") + expect_equal(aa$to_s(), sprintf("GET: %s", hb("/get"))) }) test_that("RequestSignature: different methods work", { diff --git a/tests/testthat/test-Response.R b/tests/testthat/test-Response.R index 07fb642..5e8d1c5 100644 --- a/tests/testthat/test-Response.R +++ b/tests/testthat/test-Response.R @@ -57,10 +57,10 @@ test_that("Response: bits are correct after having data", { expect_equal(aa$url, hb("/get")) expect_null(aa$name) - expect_equal(aa$body, "hello world") + expect_equal(aa$body, charToRaw("hello world")) expect_is(aa$content, "raw") expect_equal(aa$exception, "exception") - expect_equal(aa$get_body(), "hello world") + expect_equal(rawToChar(aa$get_body()), "hello world") expect_equal(aa$get_exception(), "exception") expect_equal(aa$get_request_headers()[[1]], "application/json") expect_equal(aa$get_respone_headers()[[1]], "hb.opencpu.org") @@ -73,8 +73,9 @@ test_that("Response: bits are correct after having data", { # set_body: char gets converted to raw in $content aa$set_body(body = "stuff") - expect_is(aa$body, "character") + expect_is(aa$body, "raw") expect_is(aa$content, "raw") + expect_length(aa$body, 5) expect_length(aa$content, 5) # set_body: raw remains as raw in $content @@ -85,7 +86,7 @@ test_that("Response: bits are correct after having data", { # set_body: other types return raw(0) in $content aa$set_body(body = NULL) - expect_null(aa$body) + expect_equal(aa$body, raw()) expect_is(aa$content, "raw") expect_length(aa$content, 0) diff --git a/tests/testthat/test-flipswitch.R b/tests/testthat/test-flipswitch.R index f98dbd3..ed440d1 100644 --- a/tests/testthat/test-flipswitch.R +++ b/tests/testthat/test-flipswitch.R @@ -7,15 +7,20 @@ test_that("flipswitch in default state", { }) test_that("flipswitch - turn on with 'enable'", { + skip_if_not_installed("httr") + skip_if_not_installed("httr2") + aa <- enable() expect_is(aa, "logical") - expect_equal(length(aa), 2) + expect_equal(length(aa), 3) expect_true(all(aa)) expect_true(webmockr_lightswitch$crul) skip_if_not_installed("httr") expect_true(webmockr_lightswitch$httr) + skip_if_not_installed("httr2") + expect_true(webmockr_lightswitch$httr2) }) test_that("flipswitch - turn on with 'enable' - one pkg", { @@ -32,6 +37,8 @@ test_that("flipswitch - turn on with 'enable' - one pkg", { expect_true(webmockr_lightswitch$crul) skip_if_not_installed("httr") expect_false(webmockr_lightswitch$httr) + skip_if_not_installed("httr2") + expect_false(webmockr_lightswitch$httr2) }) test_that("flipswitch - turn off with 'disable'", { @@ -43,6 +50,8 @@ test_that("flipswitch - turn off with 'disable'", { expect_false(webmockr_lightswitch$crul) skip_if_not_installed("httr") expect_false(webmockr_lightswitch$httr) + skip_if_not_installed("httr2") + expect_false(webmockr_lightswitch$httr2) }) test_that("enable and disable fail well", { @@ -65,6 +74,7 @@ test_that("enabled works", { expect_false(enabled()) expect_false(enabled('crul')) expect_false(enabled('httr')) + expect_false(enabled('httr2')) expect_error(enabled('foobar'), "'adapter' must be in the set") }) diff --git a/tests/testthat/test-onload.R b/tests/testthat/test-onload.R index 58446f7..19a4f07 100644 --- a/tests/testthat/test-onload.R +++ b/tests/testthat/test-onload.R @@ -3,13 +3,15 @@ context("onload") test_that("onload: http_lib_adapter_registry", { expect_is(http_lib_adapter_registry, "HttpLibAdapaterRegistry") expect_is(http_lib_adapter_registry, "R6") - expect_equal(sort(ls(envir=http_lib_adapter_registry)), + expect_equal(sort(ls(envir=http_lib_adapter_registry)), c('adapters', 'clone', 'print', 'register')) expect_is(http_lib_adapter_registry$adapters, "list") - expect_is(http_lib_adapter_registry$adapters[[1]], + expect_is(http_lib_adapter_registry$adapters[[1]], "CrulAdapter") - expect_is(http_lib_adapter_registry$adapters[[2]], + expect_is(http_lib_adapter_registry$adapters[[2]], "HttrAdapter") + expect_is(http_lib_adapter_registry$adapters[[3]], + "Httr2Adapter") expect_is(http_lib_adapter_registry$clone, "function") expect_is(http_lib_adapter_registry$print, "function") expect_is(http_lib_adapter_registry$register, "function") diff --git a/tests/testthat/test-stub_registry.R b/tests/testthat/test-stub_registry.R index 892b0de..4565edd 100644 --- a/tests/testthat/test-stub_registry.R +++ b/tests/testthat/test-stub_registry.R @@ -18,7 +18,7 @@ test_that("stub_registry: works", { ) expect_equal(length(stub_registry()$request_stubs), 2) expect_match(stub_registry()$request_stubs[[2]]$to_s(), - "POST: https://hb.opencpu.org/post") + sprintf("POST: %s", hb("/post"))) expect_match(stub_registry()$request_stubs[[2]]$to_s(), "CITATION") expect_match(stub_registry()$request_stubs[[2]]$to_s(), diff --git a/tests/testthat/test-stub_request.R b/tests/testthat/test-stub_request.R index 9a738fc..a213745 100644 --- a/tests/testthat/test-stub_request.R +++ b/tests/testthat/test-stub_request.R @@ -31,7 +31,7 @@ test_that("stub_request bits are correct", { expect_error(aa$to_return(), "argument \"body\" is missing") expect_is(aa$to_s, "function") - expect_equal(aa$to_s(), "GET: https://hb.opencpu.org/get") + expect_equal(aa$to_s(), sprintf("GET: %s", hb("/get"))) expect_is(aa$with, "function") expect_null(aa$with()) diff --git a/tests/testthat/test-to_return.R b/tests/testthat/test-to_return.R index c3cfb45..d82bce3 100644 --- a/tests/testthat/test-to_return.R +++ b/tests/testthat/test-to_return.R @@ -83,6 +83,35 @@ test_that("to_return (response) headers are all lowercase, httr", { }) disable() +stub_registry_clear() +enable() +test_that("to_return (response) headers are all lowercase, httr2", { + skip_if_not_installed("httr2") + loadNamespace("httr2") + stub <- stub_request(uri = hb("/get")) %>% + to_return(headers = list("Foo-Bar" = "baz")) + req <- httr2::request(hb("/get")) + x <- httr2::req_perform(req) + + expect_is(x$headers, "httr2_headers") + expect_named(x$headers, "foo-bar") +}) +disable() + + +stub_registry_clear() +enable() +test_that("to_return (response) header is the correct class, httr2", { + skip_if_not_installed("httr2") + loadNamespace("httr2") + stub <- stub_request(uri = hb("/get")) %>% + to_return(headers = list("Foo-Bar" = "baz")) + req <- httr2::request(hb("/get")) + x <- httr2::req_perform(req) + + expect_is(x$headers, "httr2_headers") +}) +disable() stub_registry_clear() @@ -149,6 +178,40 @@ test_that("to_return response header values are all character, httr", { }) disable() +enable() +test_that("to_return response header values are all character, httr2", { + skip_if_not_installed("httr2") + loadNamespace("httr2") + + stub_request(uri = hb("/get")) %>% + to_return(headers = list("Foo-Bar" = 10)) + req <- httr2::request(hb("/get")) + x <- httr2::req_perform(req) + + expect_is(x$headers, "httr2_headers") + expect_named(x$headers, "foo-bar") + expect_is(x$headers$`foo-bar`, "character") + expect_equal(x$headers$`foo-bar`, "10") + + stub_registry_clear() + stub_request(uri = hb("/get")) %>% + to_return(headers = list( + a = 10, b = 234233434, c = 2344.342342, + d = "brown", e = as.factor("blue") + )) + req <- httr2::request(hb("/get")) + z <- httr2::req_perform(req) + + expect_is(z$headers, "httr2_headers") + expect_named(z$headers, letters[1:5]) + invisible( + vapply(z$headers, function(z) expect_is(z, "character"), "") + ) + expect_equal(z$headers$c, "2344.342342") + expect_equal(z$headers$e, "blue") +}) +disable() + context("to_return_: defunct") test_that("to_return_: defunct", { diff --git a/tests/testthat/test-uri_regex.R b/tests/testthat/test-uri_regex.R index 5493ae2..8f2bfc2 100644 --- a/tests/testthat/test-uri_regex.R +++ b/tests/testthat/test-uri_regex.R @@ -6,6 +6,7 @@ test_that("uri_regex with crul", { library(crul) enable(adapter = "crul") + webmockr_disable_net_connect() invisible( lapply(c('elephants', 'bears', 'leaves', 'foo', 'bar'), function(z) { @@ -78,3 +79,50 @@ test_that("uri_regex with httr", { }) stub_registry_clear() + + +test_that("uri_regex with httr2", { + skip_if_not_installed("httr2") + + stub_request("get", uri_regex = "hb.opencpu.org/.+") %>% + to_return(body = list(foo = "bar")) + + library(httr2) + enable(adapter = "httr2") + invisible( + lapply(c('elephants', 'bears', 'leaves', 'foo', 'bar'), function(z) { + req <- request(file.path(hb(), z)) + expect_false(resp_is_error(req_perform(req))) + }) + ) + + # more complicated regex + stub_request("get", uri_regex = "[Aa].+\\.io/apple/") + invisible( + lapply(c('Anounce', 'apple', 'Afar', 'after'), function(z) { + req <- request(sprintf("https://%s.io/apple", z)) + expect_false(resp_is_error(req_perform(req))) + req2 <- request(sprintf("https://%s.io/fruit", z)) + expect_error(req_perform(req2), + "Real HTTP connections are disabled") + }) + ) + + # regex to match any URL + ## https://github.com/ropensci/webmockr/issues/113 + ## when matching any url with `.+`, it would lead to an empty url in response + ## object, at least with crul + stub_request("get", uri_regex = ".+") + invisible( + lapply(c('Anounce', 'apple', 'Afar', 'after'), function(z) { + url <- sprintf("https://%s.io", z) + # res <- GET(url, path = z) + req <- request(url) %>% req_url_path_append(z) + res <- req_perform(req) + expect_is(res, "httr2_response") + expect_true(grepl(res$url, file.path(url, z), ignore.case = TRUE)) + }) + ) +}) + +stub_registry_clear() diff --git a/tests/testthat/test-wi_th.R b/tests/testthat/test-wi_th.R index 85772e7..f6e5a3b 100644 --- a/tests/testthat/test-wi_th.R +++ b/tests/testthat/test-wi_th.R @@ -195,7 +195,7 @@ test_that("wi_th handles HEADERS with varied input classes", { disable("httr") -test_that("wi_th basic_auth", { +test_that("wi_th basic_auth, crul", { # crul library(crul) enable("crul") @@ -215,8 +215,9 @@ test_that("wi_th basic_auth", { con$auth <- crul::auth("user", "password") expect_error(con$get(), "Unregistered") disable("crul") +}) - # httr +test_that("wi_th basic_auth, httr", { library(httr) enable("httr") # pass @@ -240,6 +241,26 @@ test_that("wi_th basic_auth", { disable("httr") }) +test_that("wi_th basic_auth, httr2", { + skip_if_not_installed("httr2") + library(httr2) + enable("httr2") + # pass + stub_registry_clear() + stub_request("get", "https://x.com") %>% + wi_th(basic_auth=c("user", "passwd")) + req <- request("https://x.com") %>% req_auth_basic("user", "passwd") + expect_is(req_perform(req), "httr2_response") + # fail + stub_registry_clear() + stub_request("get", "https://x.com") %>% + wi_th(basic_auth=c("user", "passwd")) + req2 <- request("https://x.com") %>% req_auth_basic("user", "password") + expect_error(req_perform(req2), + "Unregistered") + disable("httr2") +}) + # cleanup stub_registry_clear() diff --git a/tests/testthat/test-writing-to-disk.R b/tests/testthat/test-writing-to-disk.R index cc19394..3d80f02 100644 --- a/tests/testthat/test-writing-to-disk.R +++ b/tests/testthat/test-writing-to-disk.R @@ -55,6 +55,34 @@ test_that("Write to a file before mocked request: httr", { stub_registry_clear() }) +test_that("Write to a file before mocked request: httr", { + skip_on_cran() + skip_if_not_installed("httr2") + + library(httr2) + ## make a temp file + f <- tempfile(fileext = ".json") + ## write something to the file + cat("{\"hello\":\"world\"}\n", file = f) + expect_is(readLines(f), "character") + expect_match(readLines(f), "world") + ## make the stub + stub_request("get", hb("/get")) %>% + to_return(body = file(f), + headers = list('content-type' = "application/json")) + ## make a request + req <- request(hb("/get")) + out <- req_perform(req, path = f) + expect_is(out$body, "httr2_path") + expect_equal(attr(out$body, "class"), "httr2_path") + expect_is(readLines(out$body), "character") + expect_match(readLines(out$body), "hello") + + # cleanup + unlink(f) + stub_registry_clear() +}) + test_that("Use mock_file to have webmockr handle file and contents: crul", { skip_on_cran() @@ -101,3 +129,32 @@ test_that("Use mock_file to have webmockr handle file and contents: httr", { unlink(f) stub_registry_clear() }) + +test_that("Use mock_file to have webmockr handle file and contents: httr", { + skip_on_cran() + skip_if_not_installed("httr2") + + library(httr2) + ## make a temp file + f <- tempfile(fileext = ".json") + ## make the stub + stub_request("get", hb("/get")) %>% + to_return( + body = mock_file(path = f, payload = "{\"foo\": \"bar\"}"), + headers = list('content-type' = "application/json") + ) + ## make a request + req <- request(hb("/get")) + # req <- request("https://hb.opencpu.org/get") + out <- req_perform(req, path = f) + # out <- GET(hb("/get"), write_disk(f)) + ## view stubbed file content + expect_is(out$body, "httr2_path") + expect_match(out$body, "json") + expect_is(readLines(out$body), "character") + expect_true(any(grepl("foo", readLines(out$body)))) + + # cleanup + unlink(f) + stub_registry_clear() +}) From 4e9d3f73600f7c9ab80e16683646d349eb46f856 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 12:33:17 -0700 Subject: [PATCH 56/66] ubuntu-latest on gh actions --- .github/workflows/R-CMD-check.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 671c859..2258a6e 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -14,8 +14,8 @@ jobs: config: - { os: windows-latest, r: 'release'} - { os: windows-latest, r: 'devel'} - - { os: ubuntu-20.04, r: 'release'} - - { os: ubuntu-20.04, r: 'devel'} + - { os: ubuntu-latest, r: 'release'} + - { os: ubuntu-latest, r: 'devel'} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true @@ -46,6 +46,6 @@ jobs: upload-snapshots: true - name: Test coverage - if: matrix.config.os == 'ubuntu-20.04' && matrix.config.r == 'release' + if: matrix.config.os == 'ubuntu-latest' && matrix.config.r == 'release' run: | Rscript -e 'install.packages("covr")' -e 'covr::codecov(token = "${{secrets.CODECOV_TOKEN}}")' From f8a0c0e6475d05c51f82a4e73d8f02607cfcddb2 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 12:36:29 -0700 Subject: [PATCH 57/66] rhub file --- .github/workflows/rhub.yaml | 95 +++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 .github/workflows/rhub.yaml diff --git a/.github/workflows/rhub.yaml b/.github/workflows/rhub.yaml new file mode 100644 index 0000000..74ec7b0 --- /dev/null +++ b/.github/workflows/rhub.yaml @@ -0,0 +1,95 @@ +# R-hub's generic GitHub Actions workflow file. It's canonical location is at +# https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml +# You can update this file to a newer version using the rhub2 package: +# +# rhub::rhub_setup() +# +# It is unlikely that you need to modify this file manually. + +name: R-hub +run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" + +on: + workflow_dispatch: + inputs: + config: + description: 'A comma separated list of R-hub platforms to use.' + type: string + default: 'linux,windows,macos' + name: + description: 'Run name. You can leave this empty now.' + type: string + id: + description: 'Unique ID. You can leave this empty now.' + type: string + +jobs: + + setup: + runs-on: ubuntu-latest + outputs: + containers: ${{ steps.rhub-setup.outputs.containers }} + platforms: ${{ steps.rhub-setup.outputs.platforms }} + + steps: + # NO NEED TO CHECKOUT HERE + - uses: r-hub/actions/setup@v1 + with: + config: ${{ github.event.inputs.config }} + id: rhub-setup + + linux-containers: + needs: setup + if: ${{ needs.setup.outputs.containers != '[]' }} + runs-on: ubuntu-latest + name: ${{ matrix.config.label }} + strategy: + fail-fast: false + matrix: + config: ${{ fromJson(needs.setup.outputs.containers) }} + container: + image: ${{ matrix.config.container }} + + steps: + - uses: r-hub/actions/checkout@v1 + - uses: r-hub/actions/platform-info@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + - uses: r-hub/actions/setup-deps@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + - uses: r-hub/actions/run-check@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + + other-platforms: + needs: setup + if: ${{ needs.setup.outputs.platforms != '[]' }} + runs-on: ${{ matrix.config.os }} + name: ${{ matrix.config.label }} + strategy: + fail-fast: false + matrix: + config: ${{ fromJson(needs.setup.outputs.platforms) }} + + steps: + - uses: r-hub/actions/checkout@v1 + - uses: r-hub/actions/setup-r@v1 + with: + job-config: ${{ matrix.config.job-config }} + token: ${{ secrets.RHUB_TOKEN }} + - uses: r-hub/actions/platform-info@v1 + with: + token: ${{ secrets.RHUB_TOKEN }} + job-config: ${{ matrix.config.job-config }} + - uses: r-hub/actions/setup-deps@v1 + with: + job-config: ${{ matrix.config.job-config }} + token: ${{ secrets.RHUB_TOKEN }} + - uses: r-hub/actions/run-check@v1 + with: + job-config: ${{ matrix.config.job-config }} + token: ${{ secrets.RHUB_TOKEN }} From 3c38becec1320267d077677748e6f402dd1703e7 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 12:54:11 -0700 Subject: [PATCH 58/66] delete rhub file --- .github/workflows/rhub.yaml | 95 ------------------------------------- 1 file changed, 95 deletions(-) delete mode 100644 .github/workflows/rhub.yaml diff --git a/.github/workflows/rhub.yaml b/.github/workflows/rhub.yaml deleted file mode 100644 index 74ec7b0..0000000 --- a/.github/workflows/rhub.yaml +++ /dev/null @@ -1,95 +0,0 @@ -# R-hub's generic GitHub Actions workflow file. It's canonical location is at -# https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml -# You can update this file to a newer version using the rhub2 package: -# -# rhub::rhub_setup() -# -# It is unlikely that you need to modify this file manually. - -name: R-hub -run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" - -on: - workflow_dispatch: - inputs: - config: - description: 'A comma separated list of R-hub platforms to use.' - type: string - default: 'linux,windows,macos' - name: - description: 'Run name. You can leave this empty now.' - type: string - id: - description: 'Unique ID. You can leave this empty now.' - type: string - -jobs: - - setup: - runs-on: ubuntu-latest - outputs: - containers: ${{ steps.rhub-setup.outputs.containers }} - platforms: ${{ steps.rhub-setup.outputs.platforms }} - - steps: - # NO NEED TO CHECKOUT HERE - - uses: r-hub/actions/setup@v1 - with: - config: ${{ github.event.inputs.config }} - id: rhub-setup - - linux-containers: - needs: setup - if: ${{ needs.setup.outputs.containers != '[]' }} - runs-on: ubuntu-latest - name: ${{ matrix.config.label }} - strategy: - fail-fast: false - matrix: - config: ${{ fromJson(needs.setup.outputs.containers) }} - container: - image: ${{ matrix.config.container }} - - steps: - - uses: r-hub/actions/checkout@v1 - - uses: r-hub/actions/platform-info@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - uses: r-hub/actions/setup-deps@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - uses: r-hub/actions/run-check@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - other-platforms: - needs: setup - if: ${{ needs.setup.outputs.platforms != '[]' }} - runs-on: ${{ matrix.config.os }} - name: ${{ matrix.config.label }} - strategy: - fail-fast: false - matrix: - config: ${{ fromJson(needs.setup.outputs.platforms) }} - - steps: - - uses: r-hub/actions/checkout@v1 - - uses: r-hub/actions/setup-r@v1 - with: - job-config: ${{ matrix.config.job-config }} - token: ${{ secrets.RHUB_TOKEN }} - - uses: r-hub/actions/platform-info@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - uses: r-hub/actions/setup-deps@v1 - with: - job-config: ${{ matrix.config.job-config }} - token: ${{ secrets.RHUB_TOKEN }} - - uses: r-hub/actions/run-check@v1 - with: - job-config: ${{ matrix.config.job-config }} - token: ${{ secrets.RHUB_TOKEN }} From 0196172f55bdc045a4c39ece06074f8818b0fcff Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 13:00:26 -0700 Subject: [PATCH 59/66] fix urls in description --- DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3603f3a..7322a84 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,9 +15,9 @@ Authors@R: c( person("rOpenSci", role = "fnd", comment = "https://ropensci.org") ) License: MIT + file LICENSE -URL: https://github.com/ropensci/webmockr (devel) - https://books.ropensci.org/http-testing/ (user manual) - https://docs.ropensci.org/webmockr/ (documentation) +URL: https://github.com/ropensci/webmockr, + https://books.ropensci.org/http-testing/, + https://docs.ropensci.org/webmockr/ BugReports: https://github.com/ropensci/webmockr/issues Encoding: UTF-8 Language: en-US From 050332b23e9ad73cca415c74a029c81f9e9e991b Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 13:58:52 -0700 Subject: [PATCH 60/66] update pkg level man file with fixed urls --- man/webmockr-package.Rd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/man/webmockr-package.Rd b/man/webmockr-package.Rd index 259aa81..6045798 100644 --- a/man/webmockr-package.Rd +++ b/man/webmockr-package.Rd @@ -17,6 +17,7 @@ Stubbing and setting expectations on 'HTTP' requests. Includes tools for stubbin \item Supports multiple HTTP libraries, including \pkg{crul}, \pkg{httr}, and \pkg{httr2} \item Integration with HTTP test caching library \pkg{vcr} +\item Supports async http request mocking with \pkg{crul} only } } @@ -30,7 +31,9 @@ stub_registry() \seealso{ Useful links: \itemize{ - \item \url{https://github.com/ropensci/webmockr (devel) https://books.ropensci.org/http-testing/ (user manual) https://docs.ropensci.org/webmockr/ (documentation)} + \item \url{https://github.com/ropensci/webmockr} + \item \url{https://books.ropensci.org/http-testing/} + \item \url{https://docs.ropensci.org/webmockr/} \item Report bugs at \url{https://github.com/ropensci/webmockr/issues} } From 268aaed9addab9ddb53655e985cd9cc86a770b51 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 19 Jul 2024 14:30:36 -0700 Subject: [PATCH 61/66] use pak for install instructions in readme --- README.Rmd | 3 ++- README.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.Rmd b/README.Rmd index b9d744d..308356e 100644 --- a/README.Rmd +++ b/README.Rmd @@ -56,7 +56,8 @@ install.packages("webmockr") Dev version ```{r eval=FALSE} -remotes::install_github("ropensci/webmockr") +# install.packages("pak") +pak::pak("ropensci/webmockr") ``` ```{r} diff --git a/README.md b/README.md index 222d446..d62a6ff 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,8 @@ Dev version ``` r -remotes::install_github("ropensci/webmockr") +# install.packages("pak") +pak::pak("ropensci/webmockr") ``` From e293811f31194aa71682b22708be4ebb6ab19249 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Mon, 22 Jul 2024 08:50:13 -0700 Subject: [PATCH 62/66] update codemeta.json --- codemeta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemeta.json b/codemeta.json index 97f8463..c0325d3 100644 --- a/codemeta.json +++ b/codemeta.json @@ -221,5 +221,5 @@ "applicationCategory": "Web", "isPartOf": "https://ropensci.org", "keywords": ["http", "https", "API", "web-services", "curl", "mock", "mocking", "fakeweb", "http-mocking", "testing", "testing-tools", "tdd"], - "fileSize": "378.631KB" + "fileSize": "378.648KB" } From 93adc3aadb9a4977f25da8ac23f46c8847e68ca5 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Mon, 22 Jul 2024 08:51:14 -0700 Subject: [PATCH 63/66] update cran comments file --- cran-comments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cran-comments.md b/cran-comments.md index a27591a..2c60fb8 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -18,7 +18,7 @@ We checked 18 reverse dependencies, comparing R CMD check results across CRAN an --- -This version includes ... +This version includes two new features. Thanks! Scott Chamberlain From 80cc3782ae4d37ae79f833337dd5dc14fdb69738 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Tue, 23 Jul 2024 12:47:19 -0700 Subject: [PATCH 64/66] bump to dev version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7322a84..2bcf11b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Stubbing and setting expectations on 'HTTP' requests. 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context. -Version: 1.0.0 +Version: 1.0.0.91 Authors@R: c( person("Scott", "Chamberlain", role = c("aut", "cre"), email = "myrmecocystus+r@gmail.com", comment = c(ORCID="0000-0003-1444-9135")), From 1b59f44cddd4219a225b7ff341735063adcf9436 Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Wed, 24 Jul 2024 08:37:02 -0700 Subject: [PATCH 65/66] tickle actions From acac4b42a18532c8d28706180aca4119eada7a9c Mon Sep 17 00:00:00 2001 From: Scott Chamberlain Date: Fri, 26 Jul 2024 21:59:59 -0700 Subject: [PATCH 66/66] tickle actions