Nothing
# Tests for Convenience Functions
# Path-based Access Tests -------------------------------------------------
test_that("am_get_path navigates nested structures", {
doc <- am_create()
am_put(
doc,
AM_ROOT,
"user",
list(
name = "Alice",
address = list(
city = "NYC",
zip = 10001L
)
)
)
# Navigate to nested values
expect_equal(am_get_path(doc, c("user", "name")), "Alice")
expect_equal(am_get_path(doc, c("user", "address", "city")), "NYC")
expect_equal(am_get_path(doc, c("user", "address", "zip")), 10001L)
})
test_that("am_get_path handles missing paths", {
doc <- am_create()
doc$user <- list(name = "Alice")
# Missing path returns NULL
expect_null(am_get_path(doc, c("user", "address", "city")))
expect_null(am_get_path(doc, c("nonexistent")))
})
test_that("am_get_path supports mixed string and numeric indices", {
doc <- am_create()
# Create structure with both maps and lists
am_put(
doc,
AM_ROOT,
"users",
list(
list(name = "Alice", age = 30L),
list(name = "Bob", age = 25L)
)
)
# Navigate: map key "users" -> list index 1 -> map key "name"
name <- am_get_path(doc, list("users", 1, "name"))
expect_equal(name, "Alice")
# Navigate to second user's age
age <- am_get_path(doc, list("users", 2, "age"))
expect_equal(age, 25L)
})
test_that("am_get_path validates inputs", {
doc <- am_create()
expect_error(am_get_path(doc, list()), "path cannot be empty")
expect_error(am_get_path(doc, NULL), "path must be")
expect_error(
am_get_path("not a doc", c("key")),
"must be an Automerge document"
)
})
test_that("am_put_path creates nested structures", {
doc <- am_create()
# Create nested structure with automatic intermediate objects
am_put_path(doc, c("user", "address", "city"), "Boston")
am_put_path(doc, c("user", "address", "zip"), 02101L)
am_put_path(doc, c("user", "name"), "Alice")
# Verify structure was created
expect_equal(am_get_path(doc, c("user", "name")), "Alice")
expect_equal(am_get_path(doc, c("user", "address", "city")), "Boston")
expect_equal(am_get_path(doc, c("user", "address", "zip")), 02101L)
})
test_that("am_put_path can disable intermediate creation", {
doc <- am_create()
# Should fail without create_intermediate
expect_error(
am_put_path(
doc,
c("user", "address", "city"),
"NYC",
create_intermediate = FALSE
),
"Path component at position 1 does not exist"
)
# Create parent first (must use am_map() to create a MAP, not a LIST)
doc$user <- am_map()
expect_error(
am_put_path(
doc,
c("user", "address", "city"),
"NYC",
create_intermediate = FALSE
),
"Path component at position 2 does not exist"
)
})
test_that("am_put_path validates inputs", {
doc <- am_create()
expect_error(am_put_path(doc, list(), "value"), "path cannot be empty")
expect_error(am_put_path(doc, NULL, "value"), "path must be")
expect_error(
am_put_path("not a doc", c("key"), "value"),
"must be an Automerge document"
)
})
test_that("am_put_path returns document invisibly", {
doc <- am_create()
result <- withVisible(am_put_path(doc, c("key"), "value"))
expect_false(result$visible)
expect_identical(result$value, doc)
})
test_that("am_delete_path removes nested keys", {
doc <- am_create()
am_put_path(doc, c("user", "address", "city"), "NYC")
am_put_path(doc, c("user", "name"), "Alice")
# Delete nested key
am_delete_path(doc, c("user", "address"))
# Address should be gone
expect_null(am_get_path(doc, c("user", "address")))
# But name should remain
expect_equal(am_get_path(doc, c("user", "name")), "Alice")
})
test_that("am_delete_path handles missing paths gracefully", {
doc <- am_create()
am_put(doc, AM_ROOT, "user", am_map(name = "Alice"))
# Should warn for missing intermediate path
expect_warning(am_delete_path(doc, c("user", "nonexistent", "key")))
# Deleting nonexistent root key should succeed without warning (single element path)
expect_no_condition(am_delete_path(doc, c("nonexistent")))
})
test_that("am_delete_path validates inputs", {
doc <- am_create()
expect_error(am_delete_path(doc, list()), "path cannot be empty")
expect_error(am_delete_path(doc, NULL), "path must be")
expect_error(
am_delete_path("not a doc", c("key")),
"must be an Automerge document"
)
})
test_that("am_delete_path returns document invisibly", {
doc <- am_create()
doc$key <- "value"
result <- withVisible(am_delete_path(doc, c("key")))
expect_false(result$visible)
expect_identical(result$value, doc)
})
test_that("path-based access works with deep nesting", {
doc <- am_create()
# Create 6-level deep structure
am_put_path(doc, c("a", "b", "c", "d", "e", "f"), "deep_value")
# Should be able to retrieve it
expect_equal(am_get_path(doc, c("a", "b", "c", "d", "e", "f")), "deep_value")
# Modify intermediate level
am_put_path(doc, c("a", "b", "c", "modified"), TRUE)
expect_true(am_get_path(doc, c("a", "b", "c", "modified")))
# Original deep value still accessible
expect_equal(am_get_path(doc, c("a", "b", "c", "d", "e", "f")), "deep_value")
})
# Conversion Functions Tests ----------------------------------------------
test_that("as_automerge converts simple lists", {
data <- list(name = "Alice", age = 30L, active = TRUE)
doc <- as_automerge(data)
expect_s3_class(doc, "am_doc")
expect_equal(doc[["name"]], "Alice")
expect_equal(doc[["age"]], 30L)
expect_equal(doc[["active"]], TRUE)
})
test_that("as_automerge converts nested lists", {
data <- list(
name = "Alice",
age = 30L,
address = list(
city = "NYC",
zip = 10001L
)
)
doc <- as_automerge(data)
expect_equal(doc[["name"]], "Alice")
expect_equal(am_get_path(doc, c("address", "city")), "NYC")
expect_equal(am_get_path(doc, c("address", "zip")), 10001L)
})
test_that("as_automerge converts deeply nested structures", {
data <- list(
company = list(
name = "Acme Corp",
office = list(
address = list(
street = "123 Main St",
city = "Boston"
)
)
)
)
doc <- as_automerge(data)
expect_equal(am_get_path(doc, c("company", "name")), "Acme Corp")
expect_equal(
am_get_path(doc, c("company", "office", "address", "city")),
"Boston"
)
})
test_that("as_automerge handles scalar values", {
# Single value
doc <- as_automerge(42L)
expect_equal(doc[["value"]], 42L)
# Single string
doc <- as_automerge("test")
expect_equal(doc[["value"]], "test")
})
test_that("as_automerge handles vectors", {
# Vector becomes a list
doc <- as_automerge(c(1, 2, 3))
values <- doc[["values"]]
expect_s3_class(values, "am_object")
# am_object is now the external pointer directly
expect_equal(am_length(doc, values), 3)
})
test_that("as_automerge can use existing document", {
doc <- am_create()
doc$existing <- "data"
# Add to existing document
as_automerge(list(new = "value"), doc = doc)
expect_equal(doc$existing, "data")
expect_equal(doc$new, "value")
})
test_that("as_automerge can specify actor_id", {
doc <- as_automerge(list(x = 1), actor_id = NULL)
expect_s3_class(doc, "am_doc")
# Verify actor ID was set (should be random bytes)
actor <- am_get_actor(doc)
expect_type(actor, "raw")
expect_true(length(actor) > 0)
})
test_that("as_automerge validates inputs", {
expect_error(
as_automerge(list(x = 1), doc = "not a doc"),
"must be an Automerge document"
)
})
test_that("from_automerge converts to R list", {
doc <- am_create()
doc$name <- "Alice"
doc$age <- 30L
doc$active <- TRUE
result <- from_automerge(doc)
expect_type(result, "list")
expect_equal(result$name, "Alice")
expect_equal(result$age, 30L)
expect_equal(result$active, TRUE)
})
test_that("from_automerge handles nested structures", {
doc <- am_create()
am_put(
doc,
AM_ROOT,
"user",
list(
name = "Bob",
address = list(city = "NYC", zip = 10001L)
)
)
result <- from_automerge(doc)
expect_type(result$user, "list")
expect_equal(result$user$name, "Bob")
expect_type(result$user$address, "list")
expect_equal(result$user$address$city, "NYC")
expect_equal(result$user$address$zip, 10001L)
})
test_that("from_automerge validates inputs", {
expect_error(from_automerge("not a doc"), "must be an Automerge document")
})
test_that("as_automerge and from_automerge are inverses", {
original <- list(
name = "Alice",
age = 30L,
scores = list(85L, 90L, 95L),
metadata = list(active = TRUE)
)
doc <- as_automerge(original)
result <- from_automerge(doc)
# Check top-level fields
expect_equal(result$name, original$name)
expect_equal(result$age, original$age)
expect_equal(result$metadata$active, original$metadata$active)
})
# Integration Tests -------------------------------------------------------
test_that("convenience functions work together", {
# Create document from R data
data <- list(
users = list(
list(name = "Alice", role = "admin"),
list(name = "Bob", role = "user")
),
config = list(
database = list(host = "localhost", port = 5432L)
)
)
doc <- as_automerge(data)
# Use path-based access to modify
am_put_path(doc, c("config", "database", "port"), 5433L)
# Must use list() for mixed string/numeric paths to avoid coercion
am_put_path(doc, list("users", 1, "active"), TRUE)
# Verify via path access
expect_equal(am_get_path(doc, c("config", "database", "port")), 5433L)
expect_true(am_get_path(doc, list("users", 1, "active")))
# Fork and modify
doc2 <- am_fork(doc)
am_put_path(doc2, c("config", "version"), "2.0")
# Merge back using am_merge
am_merge(doc, doc2)
# Convert back to R
result <- from_automerge(doc)
expect_equal(result$config$database$port, 5433L)
expect_equal(result$config$version, "2.0")
})
test_that("path-based functions work with save/load/merge", {
doc1 <- am_create()
am_put_path(doc1, c("user", "name"), "Alice")
am_put_path(doc1, c("user", "location", "city"), "NYC")
am_commit(doc1)
# Save and load
bytes <- am_save(doc1)
doc_loaded <- am_load(bytes)
expect_equal(am_get_path(doc_loaded, c("user", "name")), "Alice")
expect_equal(am_get_path(doc_loaded, c("user", "location", "city")), "NYC")
# Fork and modify different paths
doc2 <- am_fork(doc1)
am_put_path(doc2, c("user", "location", "state"), "NY")
am_commit(doc2)
# Merge
am_merge(doc1, doc2)
# Both paths should exist
expect_equal(am_get_path(doc1, c("user", "location", "city")), "NYC")
expect_equal(am_get_path(doc1, c("user", "location", "state")), "NY")
})
# Edge Cases ------------------------------------------------------------------
test_that("as_automerge handles data.frame", {
df <- data.frame(x = 1:3, y = letters[1:3], stringsAsFactors = FALSE)
result <- tryCatch(
as_automerge(df),
error = function(e) "error"
)
if (inherits(result, "am_doc")) {
expect_s3_class(result, "am_doc")
} else {
expect_equal(result, "error")
}
})
test_that("as_automerge handles matrix", {
mat <- matrix(1:9, nrow = 3)
result <- tryCatch(
as_automerge(mat),
error = function(e) "error"
)
if (inherits(result, "am_doc")) {
expect_s3_class(result, "am_doc")
} else {
expect_equal(result, "error")
}
})
test_that("as_automerge handles empty list", {
doc <- as_automerge(list())
expect_s3_class(doc, "am_doc")
expect_equal(am_length(doc, AM_ROOT), 0)
})
test_that("as_automerge handles list with NULL names", {
data <- list("a", "b", "c")
doc <- as_automerge(data)
expect_s3_class(doc, "am_doc")
})
test_that("as_automerge handles partially named list", {
data <- list(a = 1, "b", c = 3)
doc <- as_automerge(data)
expect_s3_class(doc, "am_doc")
})
test_that("as_automerge handles nested empty lists", {
data <- list(
outer = list(
inner = list()
)
)
doc <- as_automerge(data)
expect_s3_class(doc, "am_doc")
inner <- am_get_path(doc, c("outer", "inner"))
expect_s3_class(inner, "am_object")
# am_object is now the external pointer directly
expect_equal(am_length(doc, inner), 0)
})
test_that("as_automerge handles very large structures", {
large_list <- lapply(1:100, function(i) {
list(id = i, value = paste0("value", i))
})
doc <- as_automerge(list(data = large_list))
expect_s3_class(doc, "am_doc")
})
test_that("from_automerge handles empty document", {
doc <- am_create()
result <- from_automerge(doc)
expect_type(result, "list")
expect_length(result, 0)
})
test_that("from_automerge handles document with deleted keys", {
doc <- am_create()
am_put(doc, AM_ROOT, "key1", "value1")
am_put(doc, AM_ROOT, "key2", "value2")
am_delete(doc, AM_ROOT, "key1")
result <- from_automerge(doc)
expect_false("key1" %in% names(result))
expect_true("key2" %in% names(result))
expect_equal(result$key2, "value2")
})
test_that("from_automerge preserves POSIXct timestamps", {
timestamp <- Sys.time()
doc <- am_create()
am_put(doc, AM_ROOT, "time", timestamp)
result <- from_automerge(doc)
expect_s3_class(result$time, "POSIXct")
expect_equal(as.numeric(result$time), as.numeric(timestamp))
})
test_that("am_get_path with single element path", {
doc <- am_create()
am_put(doc, AM_ROOT, "key", "value")
expect_equal(am_get_path(doc, "key"), "value")
})
test_that("am_put_path overwrites existing values", {
doc <- am_create()
am_put_path(doc, c("key", "nested"), "original")
am_put_path(doc, c("key", "nested"), "updated")
expect_equal(am_get_path(doc, c("key", "nested")), "updated")
})
test_that("am_delete_path on intermediate path", {
doc <- am_create()
am_put_path(doc, c("a", "b", "c"), "value1")
am_put_path(doc, c("a", "b", "d"), "value2")
am_put_path(doc, c("a", "e"), "value3")
am_delete_path(doc, c("a", "b"))
expect_null(am_get_path(doc, c("a", "b")))
expect_null(am_get_path(doc, c("a", "b", "c")))
expect_equal(am_get_path(doc, c("a", "e")), "value3")
})
test_that("am_get_path handles numeric indices in lists", {
doc <- am_create()
doc$items <- am_list("first", "second", "third")
expect_equal(am_get_path(doc, list("items", 1)), "first")
expect_equal(am_get_path(doc, list("items", 2)), "second")
expect_equal(am_get_path(doc, list("items", 3)), "third")
})
test_that("am_get_path returns NULL for out-of-bounds list index", {
doc <- am_create()
doc$items <- am_list("first", "second")
expect_null(am_get_path(doc, list("items", 0)))
expect_null(am_get_path(doc, list("items", 99)))
})
test_that("am_put_path with list index extends list", {
doc <- am_create()
doc$items <- am_list()
am_put_path(doc, list("items", "end"), "first")
am_put_path(doc, list("items", "end"), "second")
expect_equal(am_get_path(doc, list("items", 1)), "first")
expect_equal(am_get_path(doc, list("items", 2)), "second")
})
test_that("round-trip conversion preserves structure", {
original <- list(
name = "Alice",
scores = list(85, 90, 95),
metadata = list(
created = TRUE,
tags = list("a", "b", "c")
)
)
doc <- as_automerge(original)
result <- from_automerge(doc)
expect_equal(result$name, original$name)
expect_equal(result$metadata$created, original$metadata$created)
})
test_that("as_automerge handles special numeric values carefully", {
doc <- tryCatch(
as_automerge(list(inf = Inf, neg_inf = -Inf, nan = NaN)),
error = function(e) "error"
)
expect_true(inherits(doc, "am_doc") || identical(doc, "error"))
})
test_that("as_automerge handles nested lists with mixed types", {
data <- list(
int = 1L,
dbl = 3.14,
str = "text",
bool = TRUE,
null = NULL,
nested = list(a = 1, b = "two")
)
doc <- as_automerge(data)
expect_s3_class(doc, "am_doc")
expect_equal(doc$int, 1L)
expect_equal(doc$str, "text")
expect_null(doc$null)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.