wrathematics / getPass

Password function for R with masking (where supported)
Other
47 stars 5 forks source link

getPass not "firing" in the context of testthat #11

Closed maptracker closed 6 years ago

maptracker commented 6 years ago
# Interface
[1] "X11"

# OS
sysname 
"Linux" 
# Not RStudio

We are writing a package to manage single-sign-on authentication inside our organization. I'm adding tests to the package using the testthat package. We've been able to happily use getPass::getPass() in "normal" contexts, but when it's called in the context of automated build testing (via devtools::check_built) no challenge is presented, and the returned value is empty (either NULL or an empty string, I have not dug down to see which yet). The basic pseudocode is:

## In our package
doSomething <- function() {
  pw <- getPassword()
  cat("Got password: ", pw, file="/tmp/foo") # Debugging code
  x <- authenticatedCallToSomething(pw)
  if (x == 42L) { TRUE } else { FALSE }
}
getPassword <- function () {
    getPass::getPass("Enter Password: ")
}

## In file tests/testthat/test-someTest.R
test_that("Blah blah", {
    expect_true( doSomething() )
})

During the check of the built package, I'm not challenged for a password, and the debugging output is written with an empty value (as expected, since there wasn't a chance to provide input to getPass).

Thoughts on this? Is there something about the testing environment that could be shunting getPass into a dead-end? Flags or options we can set?

Thanks!

wrathematics commented 6 years ago

The only thing I can think of that may be happening is that tcltk is absent (probably a good thing), and getPass:::isaterm() returns FALSE so that the reader defaults to R's readline(). In addition to this not being masked, it can only be used interactively. If you run

Rscript -e "readline()"

from a terminal, you should just get an empty string as the return, without a chance to input anything. readline() is our backup for cases where we can't be sure that getPass:::readline_masked_term() will be able to run at all.

If my guess as to what's going on is correct, and if you're really sure you want to ask for password input interactively in a batch test, it would be useful to know a bit more about how it's actually being invoked. I don't use testthat, but my guess is that it basically just sources the tests. Are you invoking testthat from R CMD check or via some devtools function?

maptracker commented 6 years ago
## Shell output of readline() via Rscript:
Rscript -e "readline()"

[1] ""

I agree that it's unusual to solicit input during a test. Because this package is handling authentication, I was setting up a set of tests that request and then utilize actual SiteMinder-protected resources, so I need to get a "real" token, which needs an actual password.

I threw in debugging code for getPass:::isaterm() and it does in fact return FALSE as you suspected. I broke out the components of isaterm(), and during testing .Platform$GUI is "X11" but isatty(stdin()) is FALSE.

devtools::check_built() appears to just build a shell string that gets passed to CMD check, which then get run against the tarball.

I had been planning on allowing credentials to be passed in via environment variables, I think I'll code that up now and have credential-reliant testing be run conditionally when the packager has provided their credentials "in advance". I'll close this out, but if you can think of any mechanisms to "trick" a prompt into appearing during testing go ahead and suggest it.

Thanks!