test_that("mail preview, failure and retry preserve pending approval and deduplicate", { queue<-tempfile();on.exit(unlink(queue,recursive=TRUE)) credentials<-tempfile();on.exit(unlink(credentials),add=TRUE) configure_reviewer_access(credentials,"owner@example.org") old_opts<-options(ALSdownloadeR.reviewer_credentials=credentials);on.exit(options(old_opts),add=TRUE) config<-list(from="app@example.org",to="owner@example.org",review_url="http://127.0.0.1:8792/",preview=TRUE) old_options<-options(ALSdownloadeR.submission_mail=config);on.exit(options(old_options),add=TRUE) p<-zenodo_build(zenodo_fixture(),zenodo_shape(),"2018","ALS","person@example.org") id<-submit_zenodo(p,queue) receipt<-file.path(queue,"notifications",paste0(id,".json")) expect_equal(jsonlite::fromJSON(receipt)$status,"preview") eml<-paste(readLines(file.path(queue,"notifications",paste0(id,".eml"))),collapse="\n") expect_match(eml,"To: owner@example.org",fixed=TRUE) body<-rawToChar(jsonlite::base64_dec(strsplit(eml,"\n\n",fixed=TRUE)[[1]][2])) expect_match(body,paste0("?zenodo_review=",id),fixed=TRUE) expect_match(body,"person@example.org",fixed=TRUE) expect_equal(zenodo_submissions(queue)$status,"pending") expect_false(dir.exists(file.path(queue,"approved"))) config$preview<-FALSE;options(ALSdownloadeR.submission_mail=config) local_mocked_bindings(zenodo_mail_send=function(...)stop("secret diagnostic"),.package="ALSdownloadeR") expect_identical(submit_zenodo(p,queue),id) expect_equal(jsonlite::fromJSON(receipt)$status,"failed") expect_false(any(grepl("secret",readLines(receipt)))) calls<-0L local_mocked_bindings(zenodo_mail_send=function(...){calls<<-calls+1L;TRUE},.package="ALSdownloadeR") submit_zenodo(p,queue);submit_zenodo(p,queue) expect_equal(calls,1L) expect_equal(jsonlite::fromJSON(receipt)$status,"sent") expect_equal(zenodo_submissions(queue)$status,"pending") config$to<-"owner@example.org\r\nBcc: another@example.org" expect_error(zenodo_mail_message(p,config),"addresses") config$to<-"owner@example.org";config$review_url<-"https://public.example.org/" expect_error(zenodo_mail_message(p,config),"localhost") }) test_that("Zenodo metadata and geometry create bounded explicit file mappings", { expect_equal(zenodo_record_id("https://zenodo.org/records/12345?preview_file=survey.laz"),"12345") expect_equal(zenodo_record_id("https://doi.org/10.5281/zenodo.12345"),"12345") expect_error(zenodo_record_id("https://example.org/records/12345"),"published") m<-zenodo_fixture(); g<-zenodo_shape() p<-zenodo_build(m,g,"","ALS") expect_null(p$index$features[[1]]$properties$acquired_start) expect_equal(p$index$features[[1]]$properties$url,"https://zenodo.org/records/12345/files/survey.laz") expect_equal(zenodo_dates("2018"),c("2018-01-01","2018-12-31")) expect_error(zenodo_dates("2020-02-30"),"dates") expect_error(zenodo_build(zenodo_fixture(c("a.laz","b.laz")),g,"","ALS"),"file_key") g$file_key<-"wrong.laz" expect_error(zenodo_build(m,g,"","ALS"),"Every file_key") g$file_key<-"survey.laz" expect_length(zenodo_build(m,rbind(g,g),"2018","ALS")$index$features,1) z<-zenodo_build(zenodo_fixture("survey clouds.zip"),zenodo_shape(),"2018","UAV-LiDAR") expect_match(z$index$features[[1]]$properties$url,"survey%20clouds.zip",fixed=TRUE) expect_error(zenodo_build(m,sf::st_set_crs(g,NA),"","ALS"),"CRS") expect_error(zenodo_build(m,g,"","TLS"),"Confirm") expect_error(zenodo_boundary_download(m,"missing.geojson"),"listed") }) test_that("pending requests require maintainer acceptance and a stable record", { queue<-tempfile();on.exit(unlink(queue,recursive=TRUE)) m<-zenodo_fixture();p<-zenodo_build(m,zenodo_shape(),"2018","ALS","private@example.org") local_mocked_bindings(inspect_zenodo=function(...)m,.package="ALSdownloadeR") id<-submit_zenodo(p,queue) expect_identical(zenodo_build(m,zenodo_shape(),"2018","ALS")$id,id) expect_identical(submit_zenodo(p,queue),id) expect_equal(nrow(zenodo_submissions(queue)),1) expect_equal(zenodo_submissions(queue)$status,"pending") expect_false(dir.exists(file.path(queue,"approved"))) expect_error(review_zenodo_submission(queue,id,"approve","Maintainer"),"confirmation") path<-review_zenodo_submission(queue,id,"approve","Maintainer",TRUE) expect_equal(nrow(read_tile_index(path)),1) expect_equal(zenodo_submissions(queue)$status,"approve") expect_false(any(grepl("private@example.org",readLines(path),fixed=TRUE))) expect_equal(nrow(find_tiles(zenodo_shape(),"contributed",tile_index_dir=file.path(queue,"approved"))),1) expect_error(review_zenodo_submission(queue,id,"approve","Maintainer",TRUE),"already has") }) test_that("changed metadata, tampering and rejection cannot activate a proposal", { queue<-tempfile();on.exit(unlink(queue,recursive=TRUE)) m<-zenodo_fixture();p<-zenodo_build(m,zenodo_shape(),"2018","ALS") id<-submit_zenodo(p,queue) changed<-m;changed$fingerprint<-"changed" local_mocked_bindings(inspect_zenodo=function(...)changed,.package="ALSdownloadeR") expect_error(review_zenodo_submission(queue,id,"approve","Maintainer",TRUE),"metadata changed") expect_false(dir.exists(file.path(queue,"approved"))) changed<-m p$index$features[[1]]$properties$url<-"https://example.org/evil.laz" zenodo_write(p,file.path(queue,"requests",paste0(id,".json"))) # Approval must validate the submitted index as well as rebuilding it. expect_error(review_zenodo_submission(queue,id,"approve","Maintainer",TRUE),"contents") review_zenodo_submission(queue,id,"reject","Maintainer",reason="Incorrect mapping") expect_equal(zenodo_submissions(queue)$status,"reject") expect_false(dir.exists(file.path(queue,"approved"))) }) test_that("the public form can queue a proposal but cannot invoke reviewer actions", { queue<-tempfile();on.exit(unlink(queue,recursive=TRUE)) coverage<-tempfile(fileext=".geojson");on.exit(unlink(coverage),add=TRUE) g<-zenodo_shape();g$tile_id<-"survey";sf::st_write(g,coverage,quiet=TRUE) local_mocked_bindings(inspect_zenodo=function(...)zenodo_fixture(),.package="ALSdownloadeR") shiny::testServer(function(input,output,session)zenodo_submission_server(input,output,session,queue),{ session$setInputs(zenodo_link="12345",zenodo_has_boundary="yes",zenodo_boundary_source="upload",zenodo_id_column="tile_id",zenodo_map_1="survey.laz",zenodo_year_mode="single",zenodo_year_1="2018", zenodo_platform="ALS",zenodo_email="test@example.org",zenodo_boundary=list(name="coverage.geojson",datapath=coverage)) session$setInputs(zenodo_inspect=1) expect_match(output$zenodo_metadata_status,"Fictitious aerial survey") session$setInputs(zenodo_prepare=1) expect_match(output$zenodo_status,"Validated") session$setInputs(zenodo_send=1) expect_equal(zenodo_submissions(queue)$status,"pending") session$setInputs(zenodo_review_id=zenodo_submissions(queue)$id,zenodo_review_confirm=TRUE,zenodo_review_approve=1) expect_false(dir.exists(file.path(queue,"approved"))) session$setInputs(zenodo_year_1="2019") session$setInputs(zenodo_send=2) expect_match(output$zenodo_status,"Validate the submission first") }) })