test_that("tt_find_dataset_files finds expected files", { expect_identical( tt_find_dataset_files(test_path("fixtures", "tt_submission")), c( test_path("fixtures", "tt_submission", "cleaning.R"), test_path("fixtures", "tt_submission", "intro.md"), test_path("fixtures", "tt_submission", "meta.yaml"), test_path("fixtures", "tt_submission", "states.csv"), test_path("fixtures", "tt_submission", "states.md"), test_path("fixtures", "tt_submission", "states_population.png") ) ) }) test_that("tt_find_dataset_files errors informatively for extra files", { stbl::expect_pkg_error_snapshot( tt_find_dataset_files(test_path("fixtures", "tt_submission_extra")), "tidytuesdayR", "extra_files" ) }) test_that("tt_find_dataset_files errors informatively for missing files", { stbl::expect_pkg_error_snapshot( tt_find_dataset_files(test_path("fixtures", "tt_submission_missing")), "tidytuesdayR", "missing_expected" ) }) test_that("tt_find_dataset_files errors informatively for missing images", { stbl::expect_pkg_error_snapshot( tt_find_dataset_files(test_path( "fixtures", "tt_submission_missing_image1" )), "tidytuesdayR", "missing_images" ) stbl::expect_pkg_error_snapshot( tt_find_dataset_files(test_path( "fixtures", "tt_submission_missing_image2" )), "tidytuesdayR", "no_images" ) }) test_that("tt_find_dataset_files errors informatively for missing dictionary", { stbl::expect_pkg_error_snapshot( tt_find_dataset_files(test_path("fixtures", "tt_submission_missing_md")), "tidytuesdayR", "missing_dictionaries" ) }) test_that("tt_find_branch deals with branch names", { path <- withr::local_tempdir() branch <- tt_find_branch(path) expect_match( branch, "submission-\\d{4}-\\d{2}-\\d{2}", all = TRUE ) branch_file_path <- fs::path(path, "branch.txt") expect_true(fs::file_exists(branch_file_path)) branch2 <- tt_find_branch(path) expect_identical(branch2, branch) }) test_that("tt_user extracts login from GitHub response", { local_mocked_call_gh(function(endpoint, ..., auth) { expect_equal(endpoint, "GET /user") expect_equal(auth, "test_token") return(list(login = "testuser", id = 12345)) }) result <- tt_user(auth = "test_token") expect_equal(result, "testuser") }) test_that("tt_fork returns fork info from GitHub and syncs it (#147)", { local_mocked_bindings( tt_sync_fork = function(fork_repo, branch, auth) { expect_equal(fork_repo, "testuser/tidytuesday") expect_equal(branch, "main") expect_equal(auth, "test_token") return(invisible(NULL)) } ) local_mocked_call_gh(function(endpoint, repo, auth, ...) { expect_equal(endpoint, "POST /repos/{repo}/forks") expect_equal(repo, "rfordatascience/tidytuesday") expect_equal(auth, "test_token") return(list( full_name = "testuser/tidytuesday", default_branch = "main", owner = list(login = "testuser") )) }) result <- tt_fork( user = "testuser", repo = "rfordatascience/tidytuesday", auth = "test_token" ) expect_equal(result$full_name, "testuser/tidytuesday") expect_equal(result$default_branch, "main") }) test_that("tt_sync_fork calls merge-upstream API (#147)", { local_mocked_call_gh(function(endpoint, fork_repo, branch, auth, ...) { expect_equal(endpoint, "POST /repos/{fork_repo}/merge-upstream") expect_equal(fork_repo, "testuser/tidytuesday") expect_equal(branch, "main") expect_equal(auth, "test_token") return(list( message = "Successfully fetched and fast-forwarded from upstream", merge_type = "fast-forward", base_branch = "upstream:main" )) }) expect_invisible( tt_sync_fork( fork_repo = "testuser/tidytuesday", branch = "main", auth = "test_token" ) ) }) test_that("tt_branch_create creates new branch from default branch", { local_mocked_call_gh(function( endpoint, fork_repo, default_branch, auth, ref, sha, ... ) { if (endpoint == "GET /repos/{fork_repo}/git/refs/heads/{default_branch}") { expect_equal(fork_repo, "testuser/tidytuesday") expect_equal(default_branch, "main") return(list(object = list(sha = "abc123"))) } if (endpoint == "GET /repos/{fork_repo}/git/refs/heads") { expect_equal(fork_repo, "testuser/tidytuesday") return(list()) # No existing branches } if (endpoint == "POST /repos/{fork_repo}/git/refs") { expect_equal(fork_repo, "testuser/tidytuesday") expect_equal(ref, "refs/heads/test-branch") expect_equal(sha, "abc123") return(list(ref = "refs/heads/test-branch")) } }) fork_info <- list(full_name = "testuser/tidytuesday", default_branch = "main") result <- tt_branch_create(fork_info, "test-branch", auth = "test_token") expect_equal(result, "refs/heads/test-branch") }) test_that("tt_branch_create skips if branch already exists", { local_mocked_call_gh(function( endpoint, fork_repo, default_branch, auth, ... ) { if (endpoint == "GET /repos/{fork_repo}/git/refs/heads/{default_branch}") { return(list(object = list(sha = "abc123"))) } if (endpoint == "GET /repos/{fork_repo}/git/refs/heads") { return(list( list(ref = "refs/heads/main"), list(ref = "refs/heads/test-branch") # Branch already exists )) } stop("Should not create branch if it already exists") }) fork_info <- list(full_name = "testuser/tidytuesday", default_branch = "main") result <- tt_branch_create(fork_info, "test-branch", auth = "test_token") expect_equal(result, "refs/heads/test-branch") }) test_that("tt_branch_content returns file info from GitHub", { local_mocked_call_gh(function(endpoint, fork_repo, ref, auth, ...) { expect_equal( endpoint, "GET /repos/{fork_repo}/contents/data/curated/new_submission" ) expect_equal(fork_repo, "testuser/tidytuesday") expect_equal(ref, "refs/heads/test-branch") return(list( list(name = "data.csv", sha = "sha123"), list(name = "meta.yaml", sha = "sha456") )) }) result <- tt_branch_content( "testuser/tidytuesday", "test-branch", auth = "test_token" ) expect_equal(names(result), c("data.csv", "meta.yaml")) expect_equal(result[["data.csv"]], "sha123") expect_equal(result[["meta.yaml"]], "sha456") }) test_that("tt_branch_content returns empty list if folder doesn't exist", { local_mocked_call_gh(function(endpoint, fork_repo, ref, auth, ...) { stop("404 error") }) result <- tt_branch_content( "testuser/tidytuesday", "test-branch", auth = "test_token" ) expect_equal(result, structure(list(), names = character(0))) }) test_that("git_blob_sha1 calculates git blob SHA correctly", { # Create a temporary test file temp_file <- withr::local_tempfile() writeLines("test content", temp_file) result <- git_blob_sha1(temp_file) # Should return a 40-character hex string (SHA-1 hash) expect_type(result, "character") expect_match(result, "^[a-f0-9]{40}$") # Same file should produce same hash result2 <- git_blob_sha1(temp_file) expect_equal(result, result2) }) test_that("tt_branch_populate adds new file when it doesn't exist", { temp_file <- withr::local_tempfile() writeLines("test content", temp_file) local_mocked_bindings( tt_branch_content = function(...) { return(structure(list(), names = character(0))) # No existing files } ) local_mocked_call_gh(function( endpoint, fork_repo, filename, message, content, branch, sha, auth, ... ) { expect_equal( endpoint, "PUT /repos/{fork_repo}/contents/data/curated/new_submission/{filename}" ) expect_equal(fork_repo, "testuser/tidytuesday") expect_match(message, "^Add ") expect_null(sha) return(list(content = list(name = filename))) }) expect_no_error( tt_branch_populate( fork_repo = "testuser/tidytuesday", branch = "test-branch", files = temp_file, auth = "test_token" ) ) }) test_that("tt_branch_populate updates existing file when SHA differs", { temp_file <- withr::local_tempfile() writeLines("new content", temp_file) local_mocked_bindings( tt_branch_content = function(...) { return(structure( list("different_sha"), names = basename(temp_file) )) } ) local_mocked_call_gh(function( endpoint, fork_repo, filename, message, content, branch, sha, auth, ... ) { expect_equal( endpoint, "PUT /repos/{fork_repo}/contents/data/curated/new_submission/{filename}" ) expect_match(message, "^Update ") expect_equal(sha, "different_sha") return(list(content = list(name = filename))) }) expect_no_error( tt_branch_populate( fork_repo = "testuser/tidytuesday", branch = "test-branch", files = temp_file, auth = "test_token" ) ) }) test_that("tt_branch_populate skips file when SHA matches", { temp_file <- withr::local_tempfile() writeLines("test content", temp_file) # Get the actual SHA for this file file_sha <- git_blob_sha1(temp_file) local_mocked_bindings( tt_branch_content = function(...) { return(structure( list(file_sha), names = basename(temp_file) )) } ) local_mocked_call_gh(function(...) { stop("Should not be called when SHA matches") }) expect_no_error( tt_branch_populate( fork_repo = "testuser/tidytuesday", branch = "test-branch", files = temp_file, auth = "test_token" ) ) }) test_that("tt_submit informs about the PR url", { local_mocked_bindings( gh_auth_check = function(...) { return("auth") }, tt_user = function(auth) { return("testuser") }, tt_fork = function(...) { return(list(full_name = "testuser/tidytuesday", default_branch = "main")) }, tt_find_branch = function(...) { return("submission-2025-01-01") }, tt_branch_create = function(...) { return("branch_response") }, tt_branch_populate = function(...) { return("populate_response") } ) local_mocked_call_gh(function(endpoint, ...) { if (grepl("/pulls", endpoint)) { return(list()) # No existing PRs } stop("Unexpected call_gh endpoint: ", endpoint) }) expect_message( { test_result <- expect_invisible({ tt_submit( test_path("fixtures", "tt_submission"), auth = "noauth", open = FALSE ) }) }, "Create PR at" ) expect_identical( test_result, glue::glue( "https://github.com/rfordatascience/tidytuesday/compare/main...testuser:submission-2025-01-01" ) ) }) # CSV file validation -------------------------------------------------------- test_that("tt_find_csv_files passes when CSVs are under 25MB", { expect_no_error( tt_find_csv_files(test_path("fixtures", "tt_submission")) ) }) test_that("tt_find_csv_files errors when CSV exceeds 25MB", { local_mocked_bindings( file_size = function(path) { fs::fs_bytes("30MB") }, .package = "fs" ) stbl::expect_pkg_error_snapshot( tt_find_csv_files(test_path("fixtures", "tt_submission")), "tidytuesdayR", "csv_size" ) }) test_that("tt_find_csv_files handles multiple large CSVs", { local_mocked_bindings( dir_ls = function(path, glob) { c( test_path("fixtures", "tt_submission", "file1.csv"), test_path("fixtures", "tt_submission", "file2.csv") ) }, file_size = function(path) { c(fs::fs_bytes("30MB"), fs::fs_bytes("40MB")) }, .package = "fs" ) stbl::expect_pkg_error_snapshot( tt_find_csv_files(test_path("fixtures", "tt_submission")), "tidytuesdayR", "csv_size" ) }) # Image validation and resizing ---------------------------------------------- test_that("tt_find_images passes when images are under limit", { expect_no_error( tt_find_images(test_path("fixtures", "tt_submission")) ) }) test_that("tt_find_images resizes large images non-interactively", { # Use temp dir so we can resize. temp_dir <- withr::local_tempdir() fs::file_copy( fs::dir_ls(test_path("fixtures", "tt_submission_large_img")), temp_dir ) img_path <- fs::path(temp_dir, "states_population.png") withr::local_options(list(rlang_interactive = FALSE)) expect_message( expect_message( tt_find_images(temp_dir), "exceeds the Bluesky limit" ), "Image resized from" ) expect_lte( unclass(fs::file_size(img_path)), unclass(fs::fs_bytes("976.56KB")) ) }) test_that("tt_find_images cancels on user rejection", { withr::local_options( list( rlang_interactive = TRUE, viewer = function(x) invisible(NULL) ) ) local_mocked_bindings( menu = function(choices, title) { 2 # Choose "No, cancel submission" }, .package = "utils" ) expect_message( stbl::expect_pkg_error_classes( tt_find_images( test_path("fixtures", "tt_submission_large_img") ), "tidytuesdayR", "cancelled" ), "exceeds the Bluesky limit" ) }) test_that("tt_find_images accepts on user approval", { # Use temp dir so we can resize. temp_dir <- withr::local_tempdir() fs::file_copy( fs::dir_ls(test_path("fixtures", "tt_submission_large_img")), temp_dir ) withr::local_options( list( rlang_interactive = TRUE, viewer = function(x) invisible(NULL) ) ) local_mocked_bindings( menu = function(choices, title) { 1 # Choose "Yes, use resized image" }, .package = "utils" ) expect_message( expect_message( tt_find_images(temp_dir), "exceeds the Bluesky limit" ), "Image resized from" ) })