Nothing
# skip tests on CRAN
skip_if(Sys.getenv("TEST_ONE") != "")
testthat::skip_on_cran()
testthat::skip_if_not_installed("duckdb")
skip_if_not_installed("sf")
# 0. Test ddbs_temp_conn helper ----
test_that("ddbs_temp_conn creates valid auto-closing connection", {
# Test that connection is valid
conn <- ddbs_temp_conn()
expect_true(DBI::dbIsValid(conn))
# Test that connection works
result <- DBI::dbGetQuery(conn, "SELECT 1 AS test")
expect_equal(result$test, 1)
})
test_that("ddbs_temp_conn auto-closes on function exit", {
# Create a helper function that uses ddbs_temp_conn
get_conn_status <- function() {
conn <- ddbs_temp_conn()
# Return the connection object so we can check it after func exits
conn
}
# Call the function - connection should be closed after it returns
returned_conn <- get_conn_status()
# Connection should be invalid (closed) after function returned
expect_false(DBI::dbIsValid(returned_conn))
})
# create duckdb connection
conn_test <- duckspatial::ddbs_create_conn()
# 1. Basic functionality with sf objects ----
test_that("ddbs_write_table writes an sf object to a new table", {
# Write the points_sf data to a new table
table_name <- "test_points_write"
expect_true(ddbs_write_table(conn_test, points_sf, table_name, overwrite = TRUE))
# Verify the table exists
all_tables <- DBI::dbListTables(conn_test)
expect_true(table_name %in% all_tables)
# Verify the content
result <- ddbs_read_table(conn_test, table_name)
expect_s3_class(result, "sf")
expect_equal(nrow(result), nrow(points_sf))
expect_equal(sf::st_crs(result), sf::st_crs(points_sf))
})
test_that("ddbs_write_table respects the overwrite argument", {
table_name <- "test_overwrite"
# Write initial data
ddbs_write_table(conn_test, points_sf, table_name, overwrite = TRUE)
# Expect an error when trying to overwrite without permission
expect_error(
ddbs_write_table(conn_test, points_sf, table_name, overwrite = FALSE),
"already present"
)
# Overwrite with new data (countries_sf)
expect_true(ddbs_write_table(conn_test, countries_sf, table_name, overwrite = TRUE))
# Verify the new data is in the table
result <- ddbs_read_table(conn_test, table_name)
expect_equal(nrow(result), nrow(countries_sf))
})
test_that("ddbs_write_table handles different geometry types", {
# Create some test data with different geometry types
line <- sf::st_as_sfc("LINESTRING(0 0, 1 1)") |>
sf::st_sf(id = 1, geom = _)
polygon <- sf::st_as_sfc("POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))") |>
sf::st_sf(id = 1, geom = _)
# Write LINESTRING
table_line <- "test_line"
ddbs_write_table(conn_test, line, table_line, overwrite = TRUE)
result_line <- ddbs_read_table(conn_test, table_line)
expect_s3_class(result_line, "sf")
expect_equal(sf::st_geometry_type(result_line) |> as.character(), "LINESTRING")
# Write POLYGON
table_polygon <- "test_polygon"
ddbs_write_table(conn_test, polygon, table_polygon, overwrite = TRUE)
result_polygon <- ddbs_read_table(conn_test, table_polygon)
expect_s3_class(result_polygon, "sf")
expect_equal(sf::st_geometry_type(result_polygon) |> as.character(), "POLYGON")
})
test_that("ddbs_write_table stores CRS information correctly", {
table_name <- "test_crs_storage"
ddbs_write_table(conn_test, points_sf, table_name, overwrite = TRUE)
# Query the crs
crs_info <- DBI::dbGetQuery(
conn_test,
glue::glue("SELECT ST_CRS(geometry) FROM {table_name} LIMIT 1")
) |> as.character()
expect_equal(crs_info, "EPSG:4326")
})
# 2. Writing from file paths ----
test_that("ddbs_write_table can write from a .geojson file path", {
file_path <- system.file("spatial/countries.geojson", package = "duckspatial")
table_name <- "countries_from_file_write"
expect_true(ddbs_write_table(conn_test, file_path, table_name, overwrite = TRUE))
# Verify table exists and has content
all_tables <- DBI::dbListTables(conn_test)
expect_true(table_name %in% all_tables)
count <- DBI::dbGetQuery(conn_test, glue::glue("SELECT COUNT(*) FROM {table_name}"))
expect_gt(count[[1]], 0)
# Verify CRS is stored
crs_info <- DBI::dbGetQuery(
conn_test,
glue::glue("SELECT ST_CRS(geom) FROM {table_name} LIMIT 1")
) |> as.character()
expect_true(!is.na(crs_info) && nchar(crs_info) > 0)
})
# 3. Edge cases and errors ----
test_that("ddbs_write_table handles sf objects with no CRS", {
points_no_crs <- points_sf
sf::st_crs(points_no_crs) <- NA
table_name <- "test_no_crs"
ddbs_write_table(conn_test, points_no_crs, table_name, overwrite = TRUE)
# Check that the crs_duckspatial column does NOT exist
# Because we skip creating it when CRS is missing
crs <- DBI::dbGetQuery(
conn_test,
"SELECT ST_CRS(geometry) as crs FROM test_no_crs LIMIT 1;"
)$crs
expect_true(is.na(crs))
})
test_that("ddbs_write_table respects temp_view = TRUE", {
table_name <- "test_temp_view_write"
# Write with temp_view = TRUE
expect_true(ddbs_write_table(conn_test, points_sf, table_name, temp_view = TRUE, overwrite = TRUE))
# The requested name should be queryable through the typed temp view
all_tables <- DBI::dbListTables(conn_test)
expect_true(table_name %in% all_tables)
# The raw Arrow view is hidden behind the typed SQL view
arrow_views <- duckdb::duckdb_list_arrow(conn_test)
expect_true(paste0("__raw_", table_name) %in% arrow_views)
# Should be readable
result <- ddbs_read_table(conn_test, table_name)
expect_s3_class(result, "sf")
expect_equal(nrow(result), nrow(points_sf))
})
# Disconnect
duckdb::dbDisconnect(conn_test)
# 4. New functionality: duckspatial_df and existing CRS columns ----
test_that("ddbs_write_table can write a duckspatial_df object", {
conn_new <- ddbs_temp_conn()
# Create a duckspatial_df
ddbs_write_table(conn_new, points_sf, "points_source", overwrite = TRUE)
df_lazy <- ddbs_read_table(conn_new, "points_source") |>
as_duckspatial_df()
table_name <- "points_from_lazy"
# This should now work (previously failed with "Expected string vector of length 1")
expect_true(ddbs_write_table(conn_new, df_lazy, table_name, overwrite = TRUE))
# Verify result
result <- ddbs_read_table(conn_new, table_name)
expect_equal(nrow(result), nrow(points_sf))
expect_equal(sf::st_crs(result), sf::st_crs(points_sf))
})
test_that("ddbs_write_table throws error for unsupported input", {
conn_new <- ddbs_temp_conn()
expect_error(
ddbs_write_table(conn_new, list(a = 1), "bad_input"),
"must be an .*sf.* object"
)
})
test_that("ddbs_write_table with temp_view=TRUE works for duckspatial_df", {
conn_new <- ddbs_temp_conn()
# Create a duckspatial_df first
ddbs_write_table(conn_new, points_sf, "points_src", overwrite = TRUE)
df_lazy <- ddbs_read_table(conn_new, "points_src") |>
as_duckspatial_df()
view_name <- "points_lazy_view"
# This should work with temp_view = TRUE
expect_true(ddbs_write_table(conn_new, df_lazy, view_name, temp_view = TRUE, overwrite = TRUE))
# Verify the typed view was created and backed by a hidden raw Arrow view
all_tables <- DBI::dbListTables(conn_new)
expect_true(view_name %in% all_tables)
arrow_views <- duckdb::duckdb_list_arrow(conn_new)
expect_true(paste0("__raw_", view_name) %in% arrow_views)
# Should be queryable
result <- ddbs_read_table(conn_new, view_name)
expect_equal(nrow(result), nrow(points_sf))
})
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.