From 6095e2ae272c86e817cabd48331dfdcaa59a376a Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 09:00:21 -0600 Subject: [PATCH 01/12] update test-build_r_pkg.R --- tests/test-build_r_pkg.R | 68 +++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/tests/test-build_r_pkg.R b/tests/test-build_r_pkg.R index 7a181c2..6eead02 100644 --- a/tests/test-build_r_pkg.R +++ b/tests/test-build_r_pkg.R @@ -1,34 +1,58 @@ library(REDCapExporter) temppath <- tempdir() + build_r_data_package( x = avs_raw_core, path = temppath, author_roles = list(dewittp = c("cre", "aut")), ) -x <- fs::dir_tree(temppath) -x <- unname(sapply(strsplit(x, "rcd14465"), `[`, 2)) -x[is.na(x)] <- "" -x <- sort(paste0("rcd14465", x)) - -print(x) +pkgdir <- file.path(temppath, "rcd14465") +# check the DESCRIPTION file for the built package +d <- read.dcf(file.path(pkgdir, "DESCRIPTION")) stopifnot( - identical( - x, - sort( - c("rcd14465", "rcd14465/DESCRIPTION", "rcd14465/LICENSE", "rcd14465/NAMESPACE", - "rcd14465/R", "rcd14465/R/datasets.R", "rcd14465/data", "rcd14465/data/metadata.rda", - "rcd14465/data/project.rda", "rcd14465/data/record.rda", "rcd14465/data/user.rda", - "rcd14465/inst", "rcd14465/inst/raw-data", "rcd14465/inst/raw-data/metadata.rds", - "rcd14465/inst/raw-data/project.rds", "rcd14465/inst/raw-data/record.rds", - "rcd14465/inst/raw-data/user.rds", "rcd14465/man", "rcd14465/man/metadata.Rd", - "rcd14465/man/project.Rd", "rcd14465/man/record.Rd", "rcd14465/man/user.Rd") - ) - ) + d[1, "Package"] == "rcd14465", + grepl("\\d{4}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d{2}", d[1, "Version"]) ) -stopifnot( - packageDescription(pkg = "rcd14465", lib.loc = temppath)$Package == "rcd14465", - grepl("\\d{4}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d{2}", packageDescription(pkg = "rcd14465", lib.loc = temppath)$Version) - ) +# check the file structure of the built package +x <- list.files( + path = pkgdir, + all.files = TRUE, + recursive = TRUE, + include.dirs = TRUE, + full.names = FALSE, + no.. = TRUE +) + +# switch slashes from windows to *nix +x <- gsub("\\\\", "/", x) + +expected <- sort(c( + "DESCRIPTION", + "LICENSE", + "NAMESPACE", + "R", + "R/datasets.R", + "data", + "data/metadata.rda", + "data/project.rda", + "data/record.rda", + "data/user.rda", + "inst", + "inst/raw-data", + "inst/raw-data/metadata.rds", + "inst/raw-data/project.rds", + "inst/raw-data/record.rds", + "inst/raw-data/user.rds", + "man", + "man/metadata.Rd", + "man/project.Rd", + "man/record.Rd", + "man/user.Rd" +)) + + +stopifnot(identical(x, expected)) + From 81c856555cfd99bbef2d9d78f174dfee162ceecb Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 09:52:55 -0600 Subject: [PATCH 02/12] update tests --- DESCRIPTION | 2 +- NEWS.md | 6 ++ cran-comments.md | 21 ++++++ tests/test-as.data.frame.R | 67 +++++++++-------- tests/test-format_record.R | 44 +++++++++-- tests/test-keyring.R | 146 +++++++++++++++++++++++++------------ 6 files changed, 195 insertions(+), 91 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5759561..b5bdf1d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: REDCapExporter Title: Automated Construction of R Data Packages from REDCap Projects -Version: 0.3.3 +Version: 0.3.4 Authors@R: c( person(given = "Peter", family = "DeWitt", email = "peter.dewitt@cuanschutz.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-6391-0795")) ) diff --git a/NEWS.md b/NEWS.md index ffdcbfe..d288f10 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,12 @@ ## Testing Change +* Update test-build_r_pkg.R to be robust to platform. + +# Version 0.3.3 + +## Testing Change + * The API used for testing is no longer working. The test as been removed from this version of the package so the package can stay on CRAN while a better solution is found. (#18) diff --git a/cran-comments.md b/cran-comments.md index 0a20fc7..f8b0ebc 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,3 +1,24 @@ +# Version 0.3.4 + +This submission to cran is comming quickly after the prior submission as a new +CRAN check failure occured. + +- Improve testing suite to account for instability of the fs::dir_tree() call on + different platforms and R versions. + +- Improve testing in general to make it more robust to platform and R versions. + +## Testing + +- Local MacOS, R 4.5.2 + - Status OK +- GitHub Actions + - Status OK +- Windows Builder + - Status OK +- Rhub + - Status OK + # Version 0.3.3 - Improve testing suite to account for a resource going offline diff --git a/tests/test-as.data.frame.R b/tests/test-as.data.frame.R index 807c9cd..f2987c1 100644 --- a/tests/test-as.data.frame.R +++ b/tests/test-as.data.frame.R @@ -1,37 +1,36 @@ library(REDCapExporter) -# Expect error if the input in not csv or json. the error should come from the -# read_text call, which is not exported. -x <- avs_raw_metadata -attr(x, "Content-Type") <- c("not-csv", "not-json") -x <- tools::assertError(REDCapExporter:::read_text(x)) -stopifnot(identical(x[[1]][["message"]], "Content-Type 'not-csv' is not yet supported.")) - -# Testing coercion of rcer_raw_metadata to data.frame and data.table -DF0 <- as.data.frame(avs_raw_metadata) -DF1 <- as.data.frame(avs_raw_metadata_json) - -stopifnot(all.equal(DF0, DF1)) - -# Testing coercion of rcer_raw_record to data.frame and data.table -rm(list = ls()) - -DF0 <- as.data.frame(avs_raw_record) -DF1 <- as.data.frame(avs_raw_record_json) - -stopifnot(all.equal(DF0, DF1)) - -# Testing coercion of rcer_raw_project to data.frame and data.table -rm(list = ls()) -DF0 <- as.data.frame(avs_raw_project) -DF1 <- as.data.frame(avs_raw_project_json) - -stopifnot(all.equal(DF0, DF1)) - -# Testing coercion of rcer_raw_user to data.frame and data.table -rm(list = ls()) -DF0 <- as.data.frame(avs_raw_user) -DF1 <- as.data.frame(avs_raw_user_json) - -stopifnot(all.equal(DF0, DF1)) +# Unsupported content type should error +x_bad <- avs_raw_metadata +attr(x_bad, "Content-Type") <- c("not-csv", "not-json") + +err <- tryCatch( + REDCapExporter:::read_text(x_bad), + error = function(e) e +) + +stopifnot( + inherits(err, "error"), + grepl("Content-Type .* not yet supported", err$message) +) + +# Metadata coercion +md_csv <- as.data.frame(avs_raw_metadata) +md_json <- as.data.frame(avs_raw_metadata_json) +stopifnot(isTRUE(all.equal(md_csv, md_json))) + +# Record coercion +rec_csv <- as.data.frame(avs_raw_record) +rec_json <- as.data.frame(avs_raw_record_json) +stopifnot(isTRUE(all.equal(rec_csv, rec_json))) + +# Project coercion +proj_csv <- as.data.frame(avs_raw_project) +proj_json <- as.data.frame(avs_raw_project_json) +stopifnot(isTRUE(all.equal(proj_csv, proj_json))) + +# User coercion +usr_csv <- as.data.frame(avs_raw_user) +usr_json <- as.data.frame(avs_raw_user_json) +stopifnot(isTRUE(all.equal(usr_csv, usr_json))) diff --git a/tests/test-format_record.R b/tests/test-format_record.R index e51503e..f6ecdb2 100644 --- a/tests/test-format_record.R +++ b/tests/test-format_record.R @@ -120,14 +120,42 @@ classes[classes == "factor"] <- "character" stopifnot(identical(sapply(DF, class), classes)) stopifnot(!any(sapply(DF, class) == "factor")) +################################################################################ # verify that you can set the timezone for the dates -DF0 <- format_record(avs_raw_core) +# verify that you can set the timezone for the dates +df_date <- format_record(avs_raw_core) + +tz_local <- Sys.timezone() +if (is.na(tz_local) || !nzchar(tz_local)) { + tz_local <- "UTC" +} + +df_local <- format_record( + avs_raw_record, + col_type = col_type( + avs_raw_metadata, + lubridate_args = list(tz = tz_local) + ) +) + +df_utc <- format_record( + avs_raw_record, + col_type = col_type( + avs_raw_metadata, + lubridate_args = list(tz = "UTC") + ) +) + +stopifnot(inherits(df_date$birthdate, "Date")) -# this timezone specification is system and locale specific -#DF1 <- format_record(avs_raw_record, col_type = col_type(avs_raw_metadata, lubridate_args = list(tz = "US/Mountain"))) -DF1 <- format_record(avs_raw_record, col_type = col_type(avs_raw_metadata, lubridate_args = list(tz = Sys.timezone()))) -DF2 <- format_record(avs_raw_record, col_type = col_type(avs_raw_metadata, lubridate_args = list(tz = "UTC"))) +stopifnot( + !inherits(df_local$birthdate, "Date"), + inherits(df_local$birthdate, "POSIXct"), + identical(attr(df_local$birthdate, "tzone"), tz_local) +) -stopifnot(inherits(DF0$birthdate, "Date")) -stopifnot(!inherits(DF1$birthdate, "Date"), inherits(DF1$birthdate, "POSIXct"), isTRUE(attr(DF1$birthdate, "tzone") == Sys.timezone())) -stopifnot(!inherits(DF2$birthdate, "Date"), inherits(DF2$birthdate, "POSIXct"), isTRUE(attr(DF2$birthdate, "tzone") == "UTC")) +stopifnot( + !inherits(df_utc$birthdate, "Date"), + inherits(df_utc$birthdate, "POSIXct"), + identical(attr(df_utc$birthdate, "tzone"), "UTC") +) diff --git a/tests/test-keyring.R b/tests/test-keyring.R index aac5bb9..44bda22 100644 --- a/tests/test-keyring.R +++ b/tests/test-keyring.R @@ -1,60 +1,110 @@ library(REDCapExporter) -# Test if a new keyring can be built kr <- keyring::backend_file$new() -try(kr$keyring_delete("testingring"), silent = TRUE) -#kr$keyring_create(password = "") -#kr$keyring_list() - -x <- tryCatch(REDCapExporter_keyring_check("testingring"), message = function(m) {m}) -stopifnot(identical(x$message, "File based keyring testingring has been created\n")) -x <- tryCatch(REDCapExporter_keyring_check("testingring"), message = function(m) {m}) -stopifnot(identical(x$message, "File based keyring testingring exists\n")) -stopifnot(isTRUE(REDCapExporter_keyring_check("testingring"))) - -# Expect that this will error because we are not interactive and a password -# prompt cannot be filled in -x <- - tryCatch( - REDCapExporter_add_api_token(project = 'testingproject', keyring = 'testingring'), - error = function(e) e - ) - -stopifnot(inherits(x, "error")) -stopifnot(isTRUE(grepl("Aborted setting keyring key", x$message))) - -# expect the get api token to fail as the token for the testingproject has not -# been set -x <- - tryCatch( - REDCapExporter_get_api_token(project = 'testingproject', keyring = 'testingring'), - error = function(e) e - ) - -stopifnot(inherits(x, "error")) -stopifnot(isTRUE(grepl("specified item could not be found in the keychain", x$message))) - -# create token -kr$set_with_value(service = "testingproject", password = "testingTOKEN", keyring = "testingring") -# verify you can get the token +keyring_name <- paste0("testingring_", Sys.getpid()) +service_name <- paste0("testingproject_", Sys.getpid()) + +# cleanup on exit +try(kr$keyring_delete(keyring_name), silent = TRUE) + +# keyring can be created or verified +msg1 <- NULL +withCallingHandlers( + { + ok1 <- REDCapExporter_keyring_check(keyring_name) + }, + message = function(m) { + msg1 <<- conditionMessage(m) + invokeRestart("muffleMessage") + } +) + stopifnot( - identical( - REDCapExporter_get_api_token(project = 'testingproject', keyring = 'testingring') - , - "testingTOKEN" - )) + isTRUE(ok1), + grepl("File based keyring", msg1), + grepl(keyring_name, msg1), + grepl("created|exists", msg1) +) +msg2 <- NULL +withCallingHandlers( + { + ok2 <- REDCapExporter_keyring_check(keyring_name) + }, + message = function(m) { + msg2 <<- conditionMessage(m) + invokeRestart("muffleMessage") + } +) -# the REDCapExporter_add_api_token should return a message that the token -# already exists stopifnot( - REDCapExporter_add_api_token(project = 'testingproject', keyring = 'testingring') + isTRUE(ok2), + grepl("File based keyring", msg2), + grepl(keyring_name, msg2), + grepl("exists", msg2) +) + +# Adding a token interactively should fail in non-interactive checks +err_add <- tryCatch( + REDCapExporter_add_api_token( + project = service_name, + keyring = keyring_name + ), + error = function(e) e +) + +stopifnot(inherits(err_add, "error")) + +# Missing token should error +err_get_missing <- tryCatch( + REDCapExporter_get_api_token( + project = service_name, + keyring = keyring_name + ), + error = function(e) e +) + +stopifnot(inherits(err_get_missing, "error")) + +# Set token directly through keyring backend +kr$keyring_unlock(keyring = keyring_name, password = "") +kr$set_with_value( + service = service_name, + password = "testingTOKEN", + keyring = keyring_name ) +kr$keyring_lock(keyring = keyring_name) -x <- tryCatch( - REDCapExporter_add_api_token(project = 'testingproject', keyring = 'testingring'), - message = function(m) {m} +# verify you can get the token +stopifnot(identical( + REDCapExporter_get_api_token( + project = service_name, + keyring = keyring_name + ), + "testingTOKEN" +)) + +# verify add_api_token short-circuits when token exists +msg3 <- NULL +withCallingHandlers( + { + ok3 <- REDCapExporter_add_api_token( + project = service_name, + keyring = keyring_name + ) + }, + message = function(m) { + msg3 <<- conditionMessage(m) + invokeRestart("muffleMessage") + } +) + +stopifnot( + isTRUE(ok3), + grepl("API token exists", msg3) ) -stopifnot(isTRUE(grepl("API token exists", x$message))) +# cleanup +try(kr$key_delete(service = service_name, keyring = keyring_name), silent = TRUE) +try(kr$keyring_delete(keyring_name), silent = TRUE) From a6fd4cbe1e07c079f58c3eaad3b92566cb9ca55a Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 10:44:59 -0600 Subject: [PATCH 03/12] update from http to https --- DESCRIPTION | 2 +- man/REDCapExporter-package.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b5bdf1d..b09f3bb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,7 +12,7 @@ Description: Export all data, including metadata, from a REDCap (Research default reports are generated as vignettes in the resulting package. License: GPL-2 Encoding: UTF-8 -URL: https://github.com/dewittpe/REDCapExporter, http://www.peteredewitt.com/REDCapExporter/ +URL: https://github.com/dewittpe/REDCapExporter, https://www.peteredewitt.com/REDCapExporter/ Language: en-us LazyData: true Depends: diff --git a/man/REDCapExporter-package.Rd b/man/REDCapExporter-package.Rd index 4ba9f77..948ffc2 100644 --- a/man/REDCapExporter-package.Rd +++ b/man/REDCapExporter-package.Rd @@ -21,7 +21,7 @@ Please read the vignette for examples and use cases. Useful links: \itemize{ \item \url{https://github.com/dewittpe/REDCapExporter} - \item \url{http://www.peteredewitt.com/REDCapExporter/} + \item \url{https://www.peteredewitt.com/REDCapExporter/} } } From 36930559d25ffa096639eea3979b78e0afff0740 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 10:53:22 -0600 Subject: [PATCH 04/12] gate devtools --- NEWS.md | 12 ++++++++++++ R/build_r_data_package.R | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index d288f10..2c3180f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,15 @@ +# Version 0.3.4 + +## Improvements + +* Gate `devtools::document()` in `build_r_data_package()` when devtools is not available. + +## Testing Change + +* Update `test-build_r_pkg.R` expectations based on devtools availability. +* Constrain keyring tests to a temporary keyring directory. +* Avoid building/installing a generated package inside the vignette during checks. + # Version 0.3.3 ## Testing Change diff --git a/R/build_r_data_package.R b/R/build_r_data_package.R index 3176c30..61cfac9 100644 --- a/R/build_r_data_package.R +++ b/R/build_r_data_package.R @@ -130,7 +130,11 @@ build_r_data_package.rcer_rccore <- function(x, path = NULL, author_roles = NULL file = paste(path, "R/datasets.R", sep = "/") ) - devtools::document(path) + if (requireNamespace("devtools", quietly = TRUE)) { + devtools::document(path) + } else { + message("Skipping devtools::document(): 'devtools' is not available.") + } invisible(TRUE) } From 18f992159e1e8243ede615adfdbe166c6acd5c55 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 10:54:01 -0600 Subject: [PATCH 05/12] update test-keyring.R --- tests/test-keyring.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test-keyring.R b/tests/test-keyring.R index 44bda22..21915e2 100644 --- a/tests/test-keyring.R +++ b/tests/test-keyring.R @@ -1,5 +1,11 @@ library(REDCapExporter) +keyring_dir <- file.path(tempdir(), paste0("redcapexporter-keyring-", Sys.getpid())) +dir.create(keyring_dir, showWarnings = FALSE, recursive = TRUE) +op <- options(keyring_file_dir = keyring_dir) +on.exit(options(op), add = TRUE) +on.exit(unlink(keyring_dir, recursive = TRUE, force = TRUE), add = TRUE) + kr <- keyring::backend_file$new() keyring_name <- paste0("testingring_", Sys.getpid()) From 922478a17c3231e50a94552db76001094b025a6d Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 10:56:07 -0600 Subject: [PATCH 06/12] add eval = FLASE to chunks in vignettes/redcap2package.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change was needed beacuse the vignette was trying to build and install the generated package during vignette rendering: tar_ball <- devtools::build(pkg = prj_dir) install.packages(pkgs = tar_ball, lib = temppath) do.call(library, ...) On CRAN this triggered a failure (processx .onLoad “Operation not permitted”) during devtools::build. Marking those chunks eval = FALSE avoids running those build/install steps while still showing the code in the vignette. This makes the vignette stable in non-interactive CRAN checks. --- vignettes/redcap2package.Rmd | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vignettes/redcap2package.Rmd b/vignettes/redcap2package.Rmd index 36627a5..74d2401 100644 --- a/vignettes/redcap2package.Rmd +++ b/vignettes/redcap2package.Rmd @@ -135,7 +135,7 @@ be accessed in an interactive R session. Let's install the package and explore the contents. -```{r} +```{r eval = FALSE} tar_ball <- devtools::build(pkg = prj_dir) tar_ball @@ -146,21 +146,20 @@ install.packages(pkgs = tar_ball, lib = temppath) library(rcd14465, lib.loc = temppath) ``` -```{r include = FALSE} +```{r include = FALSE, eval = FALSE} do.call(library, args = list(package = "rcd14465", character.only = TRUE, lib.loc = temppath)) ``` The available data sets: -```{r} +```{r eval = FALSE} data(package = "rcd14465")$results ``` A simple data analysis question: how many goals were scored by position? -```{r} +```{r eval = FALSE} aggregate(goals ~ position, data = record, FUN = sum) ``` - From ea20000a95b2e057484d21034711af5875ba6d6f Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 10:58:05 -0600 Subject: [PATCH 07/12] update tests/test-build_r_pkg.R to pass when devtools is not available --- tests/test-build_r_pkg.R | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/tests/test-build_r_pkg.R b/tests/test-build_r_pkg.R index 6eead02..c43d3dc 100644 --- a/tests/test-build_r_pkg.R +++ b/tests/test-build_r_pkg.R @@ -1,10 +1,19 @@ library(REDCapExporter) temppath <- tempdir() -build_r_data_package( - x = avs_raw_core, - path = temppath, - author_roles = list(dewittp = c("cre", "aut")), +msgs <- character() +withCallingHandlers( + { + build_r_data_package( + x = avs_raw_core, + path = temppath, + author_roles = list(dewittp = c("cre", "aut")) + ) + }, + message = function(m) { + msgs <<- c(msgs, conditionMessage(m)) + invokeRestart("muffleMessage") + } ) pkgdir <- file.path(temppath, "rcd14465") @@ -32,7 +41,6 @@ x <- gsub("\\\\", "/", x) expected <- sort(c( "DESCRIPTION", "LICENSE", - "NAMESPACE", "R", "R/datasets.R", "data", @@ -45,14 +53,22 @@ expected <- sort(c( "inst/raw-data/metadata.rds", "inst/raw-data/project.rds", "inst/raw-data/record.rds", - "inst/raw-data/user.rds", - "man", - "man/metadata.Rd", - "man/project.Rd", - "man/record.Rd", - "man/user.Rd" + "inst/raw-data/user.rds" )) +if (requireNamespace("devtools", quietly = TRUE)) { + expected <- sort(c( + expected, + "NAMESPACE", + "man", + "man/metadata.Rd", + "man/project.Rd", + "man/record.Rd", + "man/user.Rd" + )) + stopifnot(!any(grepl("Skipping devtools::document", msgs))) +} else { + stopifnot(any(grepl("Skipping devtools::document", msgs))) +} stopifnot(identical(x, expected)) - From c326e62fce3650a19a87b423a1d498f0591a5dbc Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 10:58:15 -0600 Subject: [PATCH 08/12] update cran-comments.md --- cran-comments.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/cran-comments.md b/cran-comments.md index f8b0ebc..97a1131 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,23 +1,26 @@ # Version 0.3.4 -This submission to cran is comming quickly after the prior submission as a new -CRAN check failure occured. +This submission to CRAN is coming shortly after the prior submission because of +new CRAN check failures. -- Improve testing suite to account for instability of the fs::dir_tree() call on - different platforms and R versions. - -- Improve testing in general to make it more robust to platform and R versions. +- Prevent vignette build/install of the generated package (devtools/processx) + during checks. +- Constrain keyring tests to a temporary keyring directory. +- Gate `devtools::document()` inside `build_r_data_package()` when devtools is + not available, and update tests accordingly. +- Update the personal URL to HTTPS. ## Testing -- Local MacOS, R 4.5.2 - - Status OK +- Local macOS, R 4.5.2 + - R CMD check --as-cran REDCapExporter_0.3.4.tar.gz + - Status: OK (2 NOTEs: no Internet for URL checks, time-stamp check) - GitHub Actions - - Status OK + - Status: OK - Windows Builder - - Status OK + - Status: OK - Rhub - - Status OK + - Status: OK # Version 0.3.3 @@ -268,5 +271,3 @@ be used in the examples and vignettes. > Please fix and resubmit, and document what was changed in the submission > comments. - - From 7a1f3c1a94cd60322b087b381803179f3ff79102 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 11:15:03 -0600 Subject: [PATCH 09/12] update NEWS.md --- NEWS.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2c3180f..8d7d9da 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,7 @@ ## Testing Change +* Update test-build_r_pkg.R to be robust to platform. * Update `test-build_r_pkg.R` expectations based on devtools availability. * Constrain keyring tests to a temporary keyring directory. * Avoid building/installing a generated package inside the vignette during checks. @@ -14,12 +15,6 @@ ## Testing Change -* Update test-build_r_pkg.R to be robust to platform. - -# Version 0.3.3 - -## Testing Change - * The API used for testing is no longer working. The test as been removed from this version of the package so the package can stay on CRAN while a better solution is found. (#18) From b4ee179bc1a77177fe0a678e9b0d551a4d1dcc3f Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 11:15:26 -0600 Subject: [PATCH 10/12] change case from en-us to en-US --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index b09f3bb..d3ae887 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,7 +13,7 @@ Description: Export all data, including metadata, from a REDCap (Research License: GPL-2 Encoding: UTF-8 URL: https://github.com/dewittpe/REDCapExporter, https://www.peteredewitt.com/REDCapExporter/ -Language: en-us +Language: en-US LazyData: true Depends: R (>= 3.5.0) From fa701c01b46c57c165cf9ed1eb5daa89026f1539 Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 11:40:01 -0600 Subject: [PATCH 11/12] fix path for TESTS in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0aeb7f..4604d91 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CRAN = "https://cran.rstudio.com" SRC = $(wildcard $(PKG_ROOT)/src/*.cpp) RFILES = $(wildcard $(PKG_ROOT)/R/*.R) EXAMPLES = $(wildcard $(PKG_ROOT)/examples/*.R) -TESTS = $(wildcard $(PKG_ROOT)/tests/testthat/*.R) +TESTS = $(wildcard $(PKG_ROOT)/tests/test-*.R) # Targets VIGNETTES = $(PKG_ROOT)/vignettes/redcap2package.Rmd\ From 0de880c74cc28dcd1d942e8835e1a8086be280db Mon Sep 17 00:00:00 2001 From: Peter DeWitt Date: Mon, 9 Mar 2026 11:40:32 -0600 Subject: [PATCH 12/12] update tests for updated redcap test url and token --- NEWS.md | 2 + cran-comments.md | 2 + tests/test-export.R | 106 ++++++++++---------------------------------- 3 files changed, 27 insertions(+), 83 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8d7d9da..c12a0a8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,8 @@ * Update `test-build_r_pkg.R` expectations based on devtools availability. * Constrain keyring tests to a temporary keyring directory. * Avoid building/installing a generated package inside the vignette during checks. +* Update test-export to use the REDCapR public dev-2 test endpoint/token and + skip on curl-related failures. # Version 0.3.3 diff --git a/cran-comments.md b/cran-comments.md index 97a1131..483f3b6 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -8,6 +8,8 @@ new CRAN check failures. - Constrain keyring tests to a temporary keyring directory. - Gate `devtools::document()` inside `build_r_data_package()` when devtools is not available, and update tests accordingly. +- Update test-export to use the REDCapR public dev-2 test endpoint/token and + skip on curl-related failures. - Update the personal URL to HTTPS. ## Testing diff --git a/tests/test-export.R b/tests/test-export.R index 809c8bf..6e7baa4 100644 --- a/tests/test-export.R +++ b/tests/test-export.R @@ -1,13 +1,13 @@ library(REDCapExporter) -# Tests rely on publicly available REDCap and tokens published -# https://github.com/redcap-tools/redcap-test-datasets/tree/master +# Tests rely on publicly available REDCap test datasets and tokens. +# The REDCapR project moved its public test credentials from bbmc.ouhsc.edu +# to redcap-dev-2.ouhsc.edu in February 2026 (see REDCapR repo history and +# inst/misc/example.credentials). We follow that public test endpoint here. +# The API tokens below are intended to be public and used for testing. -# project_name token server_url pid server read_only operational notes -# archer 9A81268476645C4E5F03428B8AC3AA7B https://bbmc.ouhsc.edu/redcap/api/ 153 oklahoma-bbmc TRUE TRUE simple structure; read-only -# archer D70F9ACD1EDD6F151C6EA78683944E98 https://bbmc.ouhsc.edu/redcap/api/ 213 oklahoma-bbmc FALSE TRUE simple structure; read & write -# archer 0434F0E9CF53ED0587847AB6E51DE762 https://bbmc.ouhsc.edu/redcap/api/ 212 oklahoma-bbmc TRUE TRUE longitudinal structure; read-only -# archer D72C6485B52FE9F75D27B696977FBA43 https://bbmc.ouhsc.edu/redcap/api/ 268 oklahoma-bbmc TRUE TRUE Russian characters; read-only +# project_name token server_url pid server read_only operational notes +# simple 9A068C425B1341D69E83064A2D273A70 https://redcap-dev-2.ouhsc.edu/redcap/api/ 33 ouhsc-dev-2 TRUE TRUE simple structure; read-only on_cran <- function() { # copied from testthat:::on_cran @@ -22,84 +22,24 @@ on_cran <- function() { if (!on_cran()) { archer01_csv <- tryCatch( - export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = '9A81268476645C4E5F03428B8AC3AA7B'), + export_core(uri = 'https://redcap-dev-2.ouhsc.edu/redcap/api/', token = '9A068C425B1341D69E83064A2D273A70'), error = function(e) {e} ) - expected_return <- - c( - project_raw = "ERROR: You do not have permissions to use the API", - metadata_raw = "ERROR: You do not have permissions to use the API", - user_raw = "ERROR: You do not have permissions to use the API", - record_raw = "ERROR: You do not have permissions to use the API" - ) - - stopifnot( - !inherits(archer01_csv, "error"), # not an error - a retun happened, but it is not a useful return - length(archer01_csv) == 4L, - inherits(archer01_csv[[1]], "rcer_raw_project"), - inherits(archer01_csv[[2]], "rcer_raw_metadata"), - inherits(archer01_csv[[3]], "rcer_raw_user"), - inherits(archer01_csv[[4]], "rcer_raw_record"), - identical(sapply(archer01_csv, getElement, 1), expected_return) - ) + if (inherits(archer01_csv, "error")) { + if (inherits(archer01_csv, "curl_error")) { + message(sprintf("Skip test-export.R after curl error: %s", archer01_csv$message)) + } else { + stop("in test-export.R a non-curl error occurred when calling export_core") + } + } else { + stopifnot( + length(archer01_csv) == 4L, + inherits(archer01_csv[[1]], "rcer_raw_project"), + inherits(archer01_csv[[2]], "rcer_raw_metadata"), + inherits(archer01_csv[[3]], "rcer_raw_user"), + inherits(archer01_csv[[4]], "rcer_raw_record") + ) + } } -################################################################################ -# OLD TESTS, for version < 0.3.3 these tests were used, but in Feb 2026 the API -# failed and so do the tests. Not a REDCapExporter issue, an RedCap API issue. -# -# Commented out for now until a fix can be made. -# -### -### -### Sys.setenv("REDCap_API_URI" = 'https://bbmc.ouhsc.edu/redcap/api/') -### Sys.setenv("REDCap_API_TOKEN" = '9A81268476645C4E5F03428B8AC3AA7B') -### archer01_json <- tryCatch(export_core(format = "json"), error = function(e) {e}) -### -### if (inherits(archer01_csv, "error") | inherits(archer01_json, "error")) { -### if (inherits(archer01_csv, "curl_error") | inherits(archer01_json, "curl_error")) { -### # skip the rest of the testing -### print(sprintf("Skip test-export.R after curl error: %s", archer01_csv$message)) -### } else { -### stop("in test-export.R an non curl related error occured when initially calling export_core") -### } -### } else { -### -### stopifnot( -### inherits(archer01_csv, "rcer_rccore"), -### inherits(archer01_json, "rcer_rccore"), -### inherits(archer01_csv$project_raw, "rcer_raw_project"), -### inherits(archer01_json$project_raw, "rcer_raw_project"), -### inherits(archer01_csv$metadata_raw, "rcer_raw_metadata"), -### inherits(archer01_json$metadata_raw, "rcer_raw_metadata"), -### inherits(archer01_csv$project_raw, "rcer_raw_project"), -### inherits(archer01_json$project_raw, "rcer_raw_project"), -### inherits(archer01_csv$record_raw, "rcer_raw_record"), -### inherits(archer01_json$record_raw, "rcer_raw_record"), -### grepl("text/csv", attr(archer01_csv$record_raw, "Content-Type")), -### grepl("application/json", attr(archer01_json$record_raw, "Content-Type")) -### ) -### -### a1 <- format_record(archer01_csv) -### a2 <- format_record(archer01_json) -### -### # apparently the end of line characters are exported differently -### a1$address <- gsub('\n', ' ', gsub('\r', '', a1$address)) -### a2$address <- gsub('\n', ' ', gsub('\r', '', a2$address)) -### a1$comments <- gsub('\n', ' ', gsub('\r', '', a1$comments)) -### a2$comments <- gsub('\n', ' ', gsub('\r', '', a2$comments)) -### -### stopifnot(isTRUE(all.equal(a1, a2))) -### -### } -### -### # -### #archer02 <- export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = 'D70F9ACD1EDD6F151C6EA78683944E98') -### #archer02 <- format_record(archer02) -### # -### #archer03 <- export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = '0434F0E9CF53ED0587847AB6E51DE762') -### #archer03 <- format_record(archer03) -### # -### #archer04 <- export_core(uri = 'https://bbmc.ouhsc.edu/redcap/api/', token = 'D72C6485B52FE9F75D27B696977FBA43') -### #archer04 <- format_record(archer04)