tests/testthat/test-download_url.R

#===============================================================================
# Test: download_url()
# File: test-download_url.R
# Description: Unit tests for download_url() function
#===============================================================================
#
# Testing Strategy:
# 1. Input validation tests: Fast, no network needed, run on CRAN
# 2. Network tests: Slow, skip on CRAN with skip_on_cran() + skip_if_offline()
#
# This ensures:
# - CRAN compliance (no long-running tests, no network dependency)
# - Good coverage (validation logic is tested)
# - Real-world verification (network tests run in CI/local)
#===============================================================================

# ------------------------------------------------------------------------------
# Input validation tests (FAST, NO NETWORK, RUN ON CRAN)
# ------------------------------------------------------------------------------

test_that("download_url() validates URL parameter", {
  # These tests fail BEFORE any network operation
  # Safe to run on CRAN (fast, no network)

  temp_dest <- tempfile()

  expect_error(
    download_url(NULL, dest = temp_dest),
    "url must be a single non-empty character string"
  )

  expect_error(
    download_url(123, dest = temp_dest),
    "url must be a single non-empty character string"
  )

  expect_error(
    download_url(c("url1", "url2"), dest = temp_dest),
    "url must be a single non-empty character string"
  )

  expect_error(
    download_url("", dest = temp_dest),
    "url must be a single non-empty character string"
  )

  expect_error(
    download_url(NA_character_, dest = temp_dest),
    "url must be a single non-empty character string"
  )
})

test_that("download_url() validates dest parameter", {
  # These tests fail BEFORE any network operation
  # Safe to run on CRAN (fast, no network)

  valid_url <- "https://example.com/file.txt"

  expect_error(
    download_url(valid_url, dest = NULL),
    "must be specified"
  )

  expect_error(
    download_url(valid_url, dest = 123),
    "must be specified"
  )

  expect_error(
    download_url(valid_url, dest = c("dest1", "dest2")),
    "must be specified"
  )

  expect_error(
    download_url(valid_url, dest = ""),
    "must be specified"
  )

  expect_error(
    download_url(valid_url, dest = NA_character_),
    "must be specified"
  )
})

test_that("download_url() validates logical parameters", {
  # These tests fail BEFORE any network operation
  # Safe to run on CRAN (fast, no network)

  valid_url <- "https://example.com/file.txt"
  temp_dest <- tempfile()

  # overwrite
  expect_error(
    download_url(valid_url, dest = temp_dest, overwrite = "yes"),
    "overwrite must be a single logical value"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, overwrite = c(TRUE, FALSE)),
    "overwrite must be a single logical value"
  )

  # unzip
  expect_error(
    download_url(valid_url, dest = temp_dest, unzip = "yes"),
    "unzip must be a single logical value"
  )

  # verbose
  expect_error(
    download_url(valid_url, dest = temp_dest, verbose = "yes"),
    "verbose must be a single logical value"
  )

  # resume
  expect_error(
    download_url(valid_url, dest = temp_dest, resume = "yes"),
    "resume must be a single logical value"
  )
})

test_that("download_url() validates numeric parameters", {
  # These tests fail BEFORE any network operation
  # Safe to run on CRAN (fast, no network)

  valid_url <- "https://example.com/file.txt"
  temp_dest <- tempfile()

  # timeout
  expect_error(
    download_url(valid_url, dest = temp_dest, timeout = "600"),
    "timeout must be a positive number"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, timeout = 0),
    "timeout must be a positive number"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, timeout = -10),
    "timeout must be a positive number"
  )

  # retries
  expect_error(
    download_url(valid_url, dest = temp_dest, retries = "3"),
    "retries must be a non-negative integer"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, retries = -1),
    "retries must be a non-negative integer"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, retries = 1.5),
    "retries must be a non-negative integer"
  )

  # speed_limit
  expect_error(
    download_url(valid_url, dest = temp_dest, speed_limit = "1000000"),
    "speed_limit must be a positive number or NULL"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, speed_limit = 0),
    "speed_limit must be a positive number or NULL"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, speed_limit = -100),
    "speed_limit must be a positive number or NULL"
  )
})

test_that("download_url() validates headers parameter", {
  # These tests fail BEFORE any network operation
  # Safe to run on CRAN (fast, no network)

  valid_url <- "https://example.com/file.txt"
  temp_dest <- tempfile()

  expect_error(
    download_url(valid_url, dest = temp_dest, headers = "not-a-list"),
    "headers must be a list or NULL"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, headers = 123),
    "headers must be a list or NULL"
  )

  expect_error(
    download_url(valid_url, dest = temp_dest, headers = c("header1", "header2")),
    "headers must be a list or NULL"
  )
})

test_that("download_url() accepts valid parameters", {
  # Test that valid parameters pass validation
  # This will fail at network stage, but that's expected
  # We're only testing parameter validation here

  skip_on_cran()
  skip_if_offline()
  skip("Network download test skipped.")

  valid_url <- "https://httpbin.org/status/404"  # Will fail at download, not validation
  temp_dest <- tempfile()

  # Should NOT error on parameter validation
  # (will error later due to 404, but that's not what we're testing)
  expect_error(
    download_url(
      url = valid_url,
      dest = temp_dest,
      overwrite = TRUE,
      unzip = FALSE,
      verbose = FALSE,
      timeout = 10,
      headers = list("User-Agent" = "R-test"),
      resume = FALSE,
      speed_limit = 100000,
      retries = 0
    ),
    "Failed to download file",  # Network error, not validation error
    fixed = TRUE
  )
})

# ------------------------------------------------------------------------------
# Functional tests (SLOW, NEEDS NETWORK, SKIP ON CRAN)
# ------------------------------------------------------------------------------

test_that("download_url() works with valid small file", {
  skip_on_cran()
  skip_if_offline()
  skip("Network download test skipped.")

  # Use a tiny, reliable test file
  temp_file <- tempfile(fileext = ".txt")

  result <- download_url(
    url = "https://httpbin.org/robots.txt",
    dest = temp_file,
    verbose = FALSE,
    timeout = 30,
    retries = 2
  )

  # Check that download was successful
  expect_true(file.exists(temp_file))
  expect_type(result, "character")
  expect_equal(result, temp_file)

  # Check file has content
  expect_true(file.info(temp_file)$size > 0)

  # Cleanup
  unlink(temp_file)
})

test_that("download_url() respects overwrite parameter", {
  skip_on_cran()
  skip_if_offline()
  skip("Network download test skipped.")

  temp_file <- tempfile(fileext = ".txt")

  # Create a file first
  writeLines("original content", temp_file)
  original_content <- readLines(temp_file)

  # Try to download without overwrite - should skip
  result <- download_url(
    url = "https://httpbin.org/robots.txt",
    dest = temp_file,
    overwrite = FALSE,
    verbose = FALSE
  )

  # Should return the existing file path
  expect_equal(result, temp_file)

  # File should still contain original content
  new_content <- readLines(temp_file)
  expect_equal(new_content, original_content)

  # Cleanup
  unlink(temp_file)
})

test_that("download_url() creates destination directory", {
  skip_on_cran()
  skip_if_offline()
  skip("Network download test skipped.")

  temp_dir <- tempfile("test_download_")
  temp_file <- file.path(temp_dir, "subdir", "test.txt")

  result <- download_url(
    url = "https://httpbin.org/robots.txt",
    dest = temp_file,
    verbose = FALSE,
    timeout = 30
  )

  # Check that directory was created
  expect_true(dir.exists(dirname(temp_file)))
  expect_true(file.exists(temp_file))

  # Cleanup
  unlink(temp_dir, recursive = TRUE)
})

test_that("download_url() handles download failures gracefully", {
  skip_on_cran()
  skip_if_offline()
  skip("Network download test skipped.")

  temp_file <- tempfile(fileext = ".txt")

  # Test with non-existent URL (404)
  expect_error(
    download_url(
      "https://httpbin.org/status/404",
      dest = temp_file,
      verbose = FALSE,
      retries = 0,
      timeout = 10
    ),
    "Failed to download file"
  )

  # Cleanup
  if (file.exists(temp_file)) unlink(temp_file)
})

test_that("download_url() handles timeouts", {
  skip_on_cran()
  skip_if_offline()
  skip("Timeout test takes too long for regular CI")

  temp_file <- tempfile(fileext = ".txt")

  # Test with very short timeout on a slow endpoint
  expect_error(
    download_url(
      "https://httpbin.org/delay/5",  # 5 second delay
      dest = temp_file,
      verbose = FALSE,
      retries = 0,
      timeout = 1  # 1 second timeout
    ),
    "Failed to download file"
  )

  # Cleanup
  if (file.exists(temp_file)) unlink(temp_file)
})

test_that("download_url() works with custom headers", {
  skip_on_cran()
  skip_if_offline()
  skip("Network download test skipped.")

  temp_file <- tempfile(fileext = ".txt")

  # Test with custom headers (httpbin.org/headers echoes headers back)
  result <- download_url(
    url = "https://httpbin.org/headers",
    dest = temp_file,
    headers = list(
      "User-Agent" = "R-evanverse-test",
      "X-Custom-Header" = "test-value"
    ),
    verbose = FALSE,
    timeout = 30
  )

  expect_true(file.exists(temp_file))

  # Cleanup
  unlink(temp_file)
})

# ------------------------------------------------------------------------------
# Package dependency tests
# ------------------------------------------------------------------------------

test_that("download_url() checks for required packages", {
  # This tests the dependency check logic
  # Even if packages are installed, we verify the function structure

  # The function should be callable
  expect_type(download_url, "closure")

  # Check that curl is available (required dependency)
  expect_true(requireNamespace("curl", quietly = TRUE))
})

Try the evanverse package in your browser

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

evanverse documentation built on March 10, 2026, 5:07 p.m.