tests/testthat/test-scaffold.R

test_that("scaffold() fails fast when not in a Framework project", {
  # Ensure tempdir has no leftover settings markers from other tests
  unlink(file.path(tempdir(), "settings.yml"))
  unlink(file.path(tempdir(), "config.yml"))

  # Create a temporary empty directory
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  # Change to the temporary directory
  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  # scaffold() should fail with clear error message
  expect_error(
    scaffold(),
    "Could not locate a Framework project",
    fixed = TRUE
  )

  expect_error(
    scaffold(),
    "scaffold\\(\\) searches for a project by looking for",
    fixed = FALSE  # Use regex for line breaks
  )

  expect_error(
    scaffold(),
    "To create a new project, use: project_create()",
    fixed = TRUE
  )
})

test_that("scaffold() works when config.yml exists", {
  # Create a temporary directory with minimal config
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  # Change to the temporary directory
  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  # Create minimal config.yml
  writeLines(
    c(
      "default:",
      "  project_type: project",
      "  directories:",
      "    notebooks: notebooks"
    ),
    "config.yml"
  )

  # Create minimal .env file
  writeLines("", ".env")

  # scaffold() should succeed (may issue warnings about missing dirs, but shouldn't error)
  expect_no_error(scaffold())

  # Should have recorded scaffold history in the database
  history <- framework:::.get_scaffold_history(tmp_dir)
  expect_true(inherits(history$first, "POSIXct"))
  expect_true(inherits(history$last, "POSIXct"))
})

test_that(".mark_scaffolded() stores history in the project database", {
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  if (file.exists("framework.db")) {
    unlink("framework.db")
  }

  framework:::.mark_scaffolded(tmp_dir)

  history <- framework:::.get_scaffold_history(tmp_dir)
  expect_true(inherits(history$first, "POSIXct"))
  expect_true(inherits(history$last, "POSIXct"))
  expect_true(file.exists(file.path(tmp_dir, "framework.db")))
  expect_false(file.exists(".framework_scaffolded"))
})

test_that(".mark_scaffolded() migrates legacy marker files", {
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  legacy_path <- ".framework_scaffolded"
  legacy_lines <- c(
    "First scaffolded at: 2024-01-01 12:00:00",
    "Last scaffolded at: 2024-01-02 08:30:00"
  )
  writeLines(legacy_lines, legacy_path)

  framework:::.mark_scaffolded(tmp_dir)
  history <- framework:::.get_scaffold_history(tmp_dir)

  expect_false(file.exists(legacy_path))
  expect_equal(
    lubridate::ymd_hms("2024-01-01 12:00:00", tz = "UTC"),
    history$first
  )
  expect_true(history$last >= history$first)
})

test_that("standardize_wd() returns NULL when project not found", {
  unlink(file.path(tempdir(), "settings.yml"))
  unlink(file.path(tempdir(), "config.yml"))

  # Create a temporary empty directory
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  # Change to the temporary directory
  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  # standardize_wd() should return NULL silently
  result <- standardize_wd()
  expect_null(result)
})

test_that("standardize_wd() finds and returns project root", {
  # Create a temporary directory with config.yml
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  # Change to the temporary directory
  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  # Create config.yml
  writeLines(
    c(
      "default:",
      "  project_type: project"
    ),
    "config.yml"
  )

  # standardize_wd() should return the project root
  result <- standardize_wd()
  expect_equal(normalizePath(result), normalizePath(tmp_dir))
})

test_that("dotenv loads from current directory by default", {
  # Create a temporary directory
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  # Create config.yml without dotenv_location
  writeLines(
    c(
      "default:",
      "  project_type: project",
      "  directories:",
      "    notebooks: notebooks"
    ),
    "config.yml"
  )

  # Create .env in current directory
  writeLines("TEST_VAR=current_dir", ".env")

  # Clear any existing TEST_VAR
  if (Sys.getenv("TEST_VAR") != "") {
    Sys.unsetenv("TEST_VAR")
  }

  # scaffold() should load .env from current directory
  expect_no_error(scaffold())
  expect_equal(Sys.getenv("TEST_VAR"), "current_dir")
})

test_that("dotenv loads from parent directory when dotenv_location set", {
  # Create parent and child directories
  parent_dir <- tempfile()
  dir.create(parent_dir)
  child_dir <- file.path(parent_dir, "child")
  dir.create(child_dir)
  on.exit(unlink(parent_dir, recursive = TRUE))

  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(child_dir)

  # Create config.yml with dotenv_location pointing to parent
  writeLines(
    c(
      "default:",
      "  dotenv_location: \"../\"",
      "  project_type: project",
      "  directories:",
      "    notebooks: notebooks"
    ),
    "config.yml"
  )

  # Create .env in parent directory
  writeLines("TEST_VAR=parent_dir", file.path(parent_dir, ".env"))

  # Clear any existing TEST_VAR
  if (Sys.getenv("TEST_VAR") != "") {
    Sys.unsetenv("TEST_VAR")
  }

  # scaffold() should load .env from parent directory
  expect_no_error(scaffold())
  expect_equal(Sys.getenv("TEST_VAR"), "parent_dir")
})

test_that(".ensure_framework_db respects project root", {
  test_dir <- create_test_project()
  old_wd <- getwd()
  on.exit({
    setwd(old_wd)
    cleanup_test_dir(test_dir)
  }, add = TRUE)

  setwd(test_dir)
  expect_true(file.exists("framework.db"))

  # No message when framework.db already exists
  expect_silent(framework:::.ensure_framework_db())

  # Remove database and ensure it is recreated from subdirectory
  unlink("framework.db")
  dir.create(file.path(test_dir, "notebooks"), showWarnings = FALSE)
  setwd(file.path(test_dir, "notebooks"))
  expect_message(framework:::.ensure_framework_db(), "Created framework.db")
  expect_true(file.exists(file.path(test_dir, "framework.db")))
})

test_that(".set_random_seed honours seed_on_scaffold flag", {
  withr::local_seed(42)
  config <- list(
    seed = NULL,
    options = list(
      seed = 9876,
      seed_on_scaffold = TRUE
    )
  )

  expect_message(framework:::.set_random_seed(config), 'Random seed set to 9876.', fixed = TRUE)

  withr::local_seed(42)
  config$options$seed_on_scaffold <- FALSE
  expect_silent(framework:::.set_random_seed(config))
})

test_that("dotenv_location errors when file not found", {
  # Create a temporary directory
  tmp_dir <- tempfile()
  dir.create(tmp_dir)
  on.exit(unlink(tmp_dir, recursive = TRUE))

  old_wd <- getwd()
  on.exit(setwd(old_wd), add = TRUE)
  setwd(tmp_dir)

  # Create config.yml with dotenv_location pointing to nonexistent location
  writeLines(
    c(
      "default:",
      "  dotenv_location: \"../nonexistent/\"",
      "  project_type: project"
    ),
    "config.yml"
  )

  # scaffold() should error with clear message
  expect_error(
    scaffold(),
    "Dotenv file not found at"
  )
})

Try the framework package in your browser

Any scripts or data that you put into this service are public.

framework documentation built on Feb. 18, 2026, 1:07 a.m.