# Integration tests that don't require async callbacks test_that("document persistence survives server restart", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) # Create server, add document server1 <- sync_server(data_dir = data_dir) doc_id <- create_document(server1) doc <- get_document(server1, doc_id) automerge::am_put(doc, automerge::AM_ROOT, "persistent", "data") autosync:::save_document(attr(server1, "sync"), doc_id, doc) server1$close() # Create new server with same data_dir server2 <- sync_server(data_dir = data_dir) on.exit(server2$close(), add = TRUE) # Document should be loaded expect_true(doc_id %in% list_documents(server2)) doc2 <- get_document(server2, doc_id) expect_equal(automerge::am_get(doc2, automerge::AM_ROOT, "persistent"), "data") }) test_that("server creates data directory if it doesn't exist", { data_dir <- file.path(tempfile(), "nested", "path") on.exit(unlink(dirname(dirname(data_dir)), recursive = TRUE)) expect_false(dir.exists(data_dir)) server <- sync_server(data_dir = data_dir) on.exit(server$close(), add = TRUE) expect_true(dir.exists(data_dir)) }) test_that("multiple documents can be managed simultaneously", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) server <- sync_server(data_dir = data_dir) on.exit(server$close(), add = TRUE) # Create multiple documents doc_ids <- sapply(1:5, function(i) { doc_id <- create_document(server) doc <- get_document(server, doc_id) automerge::am_put(doc, automerge::AM_ROOT, "index", i) doc_id }) # Verify each document for (i in 1:5) { doc <- get_document(server, doc_ids[i]) expect_equal(automerge::am_get(doc, automerge::AM_ROOT, "index"), i) } }) test_that("get_document returns NULL for non-existent document", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) server <- sync_server(data_dir = data_dir) on.exit(server$close()) result <- get_document(server, "nonexistent123") expect_null(result) }) test_that("create_document with explicit ID", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) server <- sync_server(data_dir = data_dir) on.exit(server$close()) explicit_id <- generate_document_id() returned_id <- create_document(server, doc_id = explicit_id) expect_equal(returned_id, explicit_id) expect_true(explicit_id %in% list_documents(server)) }) test_that("server URL format is correct", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) server <- sync_server(host = "127.0.0.1", data_dir = data_dir) on.exit(server$close()) expect_true(grepl("^ws://127\\.0\\.0\\.1:\\d+$", server$url)) }) test_that("save_sync_state and load_sync_states round-trip correctly", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) state <- new.env(hash = TRUE, parent = emptyenv()) state$data_dir <- data_dir state$sync_states <- new.env(hash = TRUE, parent = emptyenv()) storage_id <- "testStoragePeer" doc_id <- generate_document_id() sync_state <- automerge::am_sync_state() # Save autosync:::save_sync_state(state, storage_id, doc_id, sync_state) expect_true(file.exists( file.path(data_dir, ".sync_states", storage_id, paste0(doc_id, ".sync")) )) # Load into a new client_id autosync:::load_sync_states(state, storage_id, "newClientId") expect_true(exists("newClientId", envir = state$sync_states)) expect_true(exists(doc_id, envir = state$sync_states[["newClientId"]])) expect_true(inherits(state$sync_states[["newClientId"]][[doc_id]], "am_syncstate")) }) test_that("load_sync_states with no persisted data is a no-op", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) state <- new.env(hash = TRUE, parent = emptyenv()) state$data_dir <- data_dir state$sync_states <- new.env(hash = TRUE, parent = emptyenv()) autosync:::load_sync_states(state, "nonexistentPeer", "clientX") expect_false(exists("clientX", envir = state$sync_states)) }) test_that("save_sync_state warns when the file cannot be written", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) state <- new.env(hash = TRUE, parent = emptyenv()) state$data_dir <- data_dir storage_id <- "writeFailPeer" doc_id <- "blockedDoc" # Pre-create a *directory* exactly where the .sync file would be written, so # writeBin() fails on it. blocking_dir <- file.path( data_dir, ".sync_states", storage_id, paste0(doc_id, ".sync") ) dir.create(blocking_dir, recursive = TRUE) # writeBin() to a directory emits incidental platform warnings before the # connection error that save_sync_state() catches; muffle those so only the # "Failed to save sync state" warning we care about is asserted. expect_warning( withCallingHandlers( autosync:::save_sync_state(state, storage_id, doc_id, automerge::am_sync_state()), warning = function(w) { if (!grepl("Failed to save sync state", conditionMessage(w))) { invokeRestart("muffleWarning") } } ), "Failed to save sync state" ) }) test_that("load_sync_states ignores a peer directory with no sync files", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) state <- new.env(hash = TRUE, parent = emptyenv()) state$data_dir <- data_dir state$sync_states <- new.env(hash = TRUE, parent = emptyenv()) storage_id <- "emptyPeer" dir.create(file.path(data_dir, ".sync_states", storage_id), recursive = TRUE) autosync:::load_sync_states(state, storage_id, "clientE") expect_false(exists("clientE", envir = state$sync_states)) }) test_that("load_sync_states warns on a corrupted sync file", { data_dir <- tempfile() dir.create(data_dir) on.exit(unlink(data_dir, recursive = TRUE)) state <- new.env(hash = TRUE, parent = emptyenv()) state$data_dir <- data_dir state$sync_states <- new.env(hash = TRUE, parent = emptyenv()) storage_id <- "corruptPeer" dir <- file.path(data_dir, ".sync_states", storage_id) dir.create(dir, recursive = TRUE) doc_id <- generate_document_id() writeBin(as.raw(c(0xFF, 0xFF, 0xFF)), file.path(dir, paste0(doc_id, ".sync"))) expect_warning( autosync:::load_sync_states(state, storage_id, "clientC"), "Failed to load sync state" ) })