Nothing
# Recursive Conversion and Advanced Types (Phase 3)
test_that("Recursive conversion works for nested maps", {
doc <- am_create()
# Single-call nested structure
am_put(
doc,
AM_ROOT,
"user",
list(
name = "Alice",
age = 30L,
active = TRUE
)
)
user <- am_get(doc, AM_ROOT, "user")
expect_s3_class(user, "am_object")
expect_equal(am_get(doc, user, "name"), "Alice")
expect_equal(am_get(doc, user, "age"), 30L)
expect_equal(am_get(doc, user, "active"), TRUE)
})
test_that("Recursive conversion handles deep nesting", {
doc <- am_create()
# Multi-level nested structure (3 levels deep)
am_put(
doc,
AM_ROOT,
"company",
list(
name = "Acme Corp",
office = list(
location = "Boston",
address = list(
street = "123 Main St",
city = "Boston",
zip = 02101L
)
)
)
)
company <- am_get(doc, AM_ROOT, "company")
expect_equal(am_get(doc, company, "name"), "Acme Corp")
office <- am_get(doc, company, "office")
expect_equal(am_get(doc, office, "location"), "Boston")
address <- am_get(doc, office, "address")
expect_equal(am_get(doc, address, "street"), "123 Main St")
expect_equal(am_get(doc, address, "city"), "Boston")
expect_equal(am_get(doc, address, "zip"), 02101L)
})
test_that("Recursive conversion handles mixed maps and lists", {
doc <- am_create()
# Map containing lists
am_put(
doc,
AM_ROOT,
"data",
list(
tags = list("r", "automerge", "crdt"), # Unnamed = list
metadata = list(version = "1.0", author = "user") # Named = map
)
)
data <- am_get(doc, AM_ROOT, "data")
tags <- am_get(doc, data, "tags")
expect_equal(am_length(doc, tags), 3L)
expect_equal(am_get(doc, tags, 1), "r") # 1-based indexing
expect_equal(am_get(doc, tags, 2), "automerge")
expect_equal(am_get(doc, tags, 3), "crdt")
metadata <- am_get(doc, data, "metadata")
expect_equal(am_get(doc, metadata, "version"), "1.0")
expect_equal(am_get(doc, metadata, "author"), "user")
})
test_that("Explicit type constructors work", {
doc <- am_create()
# Explicit list type (empty)
am_put(doc, AM_ROOT, "items", am_list())
items <- am_get(doc, AM_ROOT, "items")
expect_s3_class(items, "am_object")
expect_equal(am_length(doc, items), 0L)
# Explicit list type (populated)
am_put(doc, AM_ROOT, "tags", am_list("a", "b", "c"))
tags <- am_get(doc, AM_ROOT, "tags")
expect_equal(am_length(doc, tags), 3L)
expect_equal(am_get(doc, tags, 1), "a")
# Explicit map type (empty)
am_put(doc, AM_ROOT, "config", am_map())
config <- am_get(doc, AM_ROOT, "config")
expect_s3_class(config, "am_object")
expect_equal(am_length(doc, config), 0L)
# Explicit map type (populated)
am_put(doc, AM_ROOT, "settings", am_map(key1 = "val1", key2 = "val2"))
settings <- am_get(doc, AM_ROOT, "settings")
expect_equal(am_get(doc, settings, "key1"), "val1")
expect_equal(am_get(doc, settings, "key2"), "val2")
})
test_that("POSIXct timestamps work", {
doc <- am_create()
# Store timestamp
now <- Sys.time()
am_put(doc, AM_ROOT, "created", now)
# Retrieve and verify (allowing for small rounding error)
retrieved <- am_get(doc, AM_ROOT, "created")
expect_s3_class(retrieved, "POSIXct")
expect_equal(as.numeric(retrieved), as.numeric(now), tolerance = 0.001)
})
test_that("am_counter type works", {
doc <- am_create()
# Store counter
am_put(doc, AM_ROOT, "score", am_counter(0))
# Retrieve and verify
score <- am_get(doc, AM_ROOT, "score")
expect_s3_class(score, "am_counter")
expect_equal(as.integer(score), 0L)
# Store counter with initial value
am_put(doc, AM_ROOT, "points", am_counter(100L))
points <- am_get(doc, AM_ROOT, "points")
expect_equal(as.integer(points), 100L)
})
test_that("am_text type creates text objects", {
doc <- am_create()
# Empty text
am_put(doc, AM_ROOT, "doc1", am_text())
text1 <- am_get(doc, AM_ROOT, "doc1")
expect_s3_class(text1, "am_object")
# Text object should be empty initially
expect_equal(am_length(doc, text1), 0L)
# Text with initial content
am_put(doc, AM_ROOT, "doc2", am_text("Hello, World!"))
text2 <- am_get(doc, AM_ROOT, "doc2")
expect_s3_class(text2, "am_object")
# Text object should have 13 characters
expect_equal(am_length(doc, text2), 13L)
})
test_that("text objects vs strings behave differently", {
doc <- am_create()
# Regular string (last-write-wins)
am_put(doc, AM_ROOT, "title", "String Value")
expect_type(am_get(doc, AM_ROOT, "title"), "character")
expect_equal(am_get(doc, AM_ROOT, "title"), "String Value")
# Text object (CRDT)
am_put(doc, AM_ROOT, "content", am_text("Text Object"))
text_obj <- am_get(doc, AM_ROOT, "content")
expect_s3_class(text_obj, "am_object")
# Length should be character count
expect_equal(am_length(doc, text_obj), 11L)
})
test_that("Recursive conversion handles NULL values", {
doc <- am_create()
am_put(
doc,
AM_ROOT,
"data",
list(
present = "value",
missing = NULL,
nested = list(
also_missing = NULL,
also_present = 42L
)
)
)
data <- am_get(doc, AM_ROOT, "data")
expect_equal(am_get(doc, data, "present"), "value")
expect_null(am_get(doc, data, "missing"))
nested <- am_get(doc, data, "nested")
expect_null(am_get(doc, nested, "also_missing"))
expect_equal(am_get(doc, nested, "also_present"), 42L)
})
test_that("Recursive conversion handles all primitive types", {
doc <- am_create()
raw_data <- as.raw(c(0x01, 0x02, 0x03))
am_put(
doc,
AM_ROOT,
"all_types",
list(
null_val = NULL,
bool_val = TRUE,
int_val = 42L,
double_val = 3.14159,
string_val = "hello",
raw_val = raw_data,
timestamp_val = Sys.time(),
counter_val = am_counter(10L)
)
)
obj <- am_get(doc, AM_ROOT, "all_types")
expect_null(am_get(doc, obj, "null_val"))
expect_equal(am_get(doc, obj, "bool_val"), TRUE)
expect_equal(am_get(doc, obj, "int_val"), 42L)
expect_equal(am_get(doc, obj, "double_val"), 3.14159, tolerance = 1e-6)
expect_equal(am_get(doc, obj, "string_val"), "hello")
expect_equal(am_get(doc, obj, "raw_val"), raw_data)
expect_s3_class(am_get(doc, obj, "timestamp_val"), "POSIXct")
expect_s3_class(am_get(doc, obj, "counter_val"), "am_counter")
})
test_that("Recursive conversion integrates with commit/save/load", {
doc1 <- am_create()
# Create complex nested structure
am_put(
doc1,
AM_ROOT,
"project",
list(
name = "MyProject",
version = "1.0.0",
metadata = list(
created = Sys.time(),
tags = list("important", "active"),
stats = list(
commits = am_counter(42L),
stars = am_counter(100L)
)
)
)
)
am_commit(doc1, "Added project data")
# Save and load
bytes <- am_save(doc1)
doc2 <- am_load(bytes)
# Verify structure is preserved
project <- am_get(doc2, AM_ROOT, "project")
expect_equal(am_get(doc2, project, "name"), "MyProject")
metadata <- am_get(doc2, project, "metadata")
expect_s3_class(am_get(doc2, metadata, "created"), "POSIXct")
tags <- am_get(doc2, metadata, "tags")
expect_equal(am_length(doc2, tags), 2L)
stats <- am_get(doc2, metadata, "stats")
commits <- am_get(doc2, stats, "commits")
expect_s3_class(commits, "am_counter")
expect_equal(as.integer(commits), 42L)
})
test_that("Very deep nesting (5+ levels) works", {
doc <- am_create()
# 6 levels deep
am_put(
doc,
AM_ROOT,
"level1",
list(
level2 = list(
level3 = list(
level4 = list(
level5 = list(
level6 = "deep value"
)
)
)
)
)
)
# Navigate down
l1 <- am_get(doc, AM_ROOT, "level1")
l2 <- am_get(doc, l1, "level2")
l3 <- am_get(doc, l2, "level3")
l4 <- am_get(doc, l3, "level4")
l5 <- am_get(doc, l4, "level5")
value <- am_get(doc, l5, "level6")
expect_equal(value, "deep value")
})
test_that("Empty nested structures work", {
doc <- am_create()
# Empty map in map
am_put(doc, AM_ROOT, "outer", list(inner = list()))
outer <- am_get(doc, AM_ROOT, "outer")
inner <- am_get(doc, outer, "inner")
expect_equal(am_length(doc, inner), 0L)
# Empty list in map
am_put(doc, AM_ROOT, "container", list(items = am_list()))
container <- am_get(doc, AM_ROOT, "container")
items <- am_get(doc, container, "items")
expect_equal(am_length(doc, items), 0L)
})
test_that("Extremely deep nesting (150 levels) works without stack overflow", {
doc <- am_create()
# Build a deeply nested structure programmatically (150 levels)
# This exceeds the old MAX_RECURSION_DEPTH=100 limit
depth <- 150
# Build nested list structure
build_nested <- function(n) {
if (n == 0) {
return("bottom value")
}
list(child = build_nested(n - 1))
}
nested_data <- build_nested(depth - 1)
# This should not crash - test that recursive conversion handles it
expect_error(
am_put(doc, AM_ROOT, "deep", nested_data),
NA # NA means we expect NO error
)
# Navigate down to verify the structure was created
current <- am_get(doc, AM_ROOT, "deep")
expect_s3_class(current, "am_object")
# Navigate several levels down (not all 150, that would be tedious)
for (i in 1:10) {
current <- am_get(doc, current, "child")
if (i < 10) {
expect_s3_class(current, "am_object")
}
}
# Verify we can commit and save without issues
expect_error(am_commit(doc, "Deep structure test"), NA)
expect_error(am_save(doc), NA)
})
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.