
test_that("we can bootstrap the current version of renv", {

  skip_on_cran()
  skip_on_ci()

  renv_tests_scope()

  library <- renv_libpaths_active()
  bootstrap(version = "1.0.0", library = library)
  expect_true(renv_package_installed("renv", library))
  expect_true(renv_package_version("renv") == "1.0.0")

})

test_that("we can bootstrap an archived version of renv", {

  skip_on_cran()
  skip_on_ci()

  renv_tests_scope()

  library <- renv_libpaths_active()
  bootstrap(version = "0.1.0", library = library)
  expect_true(renv_package_installed("renv", library))
  expect_true(renv_package_version("renv") == "0.1.0")

})

test_that("we can install a version of renv with a sha", {

  skip_on_cran()
  skip_on_ci()
  skip_if_no_github_auth()

  renv_tests_scope()

  version <- structure("0.17.3-62", sha = "5049cef8a")
  library <- renv_libpaths_active()
  bootstrap(version = version, library = library)
  expect_true(renv_package_installed("renv", library))

  desc <- utils::packageDescription("renv", library)
  expect_equal(desc$RemoteType, "github")
  expect_equal(desc$RemotePkgRef, "rstudio/renv")
  expect_equal(desc$RemoteSha, "5049cef8a94591b802f9766a0da092780f59f7e4")
})

test_that("bootstrap functions don't depend on non-bootstrap APIs", {

  # pattern matching things that are bootstrapped for renv
  pattern <- "^renv_(?:bootstrap|json|options|remote)_"

  # get all of the bootstrap functions defined in renv
  renv <- asNamespace("renv")
  keys <- grep(pattern, ls(envir = renv), value = TRUE)
  fns <- mget(keys, envir = renv, mode = "function")
  bodies <- map(fns, body)

  # iterate over those functions and look for the called functions
  calls <- stack(mode = "character")
  recurse(bodies, function(node) {
    if (is.call(node) && is.symbol(node[[1L]]))
      calls$push(as.character(node[[1L]]))
  })
  calls <- calls$data()

  # check what renv APIs are used
  apis <- grep("^renv_", calls, value = TRUE)

  # validate they're all available
  bad <- grep(pattern, apis, value = TRUE, invert = TRUE)
  expect_true(length(bad) == 0, info = paste("Functions:", bad, collapse = ", "))

})

test_that("bootstrapping functions standalone", {

  renv_tests_scope()

  # get all bootstrap APIs in package
  renv <- asNamespace("renv")
  keys <- ls(envir = renv, pattern = "^renv_bootstrap_", all.names = TRUE)
  vals <- mget(c("catf", "%||%", "header", "bootstrap", keys), envir = renv)

  # put those into a separate environment inheriting only from base, and
  # re-mark those as inheriting from base (so they only 'see' each-other)
  envir <- new.env(parent = baseenv())
  for (i in seq_along(vals))
    environment(vals[[i]]) <- envir
  list2env(vals, envir = envir)

  # set up library path
  library <- renv_scope_tempfile("renv-library-")
  ensure_directory(library)

  # try running 'sandboxed' version of bootstrap
  run <- function(version) {

    # construct bootstrap call
    code <- call("bootstrap", version = version, library = library)

    # run that call
    eval(code, envir = envir)

    # validate that renv was successfully installed
    expect_true(renv_package_installed("renv", lib.loc = library))

    # validate that the correct version was installed
    descpath <- file.path(library, "renv/DESCRIPTION")
    desc <- renv_description_read(path = descpath)
    expect_identical(desc$Package, "renv")
    expect_identical(desc$Version, version)

  }

  # current on CRAN (test repository version)
  run("1.0.0")

  # in the CRAN archive (test repository version)
  run("0.1.0")

})

test_that("bootstrapping gives informative output when succesful", {

  scope <- environment()
  local_mocked_bindings(

    renv_bootstrap_download_impl = function(url, destfile) {
      file.create(destfile)
      defer(unlink(destfile), scope = scope)
      0L
    },

    renv_bootstrap_install_impl = function(cmd, args, ...) {
      structure("", status = 0L)
    },

    renv_bootstrap_download_cran_latest_find = function(version) {
      if (package_version(version) < "1.0.0") {
        list(type = "binary", repos = "https://cran.rstudio.com")
      }
    },

    load = function(...) invisible()

  )

  renv_scope_options(renv.bootstrap.quiet = FALSE)
  library <- renv_scope_tempfile()

  renv_tests_scope()
  expect_snapshot({
    bootstrap(version = "0.9.0", library = library)
    bootstrap(version = "1.0.0", library = library)
    bootstrap(version = "1.0.0.1", library = library)
  })
})

test_that("bootstrapping gives informative output when download fails", {

  local_mocked_bindings(
    renv_bootstrap_download_impl = function(...) {
      stop("test failure")
    },
    renv_bootstrap_download_cran_latest_find = function(version) {
      if (package_version(version) < "1.0.0") {
        list(type = "binary", repos = "https://cran.rstudio.com")
      }
    }
  )

  renv_scope_options(renv.bootstrap.quiet = FALSE)
  library <- renv_scope_tempfile()

  renv_tests_scope()
  expect_snapshot(error = TRUE, {
    bootstrap(version = "0.9.0", library = library)
    bootstrap(version = "1.0.0", library = library)
    bootstrap(version = "1.0.0.1", library = library)
  })

})

test_that("bootstrapping gives informative output when install fails", {

  local_mocked_bindings(
    renv_bootstrap_download_impl = function(url, destfile) {
      file.create(destfile)
      0L
    },
    renv_bootstrap_install_impl = function(...) {
      structure("test failure", status = 123L)
    }
  )

  renv_scope_options(renv.bootstrap.quiet = FALSE)
  library <- renv_scope_tempfile()

  renv_tests_scope()
  expect_snapshot(error = TRUE, {
    bootstrap(version = "1.0.0.1", library = library)
  })

})



# helpers -----------------------------------------------------------------

test_that("renv_boostrap_version_validate() recognises when versions are the same", {

  expect_true(
    renv_bootstrap_validate_version(
      version = structure("1.2.3", sha = "abcd123"),
      description = list(RemoteSha = "abcd1234567")
    )
  )

  expect_true(
    renv_bootstrap_validate_version(
      version = "1.2.3",
      description = list(Version = "1.2.3")
    )
  )

})


test_that("renv_boostrap_version_validate() gives good warnings", {
  renv_scope_options(renv.bootstrap.quiet = FALSE)

  expect_snapshot({

    # out-of-sync (request release; have different release)
    . <- renv_bootstrap_validate_version(
      version = "1.2.3",
      description = list(Version = "2.3.4")
    )

    # out-of-sync (request dev; have release)
    . <- renv_bootstrap_validate_version(
      version = "1.2.3-1",
      description = list(Version = "1.2.3")
    )

    # out-of-sync (request dev; have different dev)
    . <- renv_bootstrap_validate_version(
      version = structure("1.2.3-1", sha = "22d015905828c3398728a5ff9e381e0433976263"),
      description = list(
        Version = "1.2.3-1",
        RemoteType = "github",
        RemoteSha = "6b09befaaba3f55e0e2c141cb45c5d247b61ef1e"
      )
    )

    # out-of-sync (request dev; have release)
    . <- renv_bootstrap_validate_version(
      version = structure("1.2.3-1", sha = "22d015905828c3398728a5ff9e381e0433976263"),
      description = list(
        Version = "1.2.3"
      )
    )

  })

})

test_that("bootstrap version validation handles 'standard' remote types", {

  renv_scope_options(renv.bootstrap.quiet = FALSE)
  expect_snapshot(

    . <- renv_bootstrap_validate_version(
      version = "1.0.0",
      description = list(
        Version = "1.0.1",
        RemoteType = "standard",
        RemoteSha = "1.0.1"
      )
    )

  )

})

test_that("version validation warnings are suppressed by RENV_CONFIG_STARTUP_QUIET", {

  # https://github.com/rstudio/renv/issues/2214
  renv_scope_options(renv.bootstrap.quiet = FALSE)
  renv_scope_envvars(RENV_CONFIG_STARTUP_QUIET = "TRUE")

  expect_silent(
    renv_bootstrap_validate_version(
      version = "1.2.3",
      description = list(Version = "2.3.4")
    )
  )

})

test_that("version validation warnings are suppressed by RENV_CONFIG_SYNCHRONIZED_CHECK=FALSE", {

  # https://github.com/rstudio/renv/issues/2214
  renv_scope_options(renv.bootstrap.quiet = FALSE)
  renv_scope_envvars(RENV_CONFIG_SYNCHRONIZED_CHECK = "FALSE")

  expect_silent(
    renv_bootstrap_validate_version(
      version = "1.2.3",
      description = list(Version = "2.3.4")
    )
  )

})

# repos override tests ----------------------------------------------------

test_that("repos override supports unnamed repo", {
  renv_tests_scope()
  url <- "https://p3m.dev/cran/latest"
  renv_scope_envvars(RENV_CONFIG_REPOS_OVERRIDE = url)
  repos <- renv_bootstrap_repos()
  expect_true(url %in% repos)
})

test_that("repos override can use 1 named repository", {
  renv_tests_scope()
  override <- "CRAN=https://p3m.dev/cran/latest"
  renv_scope_envvars(RENV_CONFIG_REPOS_OVERRIDE = override)
  repos <- renv_bootstrap_repos()
  expect_equal(repos["CRAN"], c(CRAN = "https://p3m.dev/cran/latest"))
})

test_that("repos override can use 2+ named repositories", {
  renv_tests_scope()
  override <- "CRAN=https://p3m.dev/cran/latest;R_UNIV=https://posit-dev.r-universe.dev"
  renv_scope_envvars(RENV_CONFIG_REPOS_OVERRIDE = override)
  repos <- renv_bootstrap_repos()
  expect_equal(repos["CRAN"], c(CRAN = "https://p3m.dev/cran/latest"))
  expect_equal(repos["R_UNIV"], c(R_UNIV = "https://posit-dev.r-universe.dev"))
  expect_equal(length(repos), 2L)
})

test_that("repos override can handle `=` in repo urls", {
  renv_tests_scope()
  override <- "R_UNIV=https://posit-dev.r-universe.dev/?example=query"
  renv_scope_envvars(RENV_CONFIG_REPOS_OVERRIDE = override)
  repos <- renv_bootstrap_repos()
  expect_equal(repos["R_UNIV"], c(R_UNIV = "https://posit-dev.r-universe.dev/?example=query"))
})

test_that("repos override handle paths", {
  renv_tests_scope()
  override <- "CRAN=https://p3m.dev/cran/__linux__/noble/latest"
  renv_scope_envvars(RENV_CONFIG_REPOS_OVERRIDE = override)
  repos <- renv_bootstrap_repos()
  expect_equal(repos["CRAN"], c(CRAN = "https://p3m.dev/cran/__linux__/noble/latest"))
})
