[med-svn] [r-cran-webmockr] 01/02: New upstream version 0.1.0
Andreas Tille
tille at debian.org
Sat Nov 25 21:57:48 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository r-cran-webmockr.
commit cff1c95e428d9008f689d42e3cae993fc34c48b6
Author: Andreas Tille <tille at debian.org>
Date: Sat Nov 25 22:57:10 2017 +0100
New upstream version 0.1.0
---
DESCRIPTION | 24 ++
LICENSE | 2 +
MD5 | 67 ++++
NAMESPACE | 38 ++
NEWS.md | 6 +
R/HttpLibAdapterRegistry.R | 43 +++
R/RequestPattern.R | 530 ++++++++++++++++++++++++++
R/RequestRegistry.R | 109 ++++++
R/RequestSignature.R | 161 ++++++++
R/Response.R | 294 ++++++++++++++
R/StubRegistry.R | 138 +++++++
R/StubbedRequest.R | 132 +++++++
R/adapter-crul.R | 175 +++++++++
R/flipswitch.R | 21 +
R/globals.R | 3 +
R/headers.R | 52 +++
R/onload.R | 33 ++
R/pipe.R | 9 +
R/remove_request_stub.R | 15 +
R/request_is_in_cache.R | 4 +
R/stub_registry.R | 7 +
R/stub_registry_clear.R | 10 +
R/stub_request.R | 52 +++
R/to_return.R | 34 ++
R/webmockr-opts.R | 114 ++++++
R/webmockr.R | 24 ++
R/wi_th.R | 36 ++
R/zzz.R | 64 ++++
README.md | 226 +++++++++++
inst/ignore/adapter-httr.R | 95 +++++
inst/ignore/sockets.R | 48 +++
man/BodyPattern.Rd | 27 ++
man/CrulAdapter.Rd | 39 ++
man/HashCounter.Rd | 34 ++
man/HeadersPattern.Rd | 46 +++
man/HttpLibAdapaterRegistry.Rd | 27 ++
man/MethodPattern.Rd | 30 ++
man/RequestPattern.Rd | 69 ++++
man/RequestRegistry.Rd | 37 ++
man/RequestSignature.Rd | 71 ++++
man/Response.Rd | 78 ++++
man/StubRegistry.Rd | 67 ++++
man/StubbedRequest.Rd | 64 ++++
man/UriPattern.Rd | 60 +++
man/enable.Rd | 17 +
man/pipe.Rd | 12 +
man/remove_request_stub.Rd | 28 ++
man/stub_registry.Rd | 20 +
man/stub_registry_clear.Rd | 19 +
man/stub_request.Rd | 64 ++++
man/to_return.Rd | 31 ++
man/webmockr-package.Rd | 31 ++
man/webmockr_configure.Rd | 67 ++++
man/wi_th.Rd | 35 ++
tests/test-all.R | 2 +
tests/testthat/crul_obj.rda | Bin 0 -> 246 bytes
tests/testthat/test-CrulAdapter.R | 59 +++
tests/testthat/test-HashCounter.R | 42 ++
tests/testthat/test-HttpLibAdapaterRegistry.R | 38 ++
tests/testthat/test-RequestPattern.R | 76 ++++
tests/testthat/test-RequestRegistry.R | 48 +++
tests/testthat/test-RequestSignature.R | 42 ++
tests/testthat/test-Response.R | 97 +++++
tests/testthat/test-StubRegistry.R | 105 +++++
tests/testthat/test-StubbedRequest.R | 89 +++++
tests/testthat/test-flipswitch.R | 29 ++
tests/testthat/test-stub_request.R | 50 +++
tests/testthat/test-wi_th.R | 38 ++
68 files changed, 4254 insertions(+)
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..ae05064
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,24 @@
+Package: webmockr
+Title: Stubbing and Setting Expectations on 'HTTP' Requests
+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.
+Version: 0.1.0
+Authors at R: person("Scott", "Chamberlain", role = c("aut", "cre"), email =
+ "myrmecocystus+r at gmail.com")
+License: MIT + file LICENSE
+URL: https://github.com/ropensci/webmockr
+BugReports: https://github.com/ropensci/webmockr/issues
+LazyData: true
+Imports: curl, jsonlite, magrittr (>= 1.5), lazyeval (>= 0.2.0), R6 (>=
+ 2.1.3), urltools (>= 1.6.0)
+Suggests: roxygen2 (>= 6.0.1), testthat, crul (>= 0.3.4)
+RoxygenNote: 6.0.1
+NeedsCompilation: no
+Packaged: 2017-05-20 05:58:16 UTC; sacmac
+Author: Scott Chamberlain [aut, cre]
+Maintainer: Scott Chamberlain <myrmecocystus+r at gmail.com>
+Repository: CRAN
+Date/Publication: 2017-05-20 07:09:44 UTC
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..37ee2c7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+YEAR: 2017
+COPYRIGHT HOLDER: Scott Chamberlain
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..faf6174
--- /dev/null
+++ b/MD5
@@ -0,0 +1,67 @@
+8a9fdf4fb7d208f13d63d4a71a0c38b9 *DESCRIPTION
+c5af52351472a750055a760a8924ce71 *LICENSE
+4d82566602f57ce11096b45ea7047b2c *NAMESPACE
+31541443fa75794380be7190164750ef *NEWS.md
+1d57690083903f22a871f90f00f9bf26 *R/HttpLibAdapterRegistry.R
+e4d7dca4621121f3180f5857f2305892 *R/RequestPattern.R
+f75c830437037280b72da6cf1b95f7be *R/RequestRegistry.R
+66a6621108dfe2ce28395c9bea3bd43a *R/RequestSignature.R
+a453297dffb5440ae814e4e91d8ce0af *R/Response.R
+b17e26dfe4baa9fdec82061215bd3400 *R/StubRegistry.R
+5bd4e0ad42cad41d647bab55dba8b6f2 *R/StubbedRequest.R
+948a2c3ac917929f5c5e4b60a4661bce *R/adapter-crul.R
+1c0cea5e0e135547036b1e9acbf40d46 *R/flipswitch.R
+d64d3ea6fde479b3e3a7c4114d7abb63 *R/globals.R
+df2d78c4834ed882b32d776442e710f5 *R/headers.R
+556ac20c5e37419f278c25765eed55cf *R/onload.R
+f583f5b5856f7cb5f2c5fbb04f39f8a8 *R/pipe.R
+56db156253368fd808bb2fa279befede *R/remove_request_stub.R
+1591fbffccf0e3dccd11091bf116ab35 *R/request_is_in_cache.R
+e460927529aa8f3aaca29cef83cc24ae *R/stub_registry.R
+c3180404daf6a2b77f6246a63297186f *R/stub_registry_clear.R
+e7a4dd7bc0e8b4262401df2342ad3ae2 *R/stub_request.R
+ef99a00e9a80e6c6bf268411ae44fa1e *R/to_return.R
+cdb9c16bb481bb80e785a968a973eb1f *R/webmockr-opts.R
+a692851f46491e37495832dccc780f75 *R/webmockr.R
+dc9d767586f7371a0505ffcd264a1813 *R/wi_th.R
+745b2ffdc96b547e670b9f4663e8c110 *R/zzz.R
+b841b6cc58e204b76d3e297e312a2bd3 *README.md
+bfab6207f448fab82e6cd42ebc3abbf2 *inst/ignore/adapter-httr.R
+c2dad18498a5fbbfa1ad2b9618072bcb *inst/ignore/sockets.R
+0ca5e4a771c53cd06f879d58d9a54344 *man/BodyPattern.Rd
+ec3e39c68a0f980c103331690d8a0089 *man/CrulAdapter.Rd
+c196bbf87e855d877094af15e23158e2 *man/HashCounter.Rd
+33b63c8fcb5423a346ef4d1b05f793ef *man/HeadersPattern.Rd
+ec627b3023a5aa6ca42c387da32ca891 *man/HttpLibAdapaterRegistry.Rd
+23390726ada577071131c6c6d51ef326 *man/MethodPattern.Rd
+21019e30ebb880c264ca2dcb22af3fe3 *man/RequestPattern.Rd
+1bfb8622d4bddff89f1be01750f21ce5 *man/RequestRegistry.Rd
+2ca0204d1aa26bd1f12e0112fb61faa3 *man/RequestSignature.Rd
+21da09d6388b0b36036538d9456b9db8 *man/Response.Rd
+c38decc88ddd406fc9ad8c85bb104403 *man/StubRegistry.Rd
+dde266ea76b68f6d57008ce899396a6f *man/StubbedRequest.Rd
+8e5ca8cd0233bfeae442c68beaf09ad8 *man/UriPattern.Rd
+3a11756aab1f70b465ec7e66ddbac3f9 *man/enable.Rd
+e17f41e959fd90a1736ad0fc8ee7ff81 *man/pipe.Rd
+70facf022a7b546d73af0d26dc25f4fe *man/remove_request_stub.Rd
+3c7e64a1b8550341ee65cfab6c0b171c *man/stub_registry.Rd
+3789fa757a2435e10813b1590b6d173d *man/stub_registry_clear.Rd
+c0e96ff8246a5068c8eece205fe0a177 *man/stub_request.Rd
+38a07bd49e9e0417e786553b2b7eb80d *man/to_return.Rd
+e9497f1fe828584fb7dd69af69642eb9 *man/webmockr-package.Rd
+dac7f4d84513f91977a843c3ce8c8ecf *man/webmockr_configure.Rd
+fefa06266cd5551701c9f707f8d3951a *man/wi_th.Rd
+6695b4e11699caab8ba7c936ff9d0778 *tests/test-all.R
+43e9a3a2f19d982c0919de0490556d0c *tests/testthat/crul_obj.rda
+6710e630fe560f076a9e963bf257642d *tests/testthat/test-CrulAdapter.R
+3b3c8f9cc9dc4b35609a0153d59aeca4 *tests/testthat/test-HashCounter.R
+670c4badc6762df39b94577d3c8d4750 *tests/testthat/test-HttpLibAdapaterRegistry.R
+48de2fd421e5cb753835a6862a08d406 *tests/testthat/test-RequestPattern.R
+322d4850b93f50b34970c60341d7f804 *tests/testthat/test-RequestRegistry.R
+7bda6e128575f52027f7d72c9e258981 *tests/testthat/test-RequestSignature.R
+c0387cc7350e4f67596d4e89ac4b91be *tests/testthat/test-Response.R
+33d39acaf9c54f6d75d45731fa06dd94 *tests/testthat/test-StubRegistry.R
+a31ea3b57342682c03ce55142837dc84 *tests/testthat/test-StubbedRequest.R
+685261001875b99f85a6491f1921ee2b *tests/testthat/test-flipswitch.R
+1c3d075244ad19058481a86bdec90c3c *tests/testthat/test-stub_request.R
+3bce8c753f28141f43504dbb44eb263a *tests/testthat/test-wi_th.R
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..b21b199
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,38 @@
+# Generated by roxygen2: do not edit by hand
+
+export("%>%")
+export(BodyPattern)
+export(CrulAdapter)
+export(HashCounter)
+export(HeadersPattern)
+export(HttpLibAdapaterRegistry)
+export(MethodPattern)
+export(RequestPattern)
+export(RequestRegistry)
+export(RequestSignature)
+export(Response)
+export(StubRegistry)
+export(StubbedRequest)
+export(UriPattern)
+export(disable)
+export(enable)
+export(remove_request_stub)
+export(stub_registry)
+export(stub_registry_clear)
+export(stub_request)
+export(to_return)
+export(to_return_)
+export(webmockr_allow_net_connect)
+export(webmockr_configuration)
+export(webmockr_configure)
+export(webmockr_configure_reset)
+export(webmockr_disable)
+export(webmockr_disable_net_connect)
+export(webmockr_enable)
+export(webmockr_net_connect_allowed)
+export(wi_th)
+export(wi_th_)
+import(R6)
+import(lazyeval)
+import(magrittr)
+importFrom(magrittr,"%>%")
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..60b758d
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,6 @@
+webmockr 0.1.0
+==============
+
+### NEW FEATURES
+
+* Released to CRAN.
diff --git a/R/HttpLibAdapterRegistry.R b/R/HttpLibAdapterRegistry.R
new file mode 100644
index 0000000..5a5366c
--- /dev/null
+++ b/R/HttpLibAdapterRegistry.R
@@ -0,0 +1,43 @@
+#' http lib adapter registry
+#'
+#' @export
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`register(x)`}{
+#' Register an http library adapter
+#' x: an http lib adapter, e.g., [CrulAdapter]
+#' return: nothing, registers the library adapter
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' x <- HttpLibAdapaterRegistry$new()
+#' x$register(CrulAdapter$new())
+#' x
+#' x$adapters
+#' x$adapters[[1]]$name
+
+HttpLibAdapaterRegistry <- R6::R6Class(
+ 'HttpLibAdapaterRegistry',
+ public = list(
+ adapters = NULL,
+
+ print = function(x, ...) {
+ cat("<HttpLibAdapaterRegistry> ", sep = "\n")
+ for (i in seq_along(self$adapters)) {
+ cat(sprintf(" %s: webmockr:::%s", self$adapters[[i]]$name,
+ class(self$adapters[[i]])[1]), sep = "\n")
+ }
+ },
+
+ register = function(x) {
+ # FIXME: when other adapters supported, change this inherits test
+ if (!inherits(x, "CrulAdapter")) {
+ stop("'x' must be an adapter, such as CrulAdapter", call. = FALSE)
+ }
+ self$adapters <- cc(list(self$adapters, x))
+ }
+ )
+)
diff --git a/R/RequestPattern.R b/R/RequestPattern.R
new file mode 100644
index 0000000..ff44426
--- /dev/null
+++ b/R/RequestPattern.R
@@ -0,0 +1,530 @@
+#' RequestPattern class
+#'
+#' @export
+#' @param method the HTTP method (any, head, options, get, post, put,
+#' patch, trace, or delete). "any" matches any HTTP method. required.
+#' @param uri (character) request URI. required or uri_regex
+#' @param uri_regex (character) request URI as regex. required or uri
+#' @param query (list) query parameters, optional
+#' @param body (list) body request, optional
+#' @param headers (list) headers, optional
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`matches(request_signature)`}{
+#' Test if request_signature matches a pattern
+#' - request_signature: a request signature
+#' }
+#' \item{`to_s()`}{
+#' Print pattern for easy human consumption
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @seealso pattern classes for HTTP method [MethodPattern], headers
+#' [HeadersPattern], body [BodyPattern], and URI/URL [UriPattern]
+#' @examples \dontrun{
+#' (x <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get"))
+#' x$body_pattern
+#' x$headers_pattern
+#' x$method_pattern
+#' x$uri_pattern
+#' x$to_s()
+#'
+#' # make a request signature
+#' rs <- RequestSignature$new(method = "get", uri = "https://httpbin.org/get")
+#'
+#' # check if it matches
+#' x$matches(rs)
+#'
+#' # regex uri
+#' (x <- RequestPattern$new(method = "get", uri_regex = ".+ossref.org"))
+#' x$uri_pattern
+#' x$uri_pattern$to_s()
+#' x$to_s()
+#'
+#' # uri with query parameters
+#' (x <- RequestPattern$new(
+#' method = "get", uri = "https://httpbin.org/get",
+#' query = list(foo = "bar")
+#' ))
+#' x$to_s()
+#' }
+RequestPattern <- R6::R6Class(
+ 'RequestPattern',
+ public = list(
+ method_pattern = NULL,
+ uri_pattern = NULL,
+ body_pattern = NULL,
+ headers_pattern = NULL,
+
+ initialize = function(method, uri = NULL, uri_regex = NULL,
+ query = NULL, body = NULL, headers = NULL) {
+
+ if (is.null(uri) && is.null(uri_regex)) {
+ stop("one of uri or uri_regex is required", call. = FALSE)
+ }
+
+ self$method_pattern <- MethodPattern$new(pattern = method)
+ self$uri_pattern <- if (!is.null(uri)) {
+ UriPattern$new(pattern = uri)
+ } else {
+ UriPattern$new(regex_pattern = uri_regex)
+ }
+ self$uri_pattern$add_query_params(query)
+ self$body_pattern <- if (!is.null(body)) BodyPattern$new(pattern = body)
+ self$headers_pattern <- if (!is.null(headers))
+ HeadersPattern$new(pattern = headers)
+ #if (length(options)) private$assign_options(options)
+ },
+
+ matches = function(request_signature) {
+ assert(request_signature, "RequestSignature")
+ c_type <- if (!is.null(request_signature$headers)) request_signature$headers$`Content-Type` else NULL
+ c_type <- if (!is.null(c_type)) strsplit(c_type, ';')[[1]][1]
+ self$method_pattern$matches(request_signature$method) &&
+ self$uri_pattern$matches(request_signature$uri) &&
+ (is.null(self$body_pattern) || self$body_pattern$matches(request_signature$body, c_type %||% "")) &&
+ (is.null(self$headers_pattern) || self$headers_pattern$matches(request_signature$headers))
+ },
+
+ to_s = function() {
+ gsub("^\\s+|\\s+$", "", paste(
+ toupper(self$method_pattern$to_s()),
+ self$uri_pattern$to_s(),
+ if (!is.null(self$body_pattern)) paste0(" with body ", self$body_pattern$to_s()),
+ if (!is.null(self$headers_pattern)) paste0(" with headers ", self$headers_pattern$to_s())
+ ))
+ }
+ ),
+
+ private = list(
+ # assign_options = function(options) {
+ # #self$validate_keys(options, 'body', 'headers', 'query', 'basic_auth')
+ # set_basic_auth_as_headers(options)
+ # self$body_pattern <- if ('body' %in% names(options)) BodyPattern$new(options['body'])
+ # self$headers_pattern <- if ('headers' %in% names(options)) HeadersPattern$new(options['headers'])
+ # if ('query' %in% names(options)) self$uri_pattern$add_query_params(options['query'])
+ # },
+
+ # validate_keys = function(x, ...) {
+ # valid_keys <- unlist(list(...), recursive = FALSE)
+ # for (i in seq_along(x)) {
+ # if (!names(x)[i] %in% valid_keys) {
+ # stop(
+ # sprintf("Unknown key: %s. Valid keys are: %s",
+ # names(x)[i],
+ # paste0(valid_keys, collapse = ", "),
+ # call. = FALSE
+ # )
+ # )
+ # }
+ # }
+ # },
+
+ set_basic_auth_as_headers = function(options) {
+ if ('basic_auth' %in% names(options)) {
+ private$validate_basic_auth(options$basic_auth)
+ options$headers <- list()
+ options$headers$Authorization <-
+ private$make_basic_auth(options$basic_auth[1], options$basic_auth[2])
+ }
+ },
+
+ validate_basic_auth = function(x) {
+ if (!inherits(x, "list") || length(unique(unname(unlist(x)))) == 1) {
+ stop(
+ "'basic_auth' option should be a list of length 2: username and password",
+ call. = FALSE
+ )
+ }
+ },
+
+ make_basic_auth = function(x, y) {
+ jsonlite::base64_enc(paste0(x, ":", y))
+ }
+ )
+)
+
+#' MethodPattern
+#'
+#' @export
+#' @keywords internal
+#' @param pattern (character) a HTTP method, lowercase
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`matches(method)`}{
+#' An HTTP method
+#' - method (character)
+#' }
+#' }
+#'
+#' @details Matches regardless of case. e.g., POST will match to post
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' (x <- MethodPattern$new(pattern = "post"))
+#' x$pattern
+#' x$matches(method = "post")
+#' x$matches(method = "POST")
+MethodPattern <- R6::R6Class(
+ 'MethodPattern',
+ public = list(
+ pattern = NULL,
+
+ initialize = function(pattern) {
+ self$pattern <- tolower(pattern)
+ },
+
+ matches = function(method) {
+ self$pattern == tolower(method) || self$pattern == "any"
+ },
+
+ to_s = function() self$pattern
+ )
+)
+
+#' HeadersPattern
+#'
+#' @export
+#' @keywords internal
+#' @param pattern (list) a pattern, as a named list, must be named,
+#' e.g,. `list(a = 5, b = 6)`
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`matches(headers)`}{
+#' Match a list of headers against that stored
+#' - headers (list) named list of headers, e.g,. `list(a = 5, b = 6)`
+#' }
+#' }
+#' @details
+#' `webmockr` normalises headers and treats all forms of same headers as equal:
+#' i.e the following two sets of headers are equal:
+#' `list(Header1 = "value1", content_length = 123, X_CuStOm_hEAder = "foo")`
+#' and
+#' `list(header1 = "value1", "Content-Length" = 123, "x-cuSTOM-HeAder" = "foo")`
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' (x <- HeadersPattern$new(pattern = list(a = 5)))
+#' x$pattern
+#' x$matches(list(a = 5))
+#'
+#' # different cases
+#' (x <- HeadersPattern$new(pattern = list(Header1 = "value1")))
+#' x$pattern
+#' x$matches(list(header1 = "value1"))
+#' x$matches(list(header1 = "value2"))
+#'
+#' # different symbols
+#' (x <- HeadersPattern$new(pattern = list(`Hello_World` = "yep")))
+#' x$pattern
+#' x$matches(list(`hello-world` = "yep"))
+#' x$matches(list(`hello-worlds` = "yep"))
+HeadersPattern <- R6::R6Class(
+ 'HeadersPattern',
+ public = list(
+ pattern = NULL,
+
+ initialize = function(pattern) {
+ stopifnot(is.list(pattern))
+ # # normalize names
+ # names(pattern) <- tolower(names(pattern))
+ # # normalize symbols
+ # ## underscores to single dash
+ # names(pattern) <- gsub("_", "-", names(pattern))
+ pattern <- private$normalize_headers(pattern)
+ self$pattern <- pattern
+ },
+
+ matches = function(headers) {
+ headers <- private$normalize_headers(headers)
+ if (self$empty_headers(self$pattern)) {
+ self$empty_headers(headers)
+ } else {
+ if (self$empty_headers(headers)) return(FALSE)
+ out <- c()
+ for (i in seq_along(self$pattern)) {
+ out[i] <- names(self$pattern)[i] %in% names(headers) &&
+ self$pattern[[i]] == headers[names(self$pattern)[i]]
+ }
+ all(out)
+ }
+ },
+
+ empty_headers = function(headers) {
+ is.null(headers) || length(headers) == 0
+ },
+
+ to_s = function() self$pattern
+ ),
+
+ private = list(
+ normalize_headers = function(x) {
+ # normalize names
+ names(x) <- tolower(names(x))
+ # normalize symbols
+ ## underscores to single dash
+ names(x) <- gsub("_", "-", names(x))
+ return(x)
+ }
+ )
+)
+
+#' BodyPattern
+#'
+#' @export
+#' @keywords internal
+#' @param pattern (list) a body object
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`matches(body, content_type = "")`}{
+#' Match a body object against that given in `pattern`
+#' - body (list) the body
+#' - content_type (character) content type
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' z <- BodyPattern$new(pattern = list(a = "foobar"))
+#' z$pattern
+BodyPattern <- R6::R6Class(
+ 'BodyPattern',
+ public = list(
+ pattern = NULL,
+ body = NULL,
+ content_type = NULL,
+ headers = NULL,
+ string = NULL,
+
+ initialize = function(pattern) {
+ self$pattern <- pattern
+ },
+
+ matches = function(body, content_type = "") {
+ if (inherits(self$pattern, "list")) {
+ if (length(self$pattern) == 0) return(TRUE)
+ private$matching_hashes(self$body_as_hash(body, content_type), self$pattern)
+ } else {
+ private$empty_string(self$pattern) && private$empty_string(body) ||
+ self$pattern == body
+ }
+ },
+
+ to_s = function() self$pattern
+ ),
+
+ private = list(
+
+ empty_headers = function(headers) {
+ is.null(headers) || length(headers) == 0
+ },
+
+ empty_string = function(string) {
+ is.null(string) || nchar(string) == 0
+ },
+
+ matching_hashes = function(query_parameters, pattern) {
+ if (inherits(query_parameters, "list")) return(FALSE)
+ if (sort(names(query_parameters)) == sort(names(self$pattern))) return(FALSE)
+ for (i in seq_along(query_parameters)) {
+ expected <- self$pattern[names(query_parameters)[i]]
+ if (inherits(actual, "list") && inherits(expected, "list")) {
+ if (private$matching_hashes(actual, expected)) return(FALSE)
+ } else {
+ if (identical(actual, expected)) return(FALSE)
+ }
+ }
+ },
+
+ body_as_hash = function(body, content_type) {
+ bctype <- BODY_FORMATS[[content_type]]
+ if (bctype == 'json') {
+ jsonlite::fromJSON(body, FALSE)
+ } else if (bctype == 'xml') {
+ xml2::read_xml(body)
+ } else {
+ stop('fix me')
+ }
+ }
+ )
+)
+
+BODY_FORMATS <- list(
+ 'text/xml' = 'xml',
+ 'application/xml' = 'xml',
+ 'application/json' = 'json',
+ 'text/json' = 'json',
+ 'application/javascript' = 'json',
+ 'text/javascript' = 'json',
+ 'text/html' = 'html',
+ 'application/x-yaml' = 'yaml',
+ 'text/yaml' = 'yaml',
+ 'text/plain' = 'plain'
+)
+
+#' UriPattern
+#'
+#' @export
+#' @keywords internal
+#' @param pattern (character) a uri, either plain character string or
+#' regex, see [base::regex]. if scheme is missing, it is added (we assume
+#' http)
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`add_query_params`}{
+#' Add query parameters to the URI
+#' - query_params
+#' }
+#' \item{`matches(uri)`}{
+#' Match a uri against that given in `pattern`
+#' - uri (character) a uri, including scheme (i.e., http or https)
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' # trailing slash
+#' (z <- UriPattern$new(pattern = "http://foobar.com"))
+#' z$matches("http://foobar.com")
+#' z$matches("http://foobar.com/")
+#'
+#' # default ports
+#' (z <- UriPattern$new(pattern = "http://foobar.com"))
+#' z$matches("http://foobar.com:80")
+#' z$matches("http://foobar.com:80/")
+#' z$matches("http://foobar.com:443")
+#' z$matches("http://foobar.com:443/")
+#'
+#' # user info
+#' (z <- UriPattern$new(pattern = "http://foobar.com"))
+#' z$matches("http://user:pass@foobar.com")
+#'
+#' # regex
+#' (z <- UriPattern$new(regex_pattern = ".+ample\\.."))
+#' z$matches("http://sample.org")
+#' z$matches("http://example.com")
+#' z$matches("http://tramples.net")
+#'
+#' # add query parameters
+#' (z <- UriPattern$new(pattern = "http://foobar.com"))
+#' z$add_query_params(list(pizza = "cheese", cheese = "cheddar"))
+#' z$pattern
+#'
+#' (z <- UriPattern$new(pattern = "http://foobar.com"))
+#' z$add_query_params(list(pizza = "deep dish", cheese = "cheddar"))
+#' z$pattern
+
+UriPattern <- R6::R6Class(
+ 'UriPattern',
+ public = list(
+ pattern = NULL,
+ query_params = NULL,
+ regex = FALSE,
+
+ initialize = function(pattern = NULL, regex_pattern = NULL) {
+ stopifnot(xor(is.null(pattern), is.null(regex_pattern)))
+ if (!is.null(regex_pattern)) self$regex <- TRUE
+ pattern <- if (!is.null(pattern)) pattern else regex_pattern
+ self$pattern <- normalize_uri(add_scheme(pattern))
+ },
+
+ matches = function(uri) {
+ # normalize uri
+ uri <- normalize_uri(uri)
+
+ # FIXME, may need to match optionally to URI alone or URI + query
+ # params, etc.
+ if (!self$regex) return(uri == self$pattern)
+ if (self$regex) return(grepl(self$pattern, uri))
+ },
+
+ add_query_params = function(query_params) {
+ if (
+ inherits(query_params, "list") ||
+ inherits(query_params, "character")
+ ) {
+ pars <- paste0(unname(Map(function(x, y) paste(x, esc(y), sep = "="),
+ names(query_params), query_params)), collapse = "&")
+ self$pattern <- paste0(self$pattern, "?", pars)
+ }
+ },
+
+ to_s = function() self$pattern
+ )
+)
+
+add_scheme <- function(x) {
+ if (is.na(urltools::url_parse(x)$scheme)) {
+ paste0('http://', x)
+ } else {
+ x
+ }
+}
+esc <- function(x) curl::curl_escape(x)
+normalize_uri <- function(x) {
+ x <- prune_trailing_slash(x)
+ x <- prune_port(x)
+ tmp <- urltools::url_parse(x)
+ if (is.na(tmp$path)) return(x)
+ tmp$path <- esc(tmp$path)
+ urltools::url_compose(tmp)
+}
+
+prune_trailing_slash <- function(x) sub("/$", "", x)
+
+prune_port <- function(x) gsub("(:80)|(:443)", "", x)
+
+prune_user_pwd <- function(x)
+
+# matcher helpers --------------------------
+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
+}
+
+is_url <- function(x) {
+ grepl("https?://", x, ignore.case = TRUE) ||
+ grepl("localhost:[0-9]{4}", x, ignore.case = TRUE)
+}
+
+get_uri <- function(x) {
+ x <- as.character(x)
+ #tmp <- grep("(https?|ftp|file)?:?(//)?[-A-Za-z0-9]+\\.[A-Za-z0-9]+", x, value = TRUE)
+ tmp <- x[vapply(x, is_url, logical(1))]
+ if (length(tmp) == 0) NULL else tmp
+}
+
+get_host <- function(x) {
+ eval(parse(text = vcr_c$uri_parser))(x)$hostname
+}
+
+get_path <- function(x) {
+ eval(parse(text = vcr_c$uri_parser))(x)$path
+}
+
+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/RequestRegistry.R b/R/RequestRegistry.R
new file mode 100644
index 0000000..0873cf5
--- /dev/null
+++ b/R/RequestRegistry.R
@@ -0,0 +1,109 @@
+#' hash with counter, to store requests, and count each time it is used
+#'
+#' @export
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`put(key)`}{
+#' Register a request by it's key
+#' - key: a character string of the request, serialized from
+#' [CrulAdapter] or other adapter
+#' }
+#' \item{`get(key)`}{
+#' Get a request by key
+#' - key: a character string of the request, serialized from
+#' [CrulAdapter] or other adapter
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' x <- HashCounter$new()
+#' x$put("foo bar")
+#' x$put("foo bar")
+#' x$put("hello world")
+#' x$put("hello world")
+#' x$put("hello world")
+#' x$hash
+HashCounter <- R6::R6Class(
+ 'HashCounter',
+ public = list(
+ hash = list(),
+
+ put = function(key) {
+ if (missing(key)) stop("'key' required")
+ self$hash[key] <- (self$hash[[key]] %||% 0) + 1
+ },
+
+ get = function(key) {
+ if (missing(key)) stop("'key' required")
+ self$hash[[key]] %||% 0
+ }
+ )
+)
+
+#' Request registry
+#'
+#' @export
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`register_request(request)`}{
+#' Register a request
+#' - request: a character string of the request, serialized from
+#' [CrulAdapter] or other adapter
+#' }
+#' \item{`reset()`}{
+#' Reset the registry to no registered requests
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' x <- RequestRegistry$new()
+#' x$register_request(request = "GET http://scottchamberlain.info")
+#' x$register_request(request = "GET http://scottchamberlain.info")
+#' x$register_request(request = "POST https://httpbin.org/post")
+#' # print method to list requests
+#' x
+#'
+#' # hashes, and number of times each requested
+#' x$request_signatures$hash
+#'
+#' # reset the request registry
+#' x$reset()
+RequestRegistry <- R6::R6Class(
+ 'RequestRegistry',
+ public = list(
+ request = NULL,
+ request_signatures = HashCounter$new(),
+
+ print = function(x, ...) {
+ cat("<webmockr request registry> ", sep = "\n")
+ cat(" Registered Requests", sep = "\n")
+ for (i in seq_along(self$request_signatures$hash)) {
+ cat(
+ sprintf(
+ " %s was made %s times\n",
+ names(self$request_signatures$hash)[i],
+ self$request_signatures$hash[[i]]
+ ),
+ sep = "\n"
+ )
+ }
+ invisible(self$request_signatures$hash)
+ },
+
+ reset = function() {
+ self$request_signatures <- HashCounter$new()
+ },
+
+ register_request = function(request) {
+ self$request_signatures$put(request)
+ }
+ )
+)
+
+# initialize empty request registry on package load
+webmockr_request_registry <- new.env()
+webmockr_request_registry <- RequestRegistry$new()
diff --git a/R/RequestSignature.R b/R/RequestSignature.R
new file mode 100644
index 0000000..a0d385b
--- /dev/null
+++ b/R/RequestSignature.R
@@ -0,0 +1,161 @@
+#' General purpose request signature builder
+#'
+#' @export
+#' @param method the HTTP method (any, head, options, get, post, put,
+#' patch, trace, or delete). "any" matches any HTTP method. required.
+#' @param uri (character) request URI. required.
+#' @param options (list) options. optional. See Details.
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`to_s()`}{
+#' Request signature to a string
+#' return: a character string representation of the request signature
+#' }
+#' }
+#'
+#' @section options:
+#' \itemize{
+#' \item body - body as a named list
+#' \item headers - headers as a named list
+#' \item proxies - proxies as a named list
+#' \item auth - authentication details, as a named list
+#' }
+#'
+#' @format NULL
+#' @usage NULL
+#' @examples
+#' # make request signature
+#' x <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get")
+#' # method
+#' x$method
+#' # uri
+#' x$uri
+#' # request signature to string
+#' x$to_s()
+#'
+#' # headers
+#' z <- RequestSignature$new(
+#' method = "get",
+#' uri = "https:/httpbin.org/get",
+#' options = list(headers = list(`User-Agent` = "foobar", stuff = "things"))
+#' )
+#' z
+#' z$headers
+#' z$to_s()
+#'
+#' # headers and body
+#' z <- RequestSignature$new(
+#' method = "get",
+#' uri = "https:/httpbin.org/get",
+#' options = list(
+#' headers = list(`User-Agent` = "foobar", stuff = "things"),
+#' body = list(a = "tables")
+#' )
+#' )
+#' z
+#' z$headers
+#' z$body
+#' z$to_s()
+
+RequestSignature <- R6::R6Class(
+ 'RequestSignature',
+ public = list(
+ method = NULL,
+ uri = NULL,
+ body = NULL,
+ headers = NULL,
+ proxies = NULL,
+ auth = NULL,
+
+ initialize = function(method, uri, options = list()) {
+ verb <- match.arg(tolower(method), http_verbs)
+ self$method <- verb
+ self$uri <- uri
+ if (length(options)) private$assign_options(options)
+ },
+
+ print = function(x, ...) {
+ cat("<RequestSignature> ", sep = "\n")
+ cat(paste0(" method: ", toupper(self$method)), sep = "\n")
+ cat(paste0(" uri: ", self$uri), sep = "\n")
+ if (!is.null(self$body)) {
+ cat(" body: ", sep = "\n")
+ cat_foo(z$body)
+ }
+ if (!is.null(self$headers)) {
+ cat(" headers: ", sep = "\n")
+ cat_foo(z$headers)
+ }
+ if (!is.null(self$proxies)) {
+ cat(" proxies: ", sep = "\n")
+ cat_foo(z$proxies)
+ }
+ if (!is.null(self$auth)) {
+ cat(" auth: ", sep = "\n")
+ cat_foo(z$auth)
+ }
+ },
+
+ to_s = function() {
+ gsub("^\\s+|\\s+$", "", paste(
+ toupper(self$method),
+ self$uri,
+ if (!is.null(self$body) && length(self$body)) {
+ paste0(" with body ", to_string(self$body))
+ },
+ if (!is.null(self$headers) && length(self$headers)) {
+ paste0(
+ " with headers ",
+ sprintf("{%s}",
+ paste(names(self$headers),
+ unlist(unname(self$headers)), sep = ": ",
+ collapse = ", "))
+ )
+ }
+ ))
+ }
+ ),
+
+ 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
+ }
+ }
+ }
+ )
+)
+
+cat_foo <- function(x) {
+ cat(paste0(" ",
+ paste0(paste(names(x), x, sep = ": "),
+ collapse = "\n ")), sep = "\n")
+}
+
+to_string <- function(x) {
+ if (inherits(x, "list") && all(nchar(names(x)) > 0)) {
+ tmp <- paste0(paste(names(x), x, sep = ": "), collapse = ", ")
+ } else if (inherits(x, "list") && any(nchar(names(x)) == 0)) {
+ tmp <- paste0(paste(names(x), x, sep = ": "), collapse = ", ")
+ } else {
+ tmp <- paste0(x, collapse = ", ")
+ }
+ return(sprintf("{%s}", tmp))
+}
diff --git a/R/Response.R b/R/Response.R
new file mode 100644
index 0000000..137ff34
--- /dev/null
+++ b/R/Response.R
@@ -0,0 +1,294 @@
+#' Response class
+#'
+#' @export
+#' @param options (list) a list of options
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`set_request_headers(headers)`}{
+#' set request headers
+#' - headers: a list of key-value pair headers
+#' }
+#' \item{`get_request_headers()`}{
+#' get request headers
+#' }
+#' \item{`set_response_headers(headers)`}{
+#' set response headers
+#' - headers: a list of key-value pair headers
+#' }
+#' \item{`get_response_headers()`}{
+#' get response headers
+#' }
+#' \item{`set_body(body)`}{
+#' - body: must be a string
+#' }
+#' \item{`get_body()`}{
+#' get body
+#' }
+#' \item{`set_status()`}{
+#' - body: must be an integer status code
+#' }
+#' \item{`get_status()`}{
+#' get status code
+#' }
+#' \item{`set_exception()`}{
+#' set exception
+#' }
+#' \item{`get_exception()`}{
+#' get exception
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @examples \dontrun{
+#' (x <- Response$new())
+#'
+#' x$set_url("https://httpbin.org/get")
+#' x
+#'
+#' x$set_request_headers(list('Content-Type' = "application/json"))
+#' x
+#' x$request_headers
+#'
+#' x$set_response_headers(list('Host' = "httpbin.org"))
+#' x
+#' x$response_headers
+#'
+#' x$set_status(404)
+#' x
+#' x$get_status()
+#'
+#' x$set_body("hello world")
+#' x
+#' x$get_body()
+#'
+#' x$set_exception("exception")
+#' x
+#' x$get_exception()
+#' }
+Response <- R6::R6Class(
+ 'Response',
+ public = list(
+ url = NULL,
+ body = NULL,
+ content = NULL,
+ request_headers = NULL,
+ response_headers = NULL,
+ options = NULL,
+ status_code = 200,
+ exception = NULL,
+ should_timeout = NULL,
+
+ initialize = function(options = list()) {
+ if (inherits(options, "file") || inherits(options, "character")) {
+ self$options <- read_raw_response(options)
+ } else {
+ self$options <- options
+ }
+ },
+
+ print = function(x, ...) {
+ cat("<webmockr response> ", sep = "\n")
+ cat(paste0(" url: ", self$url), sep = "\n")
+ cat(paste0(" status: ", self$status_code), sep = "\n")
+ cat(" 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")
+ }
+ 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")
+ }
+ cat(paste0(" exception: ", self$exception), sep = "\n")
+ cat(paste0(" body: ", self$body), sep = "\n")
+ },
+
+ set_url = function(url) {
+ self$url <- url
+ },
+ get_url = function() self$url,
+
+ set_request_headers = function(headers) {
+ self$request_headers <- private$normalize_headers(headers)
+ },
+ get_request_headers = function() self$request_headers,
+
+ set_response_headers = function(headers) {
+ self$response_headers <- private$normalize_headers(headers)
+ },
+ get_respone_headers = function() self$response_headers,
+
+ set_body = function(body) {
+ self$body <- body
+ },
+ get_body = function() self$body %||% '',
+
+ set_status = function(status) {
+ self$status_code <- status
+ },
+ get_status = function() self$status_code %||% 200,
+
+ set_exception = function(exception) {
+ self$exception <- exception
+ },
+ get_exception = function() self$exception
+ ),
+
+ private = list(
+ normalize_headers = function(x) normalize_headers(x)
+ )
+)
+
+# class ResponseFactory
+# def self.response_for(options)
+# if options.respond_to?(:call)
+# WebMock::DynamicResponse.new(options)
+# else
+# WebMock::Response.new(options)
+# end
+# end
+# end
+
+# class Response
+# def initialize(options = {})
+# if options.is_a?(IO) || options.is_a?(String)
+# self.options = read_raw_response(options)
+# else
+# self.options = options
+# end
+# end
+
+# def headers
+# @headers
+# end
+
+# def headers=(headers)
+# @headers = headers
+# if @headers && !@headers.is_a?(Proc)
+# @headers = Util::Headers.normalize_headers(@headers)
+# end
+# end
+
+# def body
+# @body || ''
+# end
+
+# def body=(body)
+# @body = body
+# assert_valid_body!
+# stringify_body!
+# end
+
+# def status
+# @status || [200, ""]
+# end
+
+# def status=(status)
+# @status = status.is_a?(Integer) ? [status, ""] : status
+# end
+
+# def exception
+# @exception
+# end
+
+# def exception=(exception)
+# @exception = case exception
+# when String then StandardError.new(exception)
+# when Class then exception.new('Exception from WebMock')
+# when Exception then exception
+# end
+# end
+
+# def raise_error_if_any
+# raise @exception if @exception
+# end
+
+# def should_timeout
+# @should_timeout == true
+# end
+
+# def options=(options)
+# options = WebMock::Util::HashKeysStringifier.stringify_keys!(options)
+# HashValidator.new(options).validate_keys('headers', 'status', 'body', 'exception', 'should_timeout')
+# self.headers = options['headers']
+# self.status = options['status']
+# self.body = options['body']
+# self.exception = options['exception']
+# @should_timeout = options['should_timeout']
+# end
+
+# def evaluate(request_signature)
+# self.body = @body.call(request_signature) if @body.is_a?(Proc)
+# self.headers = @headers.call(request_signature) if @headers.is_a?(Proc)
+# self.status = @status.call(request_signature) if @status.is_a?(Proc)
+# @should_timeout = @should_timeout.call(request_signature) if @should_timeout.is_a?(Proc)
+# @exception = @exception.call(request_signature) if @exception.is_a?(Proc)
+# self
+# end
+
+# def ==(other)
+# self.body == other.body &&
+# self.headers === other.headers &&
+# self.status == other.status &&
+# self.exception == other.exception &&
+# self.should_timeout == other.should_timeout
+# end
+
+# private
+
+# def stringify_body!
+# if @body.is_a?(IO) || @body.is_a?(Pathname)
+# io = @body
+# @body = io.read
+# io.close if io.respond_to?(:close)
+# end
+# end
+
+# def assert_valid_body!
+# valid_types = [Proc, IO, Pathname, String, Array]
+# return if @body.nil?
+# return if valid_types.any? { |c| @body.is_a?(c) }
+# raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given"
+# end
+
+# def read_raw_response(raw_response)
+# if raw_response.is_a?(IO)
+# string = raw_response.read
+# raw_response.close
+# raw_response = string
+# end
+# socket = ::Net::BufferedIO.new(raw_response)
+# response = ::Net::HTTPResponse.read_new(socket)
+# transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
+# response.reading_body(socket, true) {}
+
+# options = {}
+# options[:headers] = {}
+# response.each_header {|name, value| options[:headers][name] = value}
+# options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding
+# options[:body] = response.read_body
+# options[:status] = [response.code.to_i, response.message]
+# options
+# end
+
+# InvalidBody = Class.new(StandardError)
+
+# end
+
+# class DynamicResponse < Response
+# attr_accessor :responder
+
+# def initialize(responder)
+# @responder = responder
+# end
+
+# def evaluate(request_signature)
+# options = @responder.call(request_signature)
+# Response.new(options)
+# end
+# end
diff --git a/R/StubRegistry.R b/R/StubRegistry.R
new file mode 100644
index 0000000..e3f78c3
--- /dev/null
+++ b/R/StubRegistry.R
@@ -0,0 +1,138 @@
+#' Stub registry
+#'
+#' @export
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`register_stub(stub)`}{
+#' Register a stub
+#' - stub: an object of class [StubbedRequest]
+#' }
+#' \item{`find_stubbed_request(req)`}{
+#' Find a stubbed request
+#' - req: an object of class [RequestSignature]
+#' }
+#' \item{`response_for_request(request_signature)`}{
+#' Find a stubbed request
+#' - request_signature: an object of class [RequestSignature]
+#' }
+#' \item{`request_stub_for(request_signature)`}{
+#' Find a stubbed request
+#' - request_signature: an object of class [RequestSignature]
+#' }
+#' \item{`remove_request_stub(stub)`}{
+#' Remove a stubbed request by matching request signature
+#' - stub: an object of class [StubbedRequest]
+#' }
+#' \item{`remove_all_request_stubs()`}{
+#' Remove all request stubs
+#' }
+#' \item{`is_registered(x)`}{
+#' Find a stubbed request
+#' - x: an object of class [RequestSignature]
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @family stub-registry
+#' @examples \dontrun{
+#' # Make a stub
+#' stub1 <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+#' stub1$with(request_headers = list('User-Agent' = 'R'))
+#' stub1$to_return(status = 200, body = "foobar", response_headers = list())
+#' stub1
+#'
+#' # Make another stub
+#' stub2 <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+#' stub2
+#'
+#' # Put both stubs in the stub registry
+#' reg <- StubRegistry$new()
+#' reg$register_stub(stub = stub1)
+#' reg$register_stub(stub = stub2)
+#' reg
+#' reg$request_stubs
+#' }
+StubRegistry <- R6::R6Class(
+ 'StubRegistry',
+ public = list(
+ stub = NULL,
+ request_stubs = list(),
+ global_stubs = list(),
+
+ print = function(x, ...) {
+ cat("<webmockr stub registry> ", sep = "\n")
+ cat(" Registered Stubs", sep = "\n")
+ for (i in seq_along(self$request_stubs)) {
+ cat(" ", self$request_stubs[[i]]$to_s(), "\n")
+ }
+ invisible(self$request_stubs)
+ },
+
+ register_stub = function(stub) {
+ self$request_stubs <- Filter(length, c(self$request_stubs, stub))
+ },
+
+ find_stubbed_request = function(req) {
+ stubs <- c(self$global_stubs, self$request_stubs)
+ stubs[self$request_stub_for(req)]
+ },
+
+ response_for_request = function(request_signature) {
+ stub <- self$request_stub_for(request_signature)
+ evaluate_response_for_request(stub$response, request_signature) %||% NULL
+ },
+
+ request_stub_for = function(request_signature) {
+ stubs <- c(self$global_stubs, self$request_stubs)
+ 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))
+ },
+
+ remove_request_stub = function(stub) {
+ xx <- vapply(self$request_stubs, function(x) x$to_s(), "")
+ if (stub$to_s() %in% xx) {
+ self$request_stubs <- self$request_stubs[-which(stub$to_s() %in% xx)]
+ } else {
+ stop(
+ "Request stub \n\n ",
+ stub$to_s(),
+ "\n\n is not registered.",
+ call. = FALSE
+ )
+ }
+ },
+
+ remove_all_request_stubs = function() {
+ self$request_stubs <- list()
+ },
+
+ is_registered = function(x) any(self$request_stub_for(x))
+ )
+)
+
+# initialize empty stub registry on package load
+webmockr_stub_registry <- new.env()
+webmockr_stub_registry <- StubRegistry$new()
+
+# madke body info for print method
+make_body <- function(x) {
+ if (is.null(x)) return("")
+ paste0(" with body ", jsonlite::toJSON(x, auto_unbox = TRUE))
+}
+
+# madke headers info for print method
+make_headers <- function(x) {
+ if (is.null(x)) return("")
+ paste0(" with headers ", jsonlite::toJSON(x, auto_unbox = TRUE))
+}
+
+# madke body info for print method
+make_status <- function(x) {
+ if (is.null(x)) return("")
+ paste0(" with status ", as.character(x))
+}
diff --git a/R/StubbedRequest.R b/R/StubbedRequest.R
new file mode 100644
index 0000000..af44117
--- /dev/null
+++ b/R/StubbedRequest.R
@@ -0,0 +1,132 @@
+#' StubbedRequest class
+#'
+#' @export
+#' @param method the HTTP method (any, head, get, post, put,
+#' patch, or delete). "any" matches any HTTP method. required.
+#' @param uri (character) request URI. either this or `uri_regex`
+#' required
+#' @param uri_regex (character) request URI as regex. either this or `uri`
+#' required
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`with(query, body, headers)`}{
+#' Set expectations for what's given in HTTP request
+#' \itemize{
+#' \item query (list) request query params, as a named list. optional
+#' \item body (list) request body, as a named list. optional
+#' \item headers (list) request headers as a named list. optional.
+#' }
+#' }
+#' \item{`to_return(status, body, headers)`}{
+#' Set expectations for what's returned in HTTP resonse
+#' \itemize{
+#' \item status (numeric) an HTTP status code
+#' \item body (list) response body, as a list. optional
+#' \item headers (list) named list, response headers. optional.
+#' }
+#' }
+#' \item{`to_s()`}{
+#' Response as a string
+#' }
+#' }
+#' @format NULL
+#' @usage NULL
+#' @seealso [stub_request()]
+#' @examples \dontrun{
+#' x <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+#' x$method
+#' x$uri
+#' x$with(headers = list('User-Agent' = 'R'))
+#' x$to_return(status = 200, body = "foobar", headers = list(a = 5))
+#' x
+#' x$to_s()
+#'
+#' # uri_regex
+#' (x <- StubbedRequest$new(method = "get", uri_regex = ".+ossref.org"))
+#' x$method
+#' x$uri
+#' x$to_s()
+#' }
+StubbedRequest <- R6::R6Class(
+ 'StubbedRequest',
+ public = list(
+ method = NULL,
+ uri = NULL,
+ uri_regex = NULL,
+ uri_parts = NULL,
+ host = NULL,
+ query = NULL,
+ body = NULL,
+ request_headers = NULL,
+ response_headers = NULL,
+ response = NULL,
+ responses_sequences = NULL,
+
+ initialize = function(method, uri = NULL, uri_regex = NULL) {
+ if (!missing(method)) {
+ verb <- match.arg(tolower(method), http_verbs)
+ self$method <- verb
+ }
+ if (is.null(uri) && is.null(uri_regex)) {
+ stop("one of uri or uri_regex is required", call. = FALSE)
+ }
+ self$uri <- if (!is.null(uri)) uri else uri_regex
+ self$uri_parts <- parseurl(self$uri)
+ },
+
+ print = function(x, ...) {
+ cat("<webmockr stub> ", sep = "\n")
+ cat(paste0(" method: ", self$method), sep = "\n")
+ cat(paste0(" uri: ", self$uri), sep = "\n")
+ cat(" with: ", sep = "\n")
+ cat(paste0(" query: ", hdl_lst(self$query)), sep = "\n")
+ cat(paste0(" body: ", hdl_lst(self$body)), sep = "\n")
+ cat(paste0(" request_headers: ", hdl_lst(self$request_headers)),
+ sep = "\n")
+ cat(" to_return: ", sep = "\n")
+ cat(paste0(" status: ", hdl_lst(self$responses_sequences$status)),
+ sep = "\n")
+ cat(paste0(" body: ", hdl_lst(self$responses_sequences$body)),
+ sep = "\n")
+ cat(paste0(" response_headers: ", hdl_lst(self$responses_sequences$headers)),
+ sep = "\n")
+ },
+
+ with = function(query = NULL, body = NULL, headers = NULL) {
+ self$query <- query
+ self$body <- body
+ self$request_headers <- headers
+ },
+
+ to_return = function(status, body, headers) {
+ self$response_headers <- headers
+ self$responses_sequences <- list(
+ status = status,
+ body = body,
+ headers = headers
+ )
+ },
+
+ to_s = function() {
+ toret <- c(
+ make_body(self$responses_sequences$body),
+ make_status(self$responses_sequences$status),
+ make_headers(self$responses_sequences$headers)
+ )
+ gsub("^\\s+|\\s+$", "", sprintf(
+ " %s: %s %s %s %s",
+ self$method,
+ url_builder(self$uri, self$query),
+ make_body(self$body),
+ make_headers(self$request_headers),
+ # response data
+ if (any(nchar(toret) > 0)) {
+ sprintf("| to_return: %s %s %s", toret[1], toret[2], toret[3])
+ } else {
+ ""
+ }
+ ))
+ }
+ )
+)
diff --git a/R/adapter-crul.R b/R/adapter-crul.R
new file mode 100644
index 0000000..9e873fe
--- /dev/null
+++ b/R/adapter-crul.R
@@ -0,0 +1,175 @@
+#' crul library adapter
+#'
+#' @export
+#' @family http_lib_adapters
+#' @details
+#' **Methods**
+#' \describe{
+#' \item{`enable()`}{
+#' Enable the adapter
+#' }
+#' \item{`disable()`}{
+#' Disable the adapter
+#' }
+#' \item{`build_crul_request(x)`}{
+#' Build a crul [RequestSignature]
+#' x: crul request parts (list)
+#' }
+#' \item{`build_crul_response(req, resp)`}{
+#' Build a crul response
+#' req: a crul request (list)
+#' resp: a crul response ()
+#' }
+#' \item{`handle_request()`}{
+#' All logic for handling a request
+#' req: a crul request (list)
+#' }
+#' \item{`remove_crul_stubs()`}{
+#' Remove all crul stubs
+#' }
+#' }
+#'
+#' This adapter modifies \pkg{crul} to allow mocking HTTP requests
+#'
+#' @format NULL
+#' @usage NULL
+CrulAdapter <- R6::R6Class(
+ 'CrulAdapter',
+ public = list(
+ name = "crul_adapter",
+
+ enable = function() {
+ message("CrulAdapter enabled!")
+ webmockr_lightswitch$crul <- TRUE
+ },
+
+ disable = function() {
+ message("CrulAdapter disabled!")
+ webmockr_lightswitch$crul <- FALSE
+ self$remove_crul_stubs()
+ },
+
+ build_crul_request = function(x) {
+ RequestSignature$new(
+ method = x$method,
+ uri = x$url$url,
+ options = list(
+ body = x$body %||% NULL,
+ headers = x$headers %||% NULL,
+ proxies = x$proxies %||% NULL,
+ auth = x$auth %||% NULL
+ )
+ )
+ },
+
+ build_crul_response = function(req, resp) {
+ crul::HttpResponse$new(
+ method = req$method,
+ url = req$url$url,
+ status_code = resp$status_code,
+ request_headers = c(useragent = req$options$useragent, req$headers),
+ #response_headers = list(),
+ response_headers = {
+ if (grepl("^ftp://", resp$url)) {
+ list()
+ } else {
+ hh <- rawToChar(resp$response_headers %||% raw(0))
+ if (is.null(hh) || nchar(hh) == 0) {
+ list()
+ } else {
+ crul_headers_parse(curl::parse_headers(hh))
+ }
+ }
+ },
+ modified = resp$modified,
+ times = resp$times,
+ content = resp$content,
+ handle = req$url$handle,
+ request = req
+ )
+ },
+
+ handle_request = function(req) {
+ # put request in request registry
+ request_signature <- self$build_crul_request(req)
+ webmockr_request_registry$register_request(
+ request = request_signature$to_s()
+ )
+
+ if (request_is_in_cache(request_signature)) {
+ # if real requests NOT allowed
+ # even if net connects allowed, we check if stubbed found first
+
+ # if user wants to return a partial object
+ # get stub with response and return that
+ ss <-
+ webmockr_stub_registry$find_stubbed_request(request_signature)[[1]]
+
+ resp <- Response$new()
+ resp$set_url(ss$uri)
+ resp$set_body(ss$body)
+ resp$set_request_headers(ss$request_headers)
+ resp$set_response_headers(ss$response_headers)
+ # generate crul response
+ crul_resp <- self$build_crul_response(req, resp)
+
+ # add to_return() elements if given
+ if (length(cc(ss$responses_sequences)) != 0) {
+ # remove NULLs
+ toadd <- cc(ss$responses_sequences)
+ # modify responses
+ for (i in seq_along(toadd)) {
+ if (names(toadd)[i] == "status")
+ crul_resp$status_code <- toadd[[i]]
+ if (names(toadd)[i] == "body")
+ crul_resp$content <- toadd[[i]]
+ if (names(toadd)[i] == "headers")
+ crul_resp$response_headers <- toadd[[i]]
+ }
+ }
+ } else if (webmockr_net_connect_allowed()) {
+ # if real requests ARE allowed && nothing found above
+ tmp <- crul::HttpClient$new(url = req$url$url)
+ tmp2 <- webmockr_crul_fetch(req)
+ crul_resp <- self$build_crul_response(req, tmp2)
+ } else {
+ # no stubs found and net connect not allowed
+ x <- "Real HTTP connections are disabled.\nUnregistered request:"
+ 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,
+ #make_stub_request_code(request_signature)
+ private$make_stub_request_code(request_signature)
+ )
+ if (length(webmockr_stub_registry$request_stubs)) {
+ msgz <- paste(
+ z,
+ paste0(vapply(webmockr_stub_registry$request_stubs, function(z)
+ z$to_s(), ""), collapse = "\n ")
+ )
+ } else {
+ msgz <- ""
+ }
+ stop(paste0(msgx, msgy, msgz), call. = FALSE)
+ }
+
+ return(crul_resp)
+ },
+
+ remove_crul_stubs = function() {
+ webmockr_stub_registry$remove_all_request_stubs()
+ }
+ ),
+
+ private = list(
+ make_stub_request_code = function(x) {
+ sprintf(
+ "stub_request('%s', url = '%s')",
+ x$method,
+ x$uri
+ )
+ }
+ )
+)
diff --git a/R/flipswitch.R b/R/flipswitch.R
new file mode 100644
index 0000000..a74069b
--- /dev/null
+++ b/R/flipswitch.R
@@ -0,0 +1,21 @@
+webmockr_lightswitch <- new.env()
+#webmockr_lightswitch$httr <- FALSE
+webmockr_lightswitch$crul <- FALSE
+
+#' Enable or disable webmockr
+#'
+#' @export
+#' @param options list of options - ignored for now.
+enable <- function(options = list()) {
+ vapply(http_lib_adapter_registry$adapters, function(z) {
+ z$enable()
+ }, logical(1))
+}
+
+#' @export
+#' @rdname enable
+disable <- function(options = list()) {
+ unlist(lapply(http_lib_adapter_registry$adapters, function(z) {
+ z$disable()
+ }))
+}
diff --git a/R/globals.R b/R/globals.R
new file mode 100644
index 0000000..d9c91cf
--- /dev/null
+++ b/R/globals.R
@@ -0,0 +1,3 @@
+if (base::getRversion() >= "2.15.1") {
+ utils::globalVariables(c("vcr_c"))
+}
diff --git a/R/headers.R b/R/headers.R
new file mode 100644
index 0000000..9ead33d
--- /dev/null
+++ b/R/headers.R
@@ -0,0 +1,52 @@
+# headers <- list(`Content-type` = 'application/json', Stuff = "things")
+# normalize_headers(x = headers)
+
+normalize_headers <- function(x = NULL) {
+ if (is.null(x) || length(x) == 0) return(x)
+ res <- Map(function(name, value) {
+ name <- paste0(
+ vapply(strsplit(as.character(name), '_|-')[[1]], function(w) simple_cap(w), ""),
+ collapse = "-"
+ )
+ value <- switch(
+ class(value),
+ #when Regexp then value
+ list = if (length(value) == 1) value[[1]] else sort(vapply(value, function(z) as.character(z), "")),
+ as.character(value)
+ )
+ list(name, value)
+ }, names(x), unlist(unname(x)))
+ vapply(res, function(z) stats::setNames(z[2], z[1]), list(1))
+}
+
+simple_cap <- function(x) {
+ s <- strsplit(x, " ")[[1]]
+ paste(toupper(substring(s, 1, 1)), substring(s, 2),
+ sep = "", collapse = " ")
+}
+
+# class Headers
+
+# def self.sorted_headers_string(headers)
+# headers = WebMock::Util::Headers.normalize_headers(headers)
+# str = '{'
+# str << headers.map do |k,v|
+# v = case v
+# when Regexp then v.inspect
+# when Array then "["+v.map{|w| "'#{w.to_s}'"}.join(", ")+"]"
+# else "'#{v.to_s}'"
+# end
+# "'#{k}'=>#{v}"
+# end.sort.join(", ")
+# str << '}'
+# end
+
+# def self.decode_userinfo_from_header(header)
+# header.sub(/^Basic /, "").unpack("m").first
+# end
+
+# def self.basic_auth_header(*credentials)
+# "Basic #{Base64.strict_encode64(credentials.join(':')).chomp}"
+# end
+
+# end
diff --git a/R/onload.R b/R/onload.R
new file mode 100644
index 0000000..0f38ebd
--- /dev/null
+++ b/R/onload.R
@@ -0,0 +1,33 @@
+#webmockr_stub_registry <- NULL
+http_lib_adapter_registry <- NULL
+.onLoad <- function(libname, pkgname) {
+ webmockr_configure()
+ x <- HttpLibAdapaterRegistry$new()
+ x$register(CrulAdapter$new())
+ http_lib_adapter_registry <<- x
+ # initialize empty stub registry on package load
+ # webmockr_stub_registry <<- new.env()
+ # webmockr_stub_registry <- webmockr::StubRegistry$new()
+}
+
+# .onAttach <- function(libname, pkgname) {
+# #base::unlockBinding("request_perform", as.environment("package:httr"))
+# utils::assignInNamespace("request_perform", request_perform, "httr")
+# #base::lockBinding("request_perform", as.environment("package:httr"))
+# }
+
+# .onAttach <- function(libname, pkgname) {
+# when_attached("httr", {
+# utils::assignInNamespace("request_perform", request_perform, "httr")
+# })
+# }
+#
+# when_attached <- function(pkg, action) {
+# if (is_attached(pkg)) {
+# action
+# } else {
+# setHook(packageEvent(pkg, "attach"), function(...) action)
+# }
+# }
+#
+# is_attached <- function(pkg) paste0("package:", pkg) %in% search()
diff --git a/R/pipe.R b/R/pipe.R
new file mode 100644
index 0000000..036dd30
--- /dev/null
+++ b/R/pipe.R
@@ -0,0 +1,9 @@
+#' Pipe operator
+#'
+#' @name %>%
+#' @rdname pipe
+#' @keywords internal
+#' @export
+#' @importFrom magrittr %>%
+#' @usage lhs \%>\% rhs
+NULL
diff --git a/R/remove_request_stub.R b/R/remove_request_stub.R
new file mode 100644
index 0000000..42c53ac
--- /dev/null
+++ b/R/remove_request_stub.R
@@ -0,0 +1,15 @@
+#' Remove a request stub
+#'
+#' @export
+#' @param stub a request stub, of class `StubbedRequest`
+#' @return logical, `TRUE` if removed, `FALSE` if not removed
+#' @family stub-registry
+#' @examples
+#' (x <- stub_request("get", "https://httpbin.org/get"))
+#' stub_registry()
+#' remove_request_stub(x)
+#' stub_registry()
+remove_request_stub <- function(stub) {
+ stopifnot(inherits(stub, "StubbedRequest"))
+ webmockr_stub_registry$remove_request_stub(stub = stub)
+}
diff --git a/R/request_is_in_cache.R b/R/request_is_in_cache.R
new file mode 100644
index 0000000..192d196
--- /dev/null
+++ b/R/request_is_in_cache.R
@@ -0,0 +1,4 @@
+# Check if request is in cache
+request_is_in_cache <- function(request_signature) {
+ webmockr_stub_registry$is_registered(request_signature)
+}
diff --git a/R/stub_registry.R b/R/stub_registry.R
new file mode 100644
index 0000000..4cab6d3
--- /dev/null
+++ b/R/stub_registry.R
@@ -0,0 +1,7 @@
+#' List stubs in the stub registry
+#'
+#' @export
+#' @return an object of class `StubRegistry`, print method gives the
+#' stubs in the registry
+#' @family stub-registry
+stub_registry <- function() webmockr_stub_registry
diff --git a/R/stub_registry_clear.R b/R/stub_registry_clear.R
new file mode 100644
index 0000000..0e88b0f
--- /dev/null
+++ b/R/stub_registry_clear.R
@@ -0,0 +1,10 @@
+#' Clear the stub registry
+#'
+#' Clear all stubs
+#'
+#' @export
+#' @return nothing
+#' @family stub-registry
+stub_registry_clear <- function() {
+ webmockr_stub_registry$remove_all_request_stubs()
+}
diff --git a/R/stub_request.R b/R/stub_request.R
new file mode 100644
index 0000000..de45e8c
--- /dev/null
+++ b/R/stub_request.R
@@ -0,0 +1,52 @@
+#' Stub an http request
+#'
+#' @export
+#' @param method (character) HTTP method, one of "get", "post", "put", "patch",
+#' "head", "delete", "options" - or the special "any" (for any method)
+#' @param uri (character) The request uri. Can be a full uri, partial, or a
+#' regular expression to match many incantations of a uri. required.
+#' @param uri_regex (character) A URI represented as regex. See examples
+#' @return an object of class `StubbedRequest`, with print method describing
+#' the stub.
+#' @details Internally, this calls [StubbedRequest] which handles the logic
+#'
+#' See [stub_registry()] for listing stubs, [stub_registry_clear()]
+#' for removing all stubs and [remove_request_stub()] for removing specific
+#' stubs
+#' @seealso [wi_th()], [to_return()]
+#' @examples \dontrun{
+#' # basic stubbing
+#' stub_request("get", "https://httpbin.org/get")
+#' stub_request("post", "https://httpbin.org/post")
+#'
+#' # list stubs
+#' stub_registry()
+#'
+#' # add header
+#' stub_request("get", "https://httpbin.org/get") %>%
+#' wi_th(headers = list('User-Agent' = 'R'))
+#'
+#' # add expectation with to_return
+#' stub_request("get", "https://httpbin.org/get") %>%
+#' wi_th(
+#' query = list(hello = "world"),
+#' headers = list('User-Agent' = 'R')) %>%
+#' to_return(status = 200, body = "stuff", headers = list(a = 5))
+#'
+#' # list stubs again
+#' stub_registry()
+#'
+#' # regex
+#' stub_request("get", uri_regex = ".+ample\\..")
+#'
+#' # clear all stubs
+#' stub_registry_clear()
+#' }
+stub_request <- function(method = "get", uri = NULL, uri_regex = NULL) {
+ if (is.null(uri) && is.null(uri_regex)) {
+ stop("one of uri or uri_regex is required", call. = FALSE)
+ }
+ tmp <- StubbedRequest$new(method = method, uri = uri, uri_regex = uri_regex)
+ webmockr_stub_registry$register_stub(tmp)
+ return(tmp)
+}
diff --git a/R/to_return.R b/R/to_return.R
new file mode 100644
index 0000000..330ba72
--- /dev/null
+++ b/R/to_return.R
@@ -0,0 +1,34 @@
+#' Expectation for what's returned from a stubbed request
+#'
+#' Set response status code, response body, and/or response headers
+#'
+#' @export
+#' @param .data input. Anything that can be coerced to a `StubbedRequest` class
+#' object
+#' @param ... Comma separated list of variable names, passed on
+#' to [lazyeval::lazy_dots()]. accepts the following: status, body,
+#' headers
+#' @param .dots Used to work around non-standard evaluation
+#' @return an object of class `StubbedRequest`, with print method describing
+#' the stub
+#' @note see examples in [stub_request()]
+to_return <- function(.data, ...) {
+ to_return_(.data, .dots = lazyeval::lazy_dots(...))
+}
+
+#' @export
+#' @rdname to_return
+to_return_ <- function(.data, ..., .dots) {
+ tmp <- lazyeval::all_dots(.dots, ...)
+ if (length(tmp) == 0) {
+ z <- NULL
+ } else {
+ z <- lapply(tmp, function(x) eval(x$expr))
+ }
+ .data$to_return(
+ status = z$status,
+ body = z$body,
+ headers = z$headers
+ )
+ return(.data)
+}
diff --git a/R/webmockr-opts.R b/R/webmockr-opts.R
new file mode 100644
index 0000000..74d119f
--- /dev/null
+++ b/R/webmockr-opts.R
@@ -0,0 +1,114 @@
+#' webmockr configuration
+#'
+#' @export
+#' @param turn_on (logical) Default: `FALSE`
+#' @param allow_net_connect (logical) Default: `TRUE`
+#' @param allow_localhost (logical) Default: `TRUE`
+#' @param allow (logical) Default: `TRUE`
+#' @param net_http_connect_on_start (logical) Default: `TRUE`
+#' @param show_stubbing_instructions (logical) Default: `TRUE`
+#' @param query_values_notation (logical) Default: `TRUE`
+#' @param show_body_diff (logical) Default: `TRUE`
+#'
+#' @examples \dontrun{
+#' webmockr_configure()
+#' webmockr_configure(
+#' allow_localhost = TRUE
+#' )
+#' webmockr_configuration()
+#' webmockr_configure_reset()
+#'
+#' webmockr_allow_net_connect()
+#' webmockr_net_connect_allowed()
+#' webmockr_disable_net_connect()
+#' webmockr_net_connect_allowed()
+#' }
+webmockr_configure <- function(
+ turn_on = FALSE,
+ allow_net_connect = FALSE,
+ allow_localhost = FALSE,
+ allow = FALSE,
+ net_http_connect_on_start = FALSE,
+ show_stubbing_instructions = FALSE,
+ query_values_notation = FALSE,
+ show_body_diff = FALSE) {
+
+ opts <- list(
+ turn_on = turn_on,
+ 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
+ )
+ for (i in seq_along(opts)) {
+ assign(names(opts)[i], opts[[i]], envir = webmockr_conf_env)
+ }
+ webmockr_configuration()
+}
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_configure_reset <- function() webmockr_configure()
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_configuration <- function() {
+ structure(as.list(webmockr_conf_env), class = "webmockr_config")
+}
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_enable <- function() {
+ message("webmockr enabled")
+ assign('turn_on', TRUE, envir = webmockr_conf_env)
+}
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_disable <- function() {
+ message("webmockr disabled")
+ assign('turn_on', FALSE, envir = webmockr_conf_env)
+}
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_allow_net_connect <- function() {
+ message("net connect allowed")
+ assign('allow_net_connect', TRUE, envir = webmockr_conf_env)
+}
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_disable_net_connect <- function() {
+ message("net connect disabled")
+ assign('allow_net_connect', FALSE, envir = webmockr_conf_env)
+}
+
+#' @export
+#' @rdname webmockr_configure
+webmockr_net_connect_allowed <- function() {
+ #webmockr_conf_env$allow_net_connect
+ webmockr_conf_env$allow_net_connect
+ #get('allow_net_connect', envir = webmockr_conf_env)
+}
+
+
+
+print.webmockr_config <- function(x, ...) {
+ cat("<webmockr configuration>", sep = "\n")
+ cat(paste0(" enabled?: ", x$turn_on), sep = "\n")
+ cat(paste0(" allow_net_collect?: ", x$allow_net_collect), 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/R/webmockr.R b/R/webmockr.R
new file mode 100644
index 0000000..7c3ce23
--- /dev/null
+++ b/R/webmockr.R
@@ -0,0 +1,24 @@
+#' Stubbing and setting expectations on HTTP requests
+#'
+#' @import magrittr lazyeval R6
+#' @name webmockr-package
+#' @aliases webmockr
+#' @docType package
+#' @keywords package
+#' @author Scott Chamberlain \email{myrmecocystus+r@@gmail.com}
+#'
+#' @section Features:
+#' \itemize{
+#' \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 Can support many HTTP libraries, though only \pkg{crul} for now
+#' \item Integration with testing libraries (coming soon) via `vcr`
+#' }
+#'
+#' @examples
+#' library(webmockr)
+#' stub_request("get", "https://httpbin.org/get")
+#' stub_request("post", "https://httpbin.org/post")
+#' stub_registry()
+NULL
diff --git a/R/wi_th.R b/R/wi_th.R
new file mode 100644
index 0000000..46f14fe
--- /dev/null
+++ b/R/wi_th.R
@@ -0,0 +1,36 @@
+#' Set additional parts of a stubbed request
+#'
+#' Set query params, request body, and/or request headers
+#'
+#' @export
+#' @param .data input. Anything that can be coerced to a `StubbedRequest` class
+#' object
+#' @param ... Comma separated list of variable names, passed on
+#' to [lazyeval::lazy_dots()]. accepts the following: query, body,
+#' headers
+#' @param .dots Used to work around non-standard evaluation
+#' @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 examples in [stub_request()]
+wi_th <- function(.data, ...) {
+ wi_th_(.data, .dots = lazyeval::lazy_dots(...))
+}
+
+#' @export
+#' @rdname wi_th
+wi_th_ <- function(.data, ..., .dots) {
+ tmp <- lazyeval::all_dots(.dots, ...)
+ if (length(tmp) == 0) {
+ z <- NULL
+ } else {
+ z <- lapply(tmp, function(x) eval(x$expr))
+ }
+ .data$with(
+ query = z$query,
+ body = z$body,
+ headers = z$headers
+ )
+ return(.data)
+}
diff --git a/R/zzz.R b/R/zzz.R
new file mode 100644
index 0000000..1983bf6
--- /dev/null
+++ b/R/zzz.R
@@ -0,0 +1,64 @@
+http_verbs <- c("any", "get","post","put","patch","head","delete")
+
+cc <- function(x) Filter(Negate(is.null), x)
+
+hdl_lst <- function(x) {
+ if (is.null(x) || length(x) == 0) return("")
+ if (inherits(x, "list")) {
+ return(paste(names(x), unname(x), sep = "=", collapse = ", "))
+ } else {
+ x
+ }
+}
+
+parseurl <- function(x) {
+ tmp <- urltools::url_parse(x)
+ tmp <- as.list(tmp)
+ if (!is.na(tmp$parameter)) {
+ tmp$parameter <- sapply(strsplit(tmp$parameter, "&")[[1]], function(z) {
+ zz <- strsplit(z, split = "=")[[1]]
+ as.list(stats::setNames(zz[2], zz[1]))
+ }, USE.NAMES = FALSE)
+ }
+ tmp
+}
+
+url_builder <- function(uri, args = NULL) {
+ if (is.null(args)) return(uri)
+ paste0(uri, "?", paste(names(args), args, sep = "=", collapse = ","))
+}
+
+`%||%` <- function(x, y) if (is.null(x)) y else x
+
+assert <- function(x, y) {
+ if (!is.null(x)) {
+ if (!class(x)[1] %in% y) {
+ stop(deparse(substitute(x)), " must be of class ",
+ paste0(y, collapse = ", "), call. = FALSE)
+ }
+ }
+}
+
+crul_head_parse <- function(z) {
+ if (grepl("HTTP\\/", z)) {
+ list(status = z)
+ } else {
+ ff <- regexec("^([^:]*):\\s*(.*)$", z)
+ xx <- regmatches(z, ff)[[1]]
+ as.list(stats::setNames(xx[[3]], tolower(xx[[2]])))
+ }
+}
+
+crul_headers_parse <- function(x) do.call("c", lapply(x, crul_head_parse))
+
+webmockr_crul_fetch <- function(x) {
+ if (is.null(x$disk) && is.null(x$stream)) {
+ curl::curl_fetch_memory(x$url$url, handle = x$url$handle)
+ }
+ else if (!is.null(x$disk)) {
+ curl::curl_fetch_disk(x$url$url, x$disk, handle = x$url$handle)
+ }
+ else {
+ curl::curl_fetch_stream(x$url$url, x$stream, handle = x$url$handle)
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7df89f8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,226 @@
+webmockr
+========
+
+
+
+[![Build Status](https://travis-ci.org/ropensci/webmockr.svg?branch=master)](https://travis-ci.org/ropensci/webmockr)
+[![codecov](https://codecov.io/gh/ropensci/webmockr/branch/master/graph/badge.svg)](https://codecov.io/gh/ropensci/webmockr)
+
+R library for stubbing and setting expectations on HTTP requests.
+
+Port of the Ruby gem [webmock](https://github.com/bblimke/webmock)
+
+
+## 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
+* Support for `testthat` coming soon via [vcr](https://github.com/ropenscilabs/vcr)
+
+## Supported HTTP libraries
+
+* [crul](https://github.com/ropensci/crul)
+
+> more to come
+
+## Install
+
+from cran
+
+
+```r
+install.packages("webmockr")
+```
+
+Dev version
+
+
+```r
+devtools::install_github("ropensci/webmockr")
+```
+
+
+```r
+library(webmockr)
+```
+
+## Turn on webmockr
+
+
+```r
+webmockr::enable()
+#> CrulAdapter enabled!
+#> [1] TRUE
+crul::mock()
+```
+
+## Outside a test framework
+
+
+```r
+library(crul)
+```
+
+### Stubbed request based on uri only and with the default response
+
+
+```r
+stub_request("any", "https://httpbin.org/get")
+#> <webmockr stub>
+#> method: any
+#> uri: https://httpbin.org/get
+#> with:
+#> query:
+#> body:
+#> request_headers:
+#> to_return:
+#> status:
+#> body:
+#> response_headers:
+```
+
+
+```r
+x <- HttpClient$new(url = "https://httpbin.org")
+x$get('get')
+#> $url
+#> $url$url
+#> [1] "https://httpbin.org/get"
+#>
+#> $url$handle
+#> <curl handle> (empty)
+#>
+#>
+#> $method
+#> [1] "get"
+#>
+#> $options
+#> $options$httpget
+#> [1] TRUE
+#>
+#>
+#> $headers
+#> $headers$`User-Agent`
+#> [1] "libcurl/7.51.0 r-curl/2.6 crul/0.3.5.9313"
+#>
+#> $headers$`Accept-Encoding`
+#> [1] "gzip, deflate"
+```
+
+set return objects
+
+
+```r
+stub_request("get", "https://httpbin.org/get") %>%
+ wi_th(
+ query = list(hello = "world")) %>%
+ to_return(status = 418)
+#> <webmockr stub>
+#> method: get
+#> uri: https://httpbin.org/get
+#> with:
+#> query: hello=world
+#> body:
+#> request_headers:
+#> to_return:
+#> status: 418
+#> body:
+#> response_headers:
+```
+
+
+```r
+x$get('get', query = list(hello = "world"))
+#> $url
+#> $url$url
+#> [1] "https://httpbin.org/get?hello=world"
+#>
+#> $url$handle
+#> <curl handle> (empty)
+#>
+#>
+#> $method
+#> [1] "get"
+#>
+#> $options
+#> $options$httpget
+#> [1] TRUE
+#>
+#>
+#> $headers
+#> $headers$`User-Agent`
+#> [1] "libcurl/7.51.0 r-curl/2.6 crul/0.3.5.9313"
+#>
+#> $headers$`Accept-Encoding`
+#> [1] "gzip, deflate"
+```
+
+### Stubbing requests based on method, uri and query params
+
+
+```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',
+ 'Accept-Encoding' = "gzip, deflate"))
+#> <webmockr stub>
+#> method: get
+#> uri: https://httpbin.org/get
+#> with:
+#> query: hello=world
+#> body:
+#> request_headers: User-Agent=libcurl/7.51.0 r-curl/2.6 crul/0.3.6, Accept-Encoding=gzip, deflate
+#> to_return:
+#> status:
+#> body:
+#> response_headers:
+```
+
+
+```r
+stub_registry()
+#> <webmockr stub registry>
+#> Registered Stubs
+#> any: https://httpbin.org/get
+#> 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"}
+```
+
+
+```r
+x <- HttpClient$new(url = "https://httpbin.org")
+x$get('get', query = list(hello = "world"))
+#> $url
+#> $url$url
+#> [1] "https://httpbin.org/get?hello=world"
+#>
+#> $url$handle
+#> <curl handle> (empty)
+#>
+#>
+#> $method
+#> [1] "get"
+#>
+#> $options
+#> $options$httpget
+#> [1] TRUE
+#>
+#>
+#> $headers
+#> $headers$`User-Agent`
+#> [1] "libcurl/7.51.0 r-curl/2.6 crul/0.3.5.9313"
+#>
+#> $headers$`Accept-Encoding`
+#> [1] "gzip, deflate"
+```
+
+## Meta
+
+* Please [report any issues or bugs](https://github.com/ropensci/webmockr/issues).
+* License: MIT
+* Get citation information for `webmockr` in R doing `citation(package = 'webmockr')`
+* Please note that this project is released with a [Contributor Code of Conduct](CONDUCT.md).
+By participating in this project you agree to abide by its terms.
+
+[![ropensci_footer](https://ropensci.org/public_images/github_footer.png)](https://ropensci.org)
diff --git a/inst/ignore/adapter-httr.R b/inst/ignore/adapter-httr.R
new file mode 100644
index 0000000..ff2d21a
--- /dev/null
+++ b/inst/ignore/adapter-httr.R
@@ -0,0 +1,95 @@
+#' httr library adapter
+#'
+#' @export
+
+#' @family http_lib_adapters
+#' @details This adapter modifies \pkg{httr} to allow mocking HTTP requests
+#' when one is using \pkg{httr} in their code
+HttrAdapter <- R6::R6Class(
+ 'HttrAdapter',
+ public = list(
+ name = "httr_adapter",
+
+ enable = function() {
+ message("HttrAdapter enabled!")
+ webmockr_lightswitch$httr <- TRUE
+ },
+
+ disable = function() {
+ message("HttrAdapter disabled!")
+ webmockr_lightswitch$httr <- FALSE
+ },
+
+ build_request_signature = function(x) {
+ RequestSignature$new(
+ method = x$method,
+ uri = x$url,
+ options = list(
+ body = x$body %||% NULL,
+ headers = x$headers %||% NULL
+ )
+ )
+ },
+
+ handle_request = function() {
+ "fadfas"
+ }
+ )
+)
+
+# httr methods to override
+
+## request_perform -> changes:
+## - look in cache for matching request (given user specified matchers)
+## - if it's a match, return the response (body, headers, etc.)
+## - if no match, proceed with http request as normal
+request_perform <- function(req, handle, refresh = TRUE) {
+ stopifnot(httr:::is.request(req), inherits(handle, "curl_handle"))
+ req <- httr:::request_prepare(req)
+
+ curl::handle_setopt(handle, .list = req$options)
+ if (!is.null(req$fields))
+ curl::handle_setform(handle, .list = req$fields)
+ curl::handle_setheaders(handle, .list = req$headers)
+ on.exit(curl::handle_reset(handle), add = TRUE)
+
+ # put request in cache
+ request_signature <- HttrAdapter$build_request_signature(req)
+ webmockr_request_registry$register_request(request_signature)
+
+ if (request_is_in_cache(req)) {
+ StubRegistry$find_stubbed_request(req)
+ } else {
+ resp <- httr:::request_fetch(req$output, req$url, handle)
+
+ # If return 401 and have auth token, refresh it and then try again
+ needs_refresh <- refresh && resp$status_code == 401L &&
+ !is.null(req$auth_token) && req$auth_token$can_refresh()
+ if (needs_refresh) {
+ message("Auto-refreshing stale OAuth token.")
+ req$auth_token$refresh()
+ return(httr:::request_perform(req, handle, refresh = FALSE))
+ }
+
+ all_headers <- httr:::parse_headers(resp$headers)
+ headers <- httr:::last(all_headers)$headers
+ if (!is.null(headers$date)) {
+ date <- httr:::parse_http_date(headers$Date)
+ } else {
+ date <- Sys.time()
+ }
+
+ httr:::response(
+ url = resp$url,
+ status_code = resp$status_code,
+ headers = headers,
+ all_headers = all_headers,
+ cookies = curl::handle_cookies(handle),
+ content = resp$content,
+ date = date,
+ times = resp$times,
+ request = req,
+ handle = handle
+ )
+ }
+}
diff --git a/inst/ignore/sockets.R b/inst/ignore/sockets.R
new file mode 100644
index 0000000..96a5965
--- /dev/null
+++ b/inst/ignore/sockets.R
@@ -0,0 +1,48 @@
+wbenv <- new.env()
+bucket <- new.env()
+
+start_server <- function(x) {
+ app <- list(
+ call = function(req) {
+ wsUrl = paste(sep = '',
+ '"',
+ "ws://",
+ ifelse(is.null(req$HTTP_HOST), req$SERVER_NAME, req$HTTP_HOST),
+ '"')
+
+ tmp <- list(
+ status = 200L,
+ headers = list(
+ 'Content-Type' = 'application/json'
+ ),
+ body = sprintf('{
+ "http_method": "%s",
+ "url": "%s",
+ "port": "%s",
+ "query": "%s",
+ "user_agent": "%s"
+ }', req$REQUEST_METHOD, req$SERVER_NAME,
+ req$SERVER_PORT, req$QUERY_STRING, req$HTTP_USER_AGENT)
+ )
+ assign(basename(tempfile()), tmp, envir = bucket)
+ tmp
+ }
+ )
+ wbenv$server <- startDaemonizedServer("0.0.0.0", 9200, app)
+ #wbenv$server <- startDaemonizedServer("80", 9200, app)
+ message("server started")
+}
+
+stop_server <- function(x = NULL) {
+ stopDaemonizedServer(if (is.null(x)) wbenv$server else x)
+}
+
+bucket_list <- function(x) ls(envir = bucket)
+
+bucket_unique <- function(x) {
+ hashes <- vapply(ls(envir = bucket), function(z) digest::digest(get(z, envir = bucket)), "")
+ if (any(duplicated(hashes))) {
+ torm <- names(hashes)[duplicated(hashes)]
+ invisible(lapply(torm, function(z) rm(list = z, envir = bucket)))
+ }
+}
diff --git a/man/BodyPattern.Rd b/man/BodyPattern.Rd
new file mode 100644
index 0000000..4576ea7
--- /dev/null
+++ b/man/BodyPattern.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestPattern.R
+\docType{data}
+\name{BodyPattern}
+\alias{BodyPattern}
+\title{BodyPattern}
+\arguments{
+\item{pattern}{(list) a body object}
+}
+\description{
+BodyPattern
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{matches(body, content_type = "")}}{
+Match a body object against that given in \code{pattern}
+- body (list) the body
+- content_type (character) content type
+}
+}
+}
+\examples{
+z <- BodyPattern$new(pattern = list(a = "foobar"))
+z$pattern
+}
+\keyword{internal}
diff --git a/man/CrulAdapter.Rd b/man/CrulAdapter.Rd
new file mode 100644
index 0000000..4188749
--- /dev/null
+++ b/man/CrulAdapter.Rd
@@ -0,0 +1,39 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/adapter-crul.R
+\docType{data}
+\name{CrulAdapter}
+\alias{CrulAdapter}
+\title{crul library adapter}
+\description{
+crul library adapter
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{enable()}}{
+Enable the adapter
+}
+\item{\code{disable()}}{
+Disable the adapter
+}
+\item{\code{build_crul_request(x)}}{
+Build a crul \link{RequestSignature}
+x: crul request parts (list)
+}
+\item{\code{build_crul_response(req, resp)}}{
+Build a crul response
+req: a crul request (list)
+resp: a crul response ()
+}
+\item{\code{handle_request()}}{
+All logic for handling a request
+req: a crul request (list)
+}
+\item{\code{remove_crul_stubs()}}{
+Remove all crul stubs
+}
+}
+
+This adapter modifies \pkg{crul} to allow mocking HTTP requests
+}
+\keyword{datasets}
diff --git a/man/HashCounter.Rd b/man/HashCounter.Rd
new file mode 100644
index 0000000..6d84248
--- /dev/null
+++ b/man/HashCounter.Rd
@@ -0,0 +1,34 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestRegistry.R
+\docType{data}
+\name{HashCounter}
+\alias{HashCounter}
+\title{hash with counter, to store requests, and count each time it is used}
+\description{
+hash with counter, to store requests, and count each time it is used
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{put(key)}}{
+Register a request by it's key
+- key: a character string of the request, serialized from
+\link{CrulAdapter} or other adapter
+}
+\item{\code{get(key)}}{
+Get a request by key
+- key: a character string of the request, serialized from
+\link{CrulAdapter} or other adapter
+}
+}
+}
+\examples{
+x <- HashCounter$new()
+x$put("foo bar")
+x$put("foo bar")
+x$put("hello world")
+x$put("hello world")
+x$put("hello world")
+x$hash
+}
+\keyword{datasets}
diff --git a/man/HeadersPattern.Rd b/man/HeadersPattern.Rd
new file mode 100644
index 0000000..da99d61
--- /dev/null
+++ b/man/HeadersPattern.Rd
@@ -0,0 +1,46 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestPattern.R
+\docType{data}
+\name{HeadersPattern}
+\alias{HeadersPattern}
+\title{HeadersPattern}
+\arguments{
+\item{pattern}{(list) a pattern, as a named list, must be named,
+e.g,. \code{list(a = 5, b = 6)}}
+}
+\description{
+HeadersPattern
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{matches(headers)}}{
+Match a list of headers against that stored
+- headers (list) named list of headers, e.g,. \code{list(a = 5, b = 6)}
+}
+}
+
+\code{webmockr} normalises headers and treats all forms of same headers as equal:
+i.e the following two sets of headers are equal:
+\code{list(Header1 = "value1", content_length = 123, X_CuStOm_hEAder = "foo")}
+and
+\code{list(header1 = "value1", "Content-Length" = 123, "x-cuSTOM-HeAder" = "foo")}
+}
+\examples{
+(x <- HeadersPattern$new(pattern = list(a = 5)))
+x$pattern
+x$matches(list(a = 5))
+
+# different cases
+(x <- HeadersPattern$new(pattern = list(Header1 = "value1")))
+x$pattern
+x$matches(list(header1 = "value1"))
+x$matches(list(header1 = "value2"))
+
+# different symbols
+(x <- HeadersPattern$new(pattern = list(`Hello_World` = "yep")))
+x$pattern
+x$matches(list(`hello-world` = "yep"))
+x$matches(list(`hello-worlds` = "yep"))
+}
+\keyword{internal}
diff --git a/man/HttpLibAdapaterRegistry.Rd b/man/HttpLibAdapaterRegistry.Rd
new file mode 100644
index 0000000..424b2fc
--- /dev/null
+++ b/man/HttpLibAdapaterRegistry.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/HttpLibAdapterRegistry.R
+\docType{data}
+\name{HttpLibAdapaterRegistry}
+\alias{HttpLibAdapaterRegistry}
+\title{http lib adapter registry}
+\description{
+http lib adapter registry
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{register(x)}}{
+Register an http library adapter
+x: an http lib adapter, e.g., \link{CrulAdapter}
+return: nothing, registers the library adapter
+}
+}
+}
+\examples{
+x <- HttpLibAdapaterRegistry$new()
+x$register(CrulAdapter$new())
+x
+x$adapters
+x$adapters[[1]]$name
+}
+\keyword{datasets}
diff --git a/man/MethodPattern.Rd b/man/MethodPattern.Rd
new file mode 100644
index 0000000..38edec7
--- /dev/null
+++ b/man/MethodPattern.Rd
@@ -0,0 +1,30 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestPattern.R
+\docType{data}
+\name{MethodPattern}
+\alias{MethodPattern}
+\title{MethodPattern}
+\arguments{
+\item{pattern}{(character) a HTTP method, lowercase}
+}
+\description{
+MethodPattern
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{matches(method)}}{
+An HTTP method
+- method (character)
+}
+}
+
+Matches regardless of case. e.g., POST will match to post
+}
+\examples{
+(x <- MethodPattern$new(pattern = "post"))
+x$pattern
+x$matches(method = "post")
+x$matches(method = "POST")
+}
+\keyword{internal}
diff --git a/man/RequestPattern.Rd b/man/RequestPattern.Rd
new file mode 100644
index 0000000..3b9f85b
--- /dev/null
+++ b/man/RequestPattern.Rd
@@ -0,0 +1,69 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestPattern.R
+\docType{data}
+\name{RequestPattern}
+\alias{RequestPattern}
+\title{RequestPattern class}
+\arguments{
+\item{method}{the HTTP method (any, head, options, get, post, put,
+patch, trace, or delete). "any" matches any HTTP method. required.}
+
+\item{uri}{(character) request URI. required or uri_regex}
+
+\item{uri_regex}{(character) request URI as regex. required or uri}
+
+\item{query}{(list) query parameters, optional}
+
+\item{body}{(list) body request, optional}
+
+\item{headers}{(list) headers, optional}
+}
+\description{
+RequestPattern class
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{matches(request_signature)}}{
+Test if request_signature matches a pattern
+- request_signature: a request signature
+}
+\item{\code{to_s()}}{
+Print pattern for easy human consumption
+}
+}
+}
+\examples{
+\dontrun{
+(x <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get"))
+x$body_pattern
+x$headers_pattern
+x$method_pattern
+x$uri_pattern
+x$to_s()
+
+# make a request signature
+rs <- RequestSignature$new(method = "get", uri = "https://httpbin.org/get")
+
+# check if it matches
+x$matches(rs)
+
+# regex uri
+(x <- RequestPattern$new(method = "get", uri_regex = ".+ossref.org"))
+x$uri_pattern
+x$uri_pattern$to_s()
+x$to_s()
+
+# uri with query parameters
+(x <- RequestPattern$new(
+ method = "get", uri = "https://httpbin.org/get",
+ query = list(foo = "bar")
+))
+x$to_s()
+}
+}
+\seealso{
+pattern classes for HTTP method \link{MethodPattern}, headers
+\link{HeadersPattern}, body \link{BodyPattern}, and URI/URL \link{UriPattern}
+}
+\keyword{datasets}
diff --git a/man/RequestRegistry.Rd b/man/RequestRegistry.Rd
new file mode 100644
index 0000000..bf763e9
--- /dev/null
+++ b/man/RequestRegistry.Rd
@@ -0,0 +1,37 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestRegistry.R
+\docType{data}
+\name{RequestRegistry}
+\alias{RequestRegistry}
+\title{Request registry}
+\description{
+Request registry
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{register_request(request)}}{
+Register a request
+- request: a character string of the request, serialized from
+\link{CrulAdapter} or other adapter
+}
+\item{\code{reset()}}{
+Reset the registry to no registered requests
+}
+}
+}
+\examples{
+x <- RequestRegistry$new()
+x$register_request(request = "GET http://scottchamberlain.info")
+x$register_request(request = "GET http://scottchamberlain.info")
+x$register_request(request = "POST https://httpbin.org/post")
+# print method to list requests
+x
+
+# hashes, and number of times each requested
+x$request_signatures$hash
+
+# reset the request registry
+x$reset()
+}
+\keyword{datasets}
diff --git a/man/RequestSignature.Rd b/man/RequestSignature.Rd
new file mode 100644
index 0000000..d6d4cf9
--- /dev/null
+++ b/man/RequestSignature.Rd
@@ -0,0 +1,71 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestSignature.R
+\docType{data}
+\name{RequestSignature}
+\alias{RequestSignature}
+\title{General purpose request signature builder}
+\arguments{
+\item{method}{the HTTP method (any, head, options, get, post, put,
+patch, trace, or delete). "any" matches any HTTP method. required.}
+
+\item{uri}{(character) request URI. required.}
+
+\item{options}{(list) options. optional. See Details.}
+}
+\description{
+General purpose request signature builder
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{to_s()}}{
+Request signature to a string
+return: a character string representation of the request signature
+}
+}
+}
+\section{options}{
+
+\itemize{
+\item body - body as a named list
+\item headers - headers as a named list
+\item proxies - proxies as a named list
+\item auth - authentication details, as a named list
+}
+}
+
+\examples{
+# make request signature
+x <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get")
+# method
+x$method
+# uri
+x$uri
+# request signature to string
+x$to_s()
+
+# headers
+z <- RequestSignature$new(
+ method = "get",
+ uri = "https:/httpbin.org/get",
+ options = list(headers = list(`User-Agent` = "foobar", stuff = "things"))
+)
+z
+z$headers
+z$to_s()
+
+# headers and body
+z <- RequestSignature$new(
+ method = "get",
+ uri = "https:/httpbin.org/get",
+ options = list(
+ headers = list(`User-Agent` = "foobar", stuff = "things"),
+ body = list(a = "tables")
+ )
+)
+z
+z$headers
+z$body
+z$to_s()
+}
+\keyword{datasets}
diff --git a/man/Response.Rd b/man/Response.Rd
new file mode 100644
index 0000000..1139863
--- /dev/null
+++ b/man/Response.Rd
@@ -0,0 +1,78 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/Response.R
+\docType{data}
+\name{Response}
+\alias{Response}
+\title{Response class}
+\arguments{
+\item{options}{(list) a list of options}
+}
+\description{
+Response class
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{set_request_headers(headers)}}{
+set request headers
+- headers: a list of key-value pair headers
+}
+\item{\code{get_request_headers()}}{
+get request headers
+}
+\item{\code{set_response_headers(headers)}}{
+set response headers
+- headers: a list of key-value pair headers
+}
+\item{\code{get_response_headers()}}{
+get response headers
+}
+\item{\code{set_body(body)}}{
+- body: must be a string
+}
+\item{\code{get_body()}}{
+get body
+}
+\item{\code{set_status()}}{
+- body: must be an integer status code
+}
+\item{\code{get_status()}}{
+get status code
+}
+\item{\code{set_exception()}}{
+set exception
+}
+\item{\code{get_exception()}}{
+get exception
+}
+}
+}
+\examples{
+\dontrun{
+(x <- Response$new())
+
+x$set_url("https://httpbin.org/get")
+x
+
+x$set_request_headers(list('Content-Type' = "application/json"))
+x
+x$request_headers
+
+x$set_response_headers(list('Host' = "httpbin.org"))
+x
+x$response_headers
+
+x$set_status(404)
+x
+x$get_status()
+
+x$set_body("hello world")
+x
+x$get_body()
+
+x$set_exception("exception")
+x
+x$get_exception()
+}
+}
+\keyword{datasets}
diff --git a/man/StubRegistry.Rd b/man/StubRegistry.Rd
new file mode 100644
index 0000000..349a42f
--- /dev/null
+++ b/man/StubRegistry.Rd
@@ -0,0 +1,67 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/StubRegistry.R
+\docType{data}
+\name{StubRegistry}
+\alias{StubRegistry}
+\title{Stub registry}
+\description{
+Stub registry
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{register_stub(stub)}}{
+Register a stub
+- stub: an object of class \link{StubbedRequest}
+}
+\item{\code{find_stubbed_request(req)}}{
+Find a stubbed request
+- req: an object of class \link{RequestSignature}
+}
+\item{\code{response_for_request(request_signature)}}{
+Find a stubbed request
+- request_signature: an object of class \link{RequestSignature}
+}
+\item{\code{request_stub_for(request_signature)}}{
+Find a stubbed request
+- request_signature: an object of class \link{RequestSignature}
+}
+\item{\code{remove_request_stub(stub)}}{
+Remove a stubbed request by matching request signature
+- stub: an object of class \link{StubbedRequest}
+}
+\item{\code{remove_all_request_stubs()}}{
+Remove all request stubs
+}
+\item{\code{is_registered(x)}}{
+Find a stubbed request
+- x: an object of class \link{RequestSignature}
+}
+}
+}
+\examples{
+\dontrun{
+# Make a stub
+stub1 <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+stub1$with(request_headers = list('User-Agent' = 'R'))
+stub1$to_return(status = 200, body = "foobar", response_headers = list())
+stub1
+
+# Make another stub
+stub2 <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+stub2
+
+# Put both stubs in the stub registry
+reg <- StubRegistry$new()
+reg$register_stub(stub = stub1)
+reg$register_stub(stub = stub2)
+reg
+reg$request_stubs
+}
+}
+\seealso{
+Other stub-registry: \code{\link{remove_request_stub}},
+ \code{\link{stub_registry_clear}},
+ \code{\link{stub_registry}}
+}
+\keyword{datasets}
diff --git a/man/StubbedRequest.Rd b/man/StubbedRequest.Rd
new file mode 100644
index 0000000..18ddd6c
--- /dev/null
+++ b/man/StubbedRequest.Rd
@@ -0,0 +1,64 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/StubbedRequest.R
+\docType{data}
+\name{StubbedRequest}
+\alias{StubbedRequest}
+\title{StubbedRequest class}
+\arguments{
+\item{method}{the HTTP method (any, head, get, post, put,
+patch, or delete). "any" matches any HTTP method. required.}
+
+\item{uri}{(character) request URI. either this or \code{uri_regex}
+required}
+
+\item{uri_regex}{(character) request URI as regex. either this or \code{uri}
+required}
+}
+\description{
+StubbedRequest class
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{with(query, body, headers)}}{
+Set expectations for what's given in HTTP request
+\itemize{
+\item query (list) request query params, as a named list. optional
+\item body (list) request body, as a named list. optional
+\item headers (list) request headers as a named list. optional.
+}
+}
+\item{\code{to_return(status, body, headers)}}{
+Set expectations for what's returned in HTTP resonse
+\itemize{
+\item status (numeric) an HTTP status code
+\item body (list) response body, as a list. optional
+\item headers (list) named list, response headers. optional.
+}
+}
+\item{\code{to_s()}}{
+Response as a string
+}
+}
+}
+\examples{
+\dontrun{
+x <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+x$method
+x$uri
+x$with(headers = list('User-Agent' = 'R'))
+x$to_return(status = 200, body = "foobar", headers = list(a = 5))
+x
+x$to_s()
+
+# uri_regex
+(x <- StubbedRequest$new(method = "get", uri_regex = ".+ossref.org"))
+x$method
+x$uri
+x$to_s()
+}
+}
+\seealso{
+\code{\link[=stub_request]{stub_request()}}
+}
+\keyword{datasets}
diff --git a/man/UriPattern.Rd b/man/UriPattern.Rd
new file mode 100644
index 0000000..5a006f1
--- /dev/null
+++ b/man/UriPattern.Rd
@@ -0,0 +1,60 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/RequestPattern.R
+\docType{data}
+\name{UriPattern}
+\alias{UriPattern}
+\title{UriPattern}
+\arguments{
+\item{pattern}{(character) a uri, either plain character string or
+regex, see \link[base:regex]{base::regex}. if scheme is missing, it is added (we assume
+http)}
+}
+\description{
+UriPattern
+}
+\details{
+\strong{Methods}
+\describe{
+\item{\code{add_query_params}}{
+Add query parameters to the URI
+- query_params
+}
+\item{\code{matches(uri)}}{
+Match a uri against that given in \code{pattern}
+- uri (character) a uri, including scheme (i.e., http or https)
+}
+}
+}
+\examples{
+# trailing slash
+(z <- UriPattern$new(pattern = "http://foobar.com"))
+z$matches("http://foobar.com")
+z$matches("http://foobar.com/")
+
+# default ports
+(z <- UriPattern$new(pattern = "http://foobar.com"))
+z$matches("http://foobar.com:80")
+z$matches("http://foobar.com:80/")
+z$matches("http://foobar.com:443")
+z$matches("http://foobar.com:443/")
+
+# user info
+(z <- UriPattern$new(pattern = "http://foobar.com"))
+z$matches("http://user:pass@foobar.com")
+
+# regex
+(z <- UriPattern$new(regex_pattern = ".+ample\\\\.."))
+z$matches("http://sample.org")
+z$matches("http://example.com")
+z$matches("http://tramples.net")
+
+# add query parameters
+(z <- UriPattern$new(pattern = "http://foobar.com"))
+z$add_query_params(list(pizza = "cheese", cheese = "cheddar"))
+z$pattern
+
+(z <- UriPattern$new(pattern = "http://foobar.com"))
+z$add_query_params(list(pizza = "deep dish", cheese = "cheddar"))
+z$pattern
+}
+\keyword{internal}
diff --git a/man/enable.Rd b/man/enable.Rd
new file mode 100644
index 0000000..da2bee7
--- /dev/null
+++ b/man/enable.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/flipswitch.R
+\name{enable}
+\alias{enable}
+\alias{disable}
+\title{Enable or disable webmockr}
+\usage{
+enable(options = list())
+
+disable(options = list())
+}
+\arguments{
+\item{options}{list of options - ignored for now.}
+}
+\description{
+Enable or disable webmockr
+}
diff --git a/man/pipe.Rd b/man/pipe.Rd
new file mode 100644
index 0000000..1c20897
--- /dev/null
+++ b/man/pipe.Rd
@@ -0,0 +1,12 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/pipe.R
+\name{\%>\%}
+\alias{\%>\%}
+\title{Pipe operator}
+\usage{
+lhs \%>\% rhs
+}
+\description{
+Pipe operator
+}
+\keyword{internal}
diff --git a/man/remove_request_stub.Rd b/man/remove_request_stub.Rd
new file mode 100644
index 0000000..26317c1
--- /dev/null
+++ b/man/remove_request_stub.Rd
@@ -0,0 +1,28 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/remove_request_stub.R
+\name{remove_request_stub}
+\alias{remove_request_stub}
+\title{Remove a request stub}
+\usage{
+remove_request_stub(stub)
+}
+\arguments{
+\item{stub}{a request stub, of class \code{StubbedRequest}}
+}
+\value{
+logical, \code{TRUE} if removed, \code{FALSE} if not removed
+}
+\description{
+Remove a request stub
+}
+\examples{
+(x <- stub_request("get", "https://httpbin.org/get"))
+stub_registry()
+remove_request_stub(x)
+stub_registry()
+}
+\seealso{
+Other stub-registry: \code{\link{StubRegistry}},
+ \code{\link{stub_registry_clear}},
+ \code{\link{stub_registry}}
+}
diff --git a/man/stub_registry.Rd b/man/stub_registry.Rd
new file mode 100644
index 0000000..3035535
--- /dev/null
+++ b/man/stub_registry.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/stub_registry.R
+\name{stub_registry}
+\alias{stub_registry}
+\title{List stubs in the stub registry}
+\usage{
+stub_registry()
+}
+\value{
+an object of class \code{StubRegistry}, print method gives the
+stubs in the registry
+}
+\description{
+List stubs in the stub registry
+}
+\seealso{
+Other stub-registry: \code{\link{StubRegistry}},
+ \code{\link{remove_request_stub}},
+ \code{\link{stub_registry_clear}}
+}
diff --git a/man/stub_registry_clear.Rd b/man/stub_registry_clear.Rd
new file mode 100644
index 0000000..bdfcb4a
--- /dev/null
+++ b/man/stub_registry_clear.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/stub_registry_clear.R
+\name{stub_registry_clear}
+\alias{stub_registry_clear}
+\title{Clear the stub registry}
+\usage{
+stub_registry_clear()
+}
+\value{
+nothing
+}
+\description{
+Clear all stubs
+}
+\seealso{
+Other stub-registry: \code{\link{StubRegistry}},
+ \code{\link{remove_request_stub}},
+ \code{\link{stub_registry}}
+}
diff --git a/man/stub_request.Rd b/man/stub_request.Rd
new file mode 100644
index 0000000..9364564
--- /dev/null
+++ b/man/stub_request.Rd
@@ -0,0 +1,64 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/stub_request.R
+\name{stub_request}
+\alias{stub_request}
+\title{Stub an http request}
+\usage{
+stub_request(method = "get", uri = NULL, uri_regex = NULL)
+}
+\arguments{
+\item{method}{(character) HTTP method, one of "get", "post", "put", "patch",
+"head", "delete", "options" - or the special "any" (for any method)}
+
+\item{uri}{(character) The request uri. Can be a full uri, partial, or a
+regular expression to match many incantations of a uri. required.}
+
+\item{uri_regex}{(character) A URI represented as regex. See examples}
+}
+\value{
+an object of class \code{StubbedRequest}, with print method describing
+the stub.
+}
+\description{
+Stub an http request
+}
+\details{
+Internally, this calls \link{StubbedRequest} which handles the logic
+
+See \code{\link[=stub_registry]{stub_registry()}} for listing stubs, \code{\link[=stub_registry_clear]{stub_registry_clear()}}
+for removing all stubs and \code{\link[=remove_request_stub]{remove_request_stub()}} for removing specific
+stubs
+}
+\examples{
+\dontrun{
+# basic stubbing
+stub_request("get", "https://httpbin.org/get")
+stub_request("post", "https://httpbin.org/post")
+
+# list stubs
+stub_registry()
+
+# add header
+stub_request("get", "https://httpbin.org/get") \%>\%
+ wi_th(headers = list('User-Agent' = 'R'))
+
+# add expectation with to_return
+stub_request("get", "https://httpbin.org/get") \%>\%
+ wi_th(
+ query = list(hello = "world"),
+ headers = list('User-Agent' = 'R')) \%>\%
+ to_return(status = 200, body = "stuff", headers = list(a = 5))
+
+# list stubs again
+stub_registry()
+
+# regex
+stub_request("get", uri_regex = ".+ample\\\\..")
+
+# clear all stubs
+stub_registry_clear()
+}
+}
+\seealso{
+\code{\link[=wi_th]{wi_th()}}, \code{\link[=to_return]{to_return()}}
+}
diff --git a/man/to_return.Rd b/man/to_return.Rd
new file mode 100644
index 0000000..a5a5ccb
--- /dev/null
+++ b/man/to_return.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/to_return.R
+\name{to_return}
+\alias{to_return}
+\alias{to_return_}
+\title{Expectation for what's returned from a stubbed request}
+\usage{
+to_return(.data, ...)
+
+to_return_(.data, ..., .dots)
+}
+\arguments{
+\item{.data}{input. Anything that can be coerced to a \code{StubbedRequest} class
+object}
+
+\item{...}{Comma separated list of variable names, passed on
+to \code{\link[lazyeval:lazy_dots]{lazyeval::lazy_dots()}}. accepts the following: status, body,
+headers}
+
+\item{.dots}{Used to work around non-standard evaluation}
+}
+\value{
+an object of class \code{StubbedRequest}, with print method describing
+the stub
+}
+\description{
+Set response status code, response body, and/or response headers
+}
+\note{
+see examples in \code{\link[=stub_request]{stub_request()}}
+}
diff --git a/man/webmockr-package.Rd b/man/webmockr-package.Rd
new file mode 100644
index 0000000..95f7c0d
--- /dev/null
+++ b/man/webmockr-package.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/webmockr.R
+\docType{package}
+\name{webmockr-package}
+\alias{webmockr-package}
+\alias{webmockr}
+\title{Stubbing and setting expectations on HTTP requests}
+\description{
+Stubbing and setting expectations on HTTP requests
+}
+\section{Features}{
+
+\itemize{
+\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 Can support many HTTP libraries, though only \pkg{crul} for now
+\item Integration with testing libraries (coming soon) via \code{vcr}
+}
+}
+
+\examples{
+library(webmockr)
+stub_request("get", "https://httpbin.org/get")
+stub_request("post", "https://httpbin.org/post")
+stub_registry()
+}
+\author{
+Scott Chamberlain \email{myrmecocystus+r at gmail.com}
+}
+\keyword{package}
diff --git a/man/webmockr_configure.Rd b/man/webmockr_configure.Rd
new file mode 100644
index 0000000..f9985e7
--- /dev/null
+++ b/man/webmockr_configure.Rd
@@ -0,0 +1,67 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/webmockr-opts.R
+\name{webmockr_configure}
+\alias{webmockr_configure}
+\alias{webmockr_configure_reset}
+\alias{webmockr_configuration}
+\alias{webmockr_enable}
+\alias{webmockr_disable}
+\alias{webmockr_allow_net_connect}
+\alias{webmockr_disable_net_connect}
+\alias{webmockr_net_connect_allowed}
+\title{webmockr configuration}
+\usage{
+webmockr_configure(turn_on = FALSE, allow_net_connect = FALSE,
+ allow_localhost = FALSE, allow = FALSE,
+ net_http_connect_on_start = FALSE, show_stubbing_instructions = FALSE,
+ query_values_notation = FALSE, show_body_diff = FALSE)
+
+webmockr_configure_reset()
+
+webmockr_configuration()
+
+webmockr_enable()
+
+webmockr_disable()
+
+webmockr_allow_net_connect()
+
+webmockr_disable_net_connect()
+
+webmockr_net_connect_allowed()
+}
+\arguments{
+\item{turn_on}{(logical) Default: \code{FALSE}}
+
+\item{allow_net_connect}{(logical) Default: \code{TRUE}}
+
+\item{allow_localhost}{(logical) Default: \code{TRUE}}
+
+\item{allow}{(logical) Default: \code{TRUE}}
+
+\item{net_http_connect_on_start}{(logical) Default: \code{TRUE}}
+
+\item{show_stubbing_instructions}{(logical) Default: \code{TRUE}}
+
+\item{query_values_notation}{(logical) Default: \code{TRUE}}
+
+\item{show_body_diff}{(logical) Default: \code{TRUE}}
+}
+\description{
+webmockr configuration
+}
+\examples{
+\dontrun{
+webmockr_configure()
+webmockr_configure(
+ allow_localhost = TRUE
+)
+webmockr_configuration()
+webmockr_configure_reset()
+
+webmockr_allow_net_connect()
+webmockr_net_connect_allowed()
+webmockr_disable_net_connect()
+webmockr_net_connect_allowed()
+}
+}
diff --git a/man/wi_th.Rd b/man/wi_th.Rd
new file mode 100644
index 0000000..99a7984
--- /dev/null
+++ b/man/wi_th.Rd
@@ -0,0 +1,35 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/wi_th.R
+\name{wi_th}
+\alias{wi_th}
+\alias{wi_th_}
+\title{Set additional parts of a stubbed request}
+\usage{
+wi_th(.data, ...)
+
+wi_th_(.data, ..., .dots)
+}
+\arguments{
+\item{.data}{input. Anything that can be coerced to a \code{StubbedRequest} class
+object}
+
+\item{...}{Comma separated list of variable names, passed on
+to \code{\link[lazyeval:lazy_dots]{lazyeval::lazy_dots()}}. accepts the following: query, body,
+headers}
+
+\item{.dots}{Used to work around non-standard evaluation}
+}
+\value{
+an object of class \code{StubbedRequest}, with print method describing
+the stub
+}
+\description{
+Set query params, request body, and/or request headers
+}
+\details{
+\code{with} is a function in the \code{base} package, so we went with
+\code{wi_th}
+}
+\note{
+see examples in \code{\link[=stub_request]{stub_request()}}
+}
diff --git a/tests/test-all.R b/tests/test-all.R
new file mode 100644
index 0000000..59f641b
--- /dev/null
+++ b/tests/test-all.R
@@ -0,0 +1,2 @@
+library("testthat")
+test_check("webmockr")
diff --git a/tests/testthat/crul_obj.rda b/tests/testthat/crul_obj.rda
new file mode 100644
index 0000000..cd0b190
Binary files /dev/null and b/tests/testthat/crul_obj.rda differ
diff --git a/tests/testthat/test-CrulAdapter.R b/tests/testthat/test-CrulAdapter.R
new file mode 100644
index 0000000..b0c9f1c
--- /dev/null
+++ b/tests/testthat/test-CrulAdapter.R
@@ -0,0 +1,59 @@
+context("CrulAdapter")
+
+aa <- CrulAdapter$new()
+
+test_that("CrulAdapter bits are correct", {
+ skip_on_cran()
+
+ expect_is(CrulAdapter, "R6ClassGenerator")
+
+ expect_is(aa, "CrulAdapter")
+ expect_is(aa$build_crul_request, "function")
+ expect_is(aa$build_crul_response, "function")
+ expect_is(aa$disable, "function")
+ expect_is(aa$enable, "function")
+ expect_is(aa$handle_request, "function")
+ expect_is(aa$remove_crul_stubs, "function")
+ expect_is(aa$name, "character")
+
+ expect_equal(aa$name, "crul_adapter")
+})
+
+
+test_that("CrulAdapter behaves correctly", {
+ skip_on_cran()
+
+ expect_message(aa$enable(), "CrulAdapter enabled!")
+ expect_message(aa$disable(), "CrulAdapter disabled!")
+})
+
+
+test_that("CrulAdapter fails well", {
+ skip_on_cran()
+
+ expect_error(aa$build_crul_request(), "argument \"x\" is missing")
+ expect_error(aa$build_crul_response(), "argument \"req\" is missing")
+})
+
+
+context("CrulAdapter - with real data")
+test_that("CrulAdapter works", {
+ skip_on_cran()
+
+ load("crul_obj.rda")
+
+ res <- CrulAdapter$new()
+ expect_error(
+ res$handle_request(crul_obj),
+ "Real HTTP connections are disabled.\nUnregistered request: GET http://localhost:9000/get\n\nYou can stub this request with the following snippet"
+ )
+
+ invisible(stub_request("get", "http://localhost:9000/get"))
+
+ aa <- res$handle_request(crul_obj)
+
+ expect_is(res, "CrulAdapter")
+ expect_is(aa, "HttpResponse")
+ expect_equal(aa$method, "get")
+ expect_equal(aa$url, "http://localhost:9000/get")
+})
diff --git a/tests/testthat/test-HashCounter.R b/tests/testthat/test-HashCounter.R
new file mode 100644
index 0000000..b5ac543
--- /dev/null
+++ b/tests/testthat/test-HashCounter.R
@@ -0,0 +1,42 @@
+context("HashCounter")
+
+test_that("HashCounter: structure", {
+ expect_is(HashCounter, "R6ClassGenerator")
+
+ x <- HashCounter$new()
+ expect_is(x, "HashCounter")
+
+ expect_is(x$clone, "function")
+ expect_is(x$get, "function")
+ expect_is(x$put, "function")
+
+ expect_is(x$hash, "list")
+})
+
+test_that("HashCounter: works as expected", {
+ x <- HashCounter$new()
+
+ x$put("foo bar")
+ expect_length(x$hash, 1)
+ expect_equal(x$hash$`foo bar`, 1)
+
+ x$put("foo bar")
+ expect_length(x$hash, 1)
+ expect_equal(x$hash$`foo bar`, 2)
+
+ x$put("hello world")
+ expect_length(x$hash, 2)
+ expect_equal(x$hash$`hello world`, 1)
+
+ x$put("hello world")
+ x$put("hello world")
+ expect_length(x$hash, 2)
+ expect_equal(x$hash$`hello world`, 3)
+})
+
+test_that("HashCounter fails well", {
+ x <- HashCounter$new()
+
+ expect_error(x$get(), "'key' required")
+ expect_error(x$put(), "'key' required")
+})
diff --git a/tests/testthat/test-HttpLibAdapaterRegistry.R b/tests/testthat/test-HttpLibAdapaterRegistry.R
new file mode 100644
index 0000000..1fa4ed8
--- /dev/null
+++ b/tests/testthat/test-HttpLibAdapaterRegistry.R
@@ -0,0 +1,38 @@
+context("HttpLibAdapaterRegistry")
+
+test_that("HttpLibAdapaterRegistry: structure", {
+ expect_is(HttpLibAdapaterRegistry, "R6ClassGenerator")
+
+ aa <- HttpLibAdapaterRegistry$new()
+
+ expect_is(aa, "HttpLibAdapaterRegistry")
+
+ expect_null(aa$adapters)
+ expect_is(aa$clone, "function")
+ expect_is(aa$print, "function")
+ expect_is(aa$register, "function")
+
+ expect_output(print(aa), "HttpLibAdapaterRegistry")
+})
+
+test_that("HttpLibAdapaterRegistry: behaves as expected", {
+ skip_on_cran()
+
+ aa <- HttpLibAdapaterRegistry$new()
+ aa$register(CrulAdapter$new())
+
+ expect_length(aa$adapters, 1)
+ expect_is(aa$adapters[[1]], "CrulAdapter")
+ expect_equal(aa$adapters[[1]]$name, "crul_adapter")
+
+ expect_output(print(aa), "HttpLibAdapaterRegistry")
+ expect_output(print(aa), "crul_adapter")
+})
+
+test_that("HttpLibAdapaterRegistry fails well", {
+ x <- HttpLibAdapaterRegistry$new()
+
+ expect_error(x$register(), "argument \"x\" is missing")
+ expect_error(x$register(4),
+ "'x' must be an adapter, such as CrulAdapter")
+})
diff --git a/tests/testthat/test-RequestPattern.R b/tests/testthat/test-RequestPattern.R
new file mode 100644
index 0000000..098c581
--- /dev/null
+++ b/tests/testthat/test-RequestPattern.R
@@ -0,0 +1,76 @@
+context("RequestPattern")
+
+test_that("RequestPattern: structure is correct", {
+ expect_is(RequestPattern, "R6ClassGenerator")
+
+ aa <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get")
+
+ expect_is(aa, "RequestPattern")
+ expect_null(aa$body_pattern)
+ expect_null(aa$headers_pattern)
+ expect_is(aa$clone, "function")
+ expect_is(aa$initialize, "function")
+ expect_is(aa$matches, "function")
+ expect_is(aa$method_pattern, "MethodPattern")
+ expect_is(aa$to_s, "function")
+ expect_is(aa$uri_pattern, "UriPattern")
+})
+
+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")
+
+ expect_true(aa$matches(rs1))
+ expect_false(aa$matches(rs2))
+
+ expect_is(aa$to_s(), "character")
+ expect_match(aa$to_s(), "GET")
+ expect_match(aa$to_s(), "httpbin.org/get")
+})
+
+test_that("RequestPattern fails well", {
+ x <- RequestPattern$new(method = "get", uri = "https://httpbin.org/get")
+
+ expect_error(x$matches(), "argument \"request_signature\" is missing")
+ expect_error(x$matches("adfadf"),
+ "request_signature must be of class RequestSignature")
+})
+
+
+context("MethodPattern")
+test_that("MethodPattern: structure is correct", {
+ expect_is(MethodPattern, "R6ClassGenerator")
+
+ aa <- MethodPattern$new(pattern = "get")
+
+ expect_is(aa, "MethodPattern")
+ expect_is(aa$pattern, "character")
+ expect_equal(aa$pattern, "get")
+ expect_true(aa$matches(method = "get"))
+ expect_false(aa$matches(method = "post"))
+
+ expect_error(
+ expect_is(aa$matches(), "function"),
+ "argument \"method\" is missing"
+ )
+})
+
+
+context("HeadersPattern")
+test_that("HeadersPattern: structure is correct", {
+ expect_is(HeadersPattern, "R6ClassGenerator")
+
+ aa <- HeadersPattern$new(pattern = list(a = 5))
+
+ expect_is(aa, "HeadersPattern")
+ expect_is(aa$pattern, "list")
+ expect_named(aa$pattern, "a")
+ expect_true(aa$matches(headers = list(a = 5)))
+ expect_false(aa$matches(headers = list(a = 6)))
+
+ expect_error(
+ expect_is(aa$matches(), "function"),
+ "argument \"headers\" is missing"
+ )
+})
diff --git a/tests/testthat/test-RequestRegistry.R b/tests/testthat/test-RequestRegistry.R
new file mode 100644
index 0000000..5de50df
--- /dev/null
+++ b/tests/testthat/test-RequestRegistry.R
@@ -0,0 +1,48 @@
+context("RequestRegistry")
+
+test_that("RequestRegistry: structure", {
+ expect_is(RequestRegistry, "R6ClassGenerator")
+
+ aa <- RequestRegistry$new()
+
+ expect_is(aa, "RequestRegistry")
+ expect_is(aa$clone, "function")
+ expect_is(aa$print, "function")
+ expect_is(aa$register_request, "function")
+ expect_null(aa$request)
+ expect_is(aa$request_signatures, "HashCounter")
+ expect_is(aa$reset, "function")
+})
+
+test_that("RequestRegistry: behaves as expected", {
+ aa <- RequestRegistry$new()
+ aa$reset()
+
+ expect_length(aa$request_signatures$hash, 0)
+
+ aa$register_request(request = "GET https://scottchamberlain.info")
+ aa$register_request(request = "GET https://scottchamberlain.info")
+
+ expect_length(aa$request_signatures$hash, 1)
+ expect_equal(
+ aa$request_signatures$hash$`GET https://scottchamberlain.info`,
+ 2
+ )
+
+ expect_output(
+ print(aa), "Registered Requests"
+ )
+ expect_output(
+ print(aa), "GET https://scottchamberlain.info was made"
+ )
+
+ # reset the request registry
+ aa$reset()
+ expect_length(aa$request_signatures$hash, 0)
+})
+
+test_that("RequestRegistry fails well", {
+ x <- RequestRegistry$new()
+
+ expect_error(x$register_request(), "'key' required")
+})
diff --git a/tests/testthat/test-RequestSignature.R b/tests/testthat/test-RequestSignature.R
new file mode 100644
index 0000000..810b090
--- /dev/null
+++ b/tests/testthat/test-RequestSignature.R
@@ -0,0 +1,42 @@
+context("RequestSignature")
+
+test_that("RequestSignature: works", {
+ expect_is(RequestSignature, "R6ClassGenerator")
+
+ aa <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get")
+
+ expect_is(aa, "RequestSignature")
+
+ expect_null(aa$auth)
+ expect_null(aa$body)
+ expect_null(aa$headers)
+ expect_null(aa$proxies)
+
+ expect_is(aa$method, "character")
+ expect_equal(aa$method, "get")
+
+ expect_is(aa$uri, "character")
+ expect_equal(aa$uri, "https:/httpbin.org/get")
+
+ expect_is(aa$to_s, "function")
+ expect_equal(aa$to_s(), "GET https:/httpbin.org/get")
+})
+
+test_that("RequestSignature: different methods work", {
+ aa <- RequestSignature$new(method = "get", uri = "https:/httpbin.org/get")
+ aa$headers <- list(Accept = "application/json")
+ aa$body <- list(foo = "bar")
+
+ expect_is(aa$method, "character")
+ expect_is(aa$uri, "character")
+ expect_is(aa$headers, "list")
+ expect_is(aa$body, "list")
+})
+
+test_that("RequestSignature fails well", {
+ expect_error(RequestSignature$new(), "argument \"method\" is missing")
+ expect_error(RequestSignature$new(method = "adf"),
+ "'arg' should be one of")
+ expect_error(RequestSignature$new(method = "get"),
+ "argument \"uri\" is missing")
+})
diff --git a/tests/testthat/test-Response.R b/tests/testthat/test-Response.R
new file mode 100644
index 0000000..a3de30b
--- /dev/null
+++ b/tests/testthat/test-Response.R
@@ -0,0 +1,97 @@
+context("Response")
+
+aa <- Response$new()
+
+test_that("Response: bits are correct prior to having data", {
+ expect_is(Response, "R6ClassGenerator")
+
+ expect_is(aa, "Response")
+ expect_null(aa$body, "function")
+ expect_null(aa$content, "function")
+ expect_null(aa$exception, "function")
+ expect_is(aa$get_body, "function")
+ expect_is(aa$get_exception, "function")
+ expect_is(aa$get_request_headers, "function")
+ expect_is(aa$get_respone_headers, "function")
+ expect_is(aa$get_status, "function")
+ expect_is(aa$get_url, "function")
+ expect_is(aa$print, "function")
+ expect_is(aa$set_body, "function")
+ expect_is(aa$set_exception, "function")
+ expect_is(aa$set_request_headers, "function")
+ expect_is(aa$set_response_headers, "function")
+ expect_is(aa$set_status, "function")
+ expect_is(aa$set_url, "function")
+ expect_null(aa$should_timeout, "function")
+
+ expect_null(aa$request_headers)
+ expect_null(aa$response_headers)
+ expect_equal(aa$status_code, 200)
+ expect_null(aa$url)
+ expect_null(aa$name)
+})
+
+
+test_that("Response: bits are correct after having data", {
+ aa <- Response$new()
+ aa$set_url("https://httpbin.org/get")
+ aa$set_request_headers(list('Content-Type' = "application/json"))
+ aa$set_response_headers(list('Host' = "httpbin.org"))
+ aa$set_status(404)
+ aa$set_body("hello world")
+ aa$set_exception("exception")
+
+ expect_is(aa, "Response")
+
+ expect_null(aa$should_timeout)
+
+ expect_is(aa$request_headers, "list")
+ expect_named(aa$request_headers, "Content-Type")
+ expect_is(aa$response_headers, "list")
+ expect_named(aa$response_headers, "Host")
+
+ expect_equal(aa$status_code, 404)
+ expect_equal(aa$url, "https://httpbin.org/get")
+ expect_null(aa$name)
+
+ expect_equal(aa$body, "hello world")
+ expect_null(aa$content)
+ expect_equal(aa$exception, "exception")
+ 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_status(), 404)
+ expect_equal(aa$get_url(), "https://httpbin.org/get")
+
+ expect_output(aa$print(), "<webmockr response>")
+ expect_output(aa$print(), "headers")
+ expect_output(aa$print(), "request headers")
+
+ aa$set_body(body = "stuff")
+ expect_equal(aa$body, "stuff")
+
+ aa$set_exception(exception = "stop, wait, listen")
+ expect_equal(aa$exception, "stop, wait, listen")
+
+ aa$set_request_headers(headers = list(a = "howdy"))
+ expect_equal(aa$request_headers[[1]], "howdy")
+
+ aa$set_response_headers(headers = list(b = 6))
+ expect_equal(aa$get_respone_headers()[[1]], "6")
+
+ aa$set_status(status = 410)
+ expect_equal(aa$status_code, 410)
+
+ aa$set_url(url = "foobar.com")
+ expect_equal(aa$url, "foobar.com")
+})
+
+test_that("Response fails well", {
+ expect_error(aa$set_body(), "argument \"body\" is missing")
+ expect_error(aa$set_exception(), "argument \"exception\" is missing")
+ expect_error(aa$set_request_headers(), "argument \"headers\" is missing")
+ expect_error(aa$set_response_headers(), "argument \"headers\" is missing")
+ expect_error(aa$set_status(), "argument \"status\" is missing")
+ expect_error(aa$set_url(), "argument \"url\" is missing")
+})
diff --git a/tests/testthat/test-StubRegistry.R b/tests/testthat/test-StubRegistry.R
new file mode 100644
index 0000000..20883dd
--- /dev/null
+++ b/tests/testthat/test-StubRegistry.R
@@ -0,0 +1,105 @@
+context("StubRegistry")
+
+aa <- StubRegistry$new()
+
+test_that("StubRegistry: bits are correct prior to having data", {
+ expect_is(StubRegistry, "R6ClassGenerator")
+
+ expect_is(aa, "StubRegistry")
+
+ expect_is(aa$global_stubs, "list")
+ expect_equal(length(aa$global_stubs), 0)
+
+ expect_is(aa$request_stubs, "list")
+ expect_equal(length(aa$request_stubs), 0)
+
+ expect_null(aa$stub)
+
+ expect_is(aa$find_stubbed_request, "function")
+ expect_is(aa$is_registered, "function")
+ expect_is(aa$print, "function")
+ expect_is(aa$register_stub, "function")
+ expect_is(aa$remove_all_request_stubs, "function")
+ expect_is(aa$remove_request_stub, "function")
+ expect_is(aa$request_stub_for, "function")
+ expect_is(aa$response_for_request, "function")
+})
+
+
+test_that("StubRegistry: bits are correct after having data", {
+ stub1 <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+ 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")
+
+ aa <- StubRegistry$new()
+ expect_is(aa$register_stub(stub = stub1), "list")
+ expect_is(aa$register_stub(stub = stub2), "list")
+
+ expect_is(aa, "StubRegistry")
+
+ # global stubs are still empty
+ expect_is(aa$global_stubs, "list")
+ expect_equal(length(aa$global_stubs), 0)
+
+ # request stubs now length 2
+ expect_is(aa$request_stubs, "list")
+ expect_equal(length(aa$request_stubs), 2)
+
+ expect_null(aa$stub)
+
+ # find_stubbed_request
+ req1 <- RequestSignature$new(
+ method = "get",
+ uri = "http://api.crossref.org",
+ options = list(
+ headers = list('User-Agent' = 'R')
+ )
+ )
+
+ res <- aa$find_stubbed_request(req = req1)
+ expect_is(res, "list")
+ expect_is(res[[1]], "StubbedRequest")
+ expect_equal(res[[1]]$uri, "api.crossref.org")
+
+
+ # is_registered
+ expect_true(aa$is_registered(x = req1))
+
+ # request_stub_for
+ matches <- aa$request_stub_for(request_signature = req1)
+ expect_is(matches, "logical")
+ expect_equal(matches, c(TRUE, FALSE))
+
+ # response_for_request
+ ## FIXME!!!! - internal function not made yet
+ expect_error(aa$response_for_request(request_signature = req1),
+ "could not find function")
+
+ # remove_request_stub
+ res <- aa$remove_request_stub(stub = stub1)
+ expect_is(res, "list")
+ expect_equal(length(res), 1)
+
+ # remove_all_request_stubs
+ ## add another first
+ aa$register_stub(stub = stub1)
+ res <- aa$remove_all_request_stubs()
+ expect_is(res, "list")
+ expect_equal(length(res), 0)
+})
+
+test_that("StubRegistry fails well", {
+ # fill ins ome data first
+ stub1 <- StubbedRequest$new(method = "get", uri = "api.crossref.org")
+ aa <- StubRegistry$new()
+ aa$register_stub(stub = stub1)
+
+ expect_error(aa$find_stubbed_request(), "argument \"req\" is missing")
+ expect_error(aa$is_registered(), "argument \"x\" is missing")
+ expect_error(aa$register_stub(), "argument \"stub\" is missing")
+ expect_error(aa$remove_request_stub(), "argument \"stub\" is missing")
+ expect_error(aa$request_stub_for(), "argument \"request_signature\" is missing")
+ expect_error(aa$response_for_request(), "argument \"request_signature\" is missing")
+})
diff --git a/tests/testthat/test-StubbedRequest.R b/tests/testthat/test-StubbedRequest.R
new file mode 100644
index 0000000..2407bda
--- /dev/null
+++ b/tests/testthat/test-StubbedRequest.R
@@ -0,0 +1,89 @@
+context("StubbedRequest")
+
+test_that("StubbedRequest: works", {
+ expect_is(StubbedRequest, "R6ClassGenerator")
+
+ aa <- StubbedRequest$new(method = "get", uri = "https:/httpbin.org/get")
+
+ expect_is(aa, "StubbedRequest")
+
+ expect_null(aa$host)
+ expect_null(aa$query)
+ expect_null(aa$body)
+ expect_null(aa$request_headers)
+ expect_null(aa$response_headers)
+ expect_null(aa$response)
+ expect_null(aa$response_sequences)
+
+ expect_is(aa$method, "character")
+ expect_equal(aa$method, "get")
+
+ expect_is(aa$uri, "character")
+ expect_equal(aa$uri, "https:/httpbin.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_is(aa$to_s, "function")
+ expect_equal(aa$to_s(), "get: https:/httpbin.org/get")
+
+ # with
+ expect_is(aa$with, "function")
+ expect_null(aa$query)
+ aa$with(query = list(foo = "bar"))
+ expect_is(aa$query, "list")
+ expect_named(aa$query, "foo")
+
+ # to_return
+ expect_is(aa$to_return, "function")
+ expect_null(aa$body)
+ aa$to_return(
+ status = 404,
+ body = list(hello = "world"),
+ headers = list(a = 5)
+ )
+ expect_is(aa$responses_sequences, "list")
+ expect_is(aa$responses_sequences$body, "list")
+ expect_named(aa$responses_sequences$body, "hello")
+})
+
+test_that("StubbedRequest: different methods work", {
+ expect_equal(
+ StubbedRequest$new(method = "any", uri = "https:/httpbin.org/get")$method,
+ "any"
+ )
+ expect_equal(
+ StubbedRequest$new(method = "get", uri = "https:/httpbin.org/get")$method,
+ "get"
+ )
+ expect_equal(
+ StubbedRequest$new(method = "head", uri = "https:/httpbin.org/get")$method,
+ "head"
+ )
+ expect_equal(
+ StubbedRequest$new(method = "post", uri = "https:/httpbin.org/get")$method,
+ "post"
+ )
+ expect_equal(
+ StubbedRequest$new(method = "put", uri = "https:/httpbin.org/get")$method,
+ "put"
+ )
+ expect_equal(
+ StubbedRequest$new(method = "patch", uri = "https:/httpbin.org/get")$method,
+ "patch"
+ )
+ expect_equal(
+ StubbedRequest$new(method = "delete", uri = "https:/httpbin.org/get")$method,
+ "delete"
+ )
+})
+
+test_that("StubbedRequest fails well", {
+ # requires uri or uri_regex
+ expect_error(StubbedRequest$new(), "one of uri or uri_regex is required")
+
+ # method not in acceptable set
+ expect_error(StubbedRequest$new(method = "adf"),
+ "'arg' should be one of")
+})
diff --git a/tests/testthat/test-flipswitch.R b/tests/testthat/test-flipswitch.R
new file mode 100644
index 0000000..68afa6e
--- /dev/null
+++ b/tests/testthat/test-flipswitch.R
@@ -0,0 +1,29 @@
+context("flipswitch (enable/disable)")
+
+test_that("flipswitch in default state", {
+ expect_is(webmockr_lightswitch, "environment")
+ expect_is(webmockr_lightswitch$crul, "logical")
+ expect_false(webmockr_lightswitch$crul)
+})
+
+test_that("flipswitch - turn on with 'enable'", {
+ aa <- enable()
+
+ expect_is(aa, "logical")
+ expect_equal(length(aa), 1)
+
+ expect_true(webmockr_lightswitch$crul)
+})
+
+test_that("flipswitch - turn off with 'disable'", {
+ aa <- disable()
+
+ expect_null(aa)
+
+ expect_false(webmockr_lightswitch$crul)
+})
+
+test_that("enable and disable fail well", {
+ expect_error(enable(a = 5), "unused argument")
+ expect_error(disable(a = 5), "unused argument")
+})
diff --git a/tests/testthat/test-stub_request.R b/tests/testthat/test-stub_request.R
new file mode 100644
index 0000000..7315129
--- /dev/null
+++ b/tests/testthat/test-stub_request.R
@@ -0,0 +1,50 @@
+context("stub_request")
+
+stub_registry()$remove_all_request_stubs()
+
+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")
+
+test_that("stub_request bits are correct", {
+
+ expect_is(aa, "StubbedRequest")
+ expect_null(aa$body)
+ expect_null(aa$host)
+ expect_null(aa$query)
+ expect_null(aa$request_headers)
+ expect_null(aa$response)
+ expect_null(aa$response_headers)
+ expect_null(aa$responses_sequences)
+
+ expect_is(aa$method, "character")
+ expect_equal(aa$method, "get")
+ expect_is(aa$uri, "character")
+ expect_equal(aa$uri, "https://httpbin.org/get")
+
+ expect_is(aa$print, "function")
+ expect_output(aa$print(), "<webmockr stub>")
+
+ expect_is(aa$to_return, "function")
+ expect_error(aa$to_return(), "argument \"headers\" is missing")
+
+ expect_is(aa$to_s, "function")
+ expect_equal(aa$to_s(), "get: https://httpbin.org/get")
+
+ expect_is(aa$with, "function")
+ expect_null(aa$with())
+
+ expect_is(aa$uri_parts, "list")
+})
+
+test_that("stubs exist after stub_request called", {
+ expect_equal(length(stub_registry()$request_stubs), 1)
+})
+
+test_that("stub_request fails well", {
+ expect_error(stub_request(), "one of uri or uri_regex is required")
+ expect_error(stub_request(method = "stuff", "adf"),
+ "'arg' should be one of")
+})
diff --git a/tests/testthat/test-wi_th.R b/tests/testthat/test-wi_th.R
new file mode 100644
index 0000000..8b3b9cd
--- /dev/null
+++ b/tests/testthat/test-wi_th.R
@@ -0,0 +1,38 @@
+context("wi_th")
+
+test_that("wi_th: with just headers", {
+ aa <- stub_request("get", "https://httpbin.org/get") %>%
+ wi_th(headers = list('User-Agent' = 'R'))
+
+ expect_is(aa, "StubbedRequest")
+ expect_null(aa$body)
+ expect_null(aa$host)
+ expect_null(aa$query)
+ expect_is(aa$request_headers, "list")
+ expect_null(aa$response)
+ expect_null(aa$response_headers)
+ expect_null(aa$responses_sequences)
+
+ 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$request_headers, list('User-Agent' = 'R'))
+})
+
+test_that("wi_th: with headers and query", {
+ aa <- stub_request("get", "https://httpbin.org/get") %>%
+ wi_th(
+ query = list(hello = "world"),
+ headers = list('User-Agent' = 'R'))
+
+ expect_is(aa$query, "list")
+ expect_is(aa$request_headers, "list")
+
+ expect_output(print(aa), "hello=world")
+ expect_output(print(aa), "User-Agent=R")
+})
+
+test_that("wi_th fails well", {
+ expect_error(wi_th(), "argument \".data\" is missing")
+})
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-webmockr.git
More information about the debian-med-commit
mailing list