tests/testthat/tests.R

# This file is part of 'writer' R package
library("testthat")

## append ----------------------------------------------------------------------
# Test for appending data to an existing table with matching schema
test_that("Append data to existing table", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  DBI::dbWriteTable(con, "new_table", df, overwrite = TRUE)

  append_df = data.frame(id = 4:5, name = c("Dave", "Eve"))
  write_table(append_df, "new_table", mode = "append", con = con)

  expect_equal(dplyr::tbl(con, "new_table") |> dplyr::collect(),
               rbind(df, append_df),
               ignore_attr = TRUE
               )
})

# Test for appending data to a non-existing table with matching schema
test_that("Append data to non-existing table", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  # Data for appending
  append_df = data.frame(id = 4:5, name = c("Dave", "Eve"))

  # Write the data
  expect_error(write_table(append_df, "new_table", mode = "append", con = con))
})

# Test for appending data with mismatched schema (throws an error)
test_that("Append data with mismatched schema", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))
  df = data.frame(id = 1:3,
                  name = c("Alice", "Bob", "Charlie"),
                  age = c(20, 25, 30)
                  )
  DBI::dbWriteTable(con, "new_table", df, overwrite = TRUE)

  # Data for appending with mismatched schema
  append_df = data.frame(id = 4:5, name = c("Dave", "Eve"))
})

## create ----------------------------------------------------------------------

test_that("create new table", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  write_table(df, "new_table", mode = "create", con = con)

  expect_equal(df,
               dplyr::tbl(con, "new_table") |>
                 dplyr::collect() |>
                 as.data.frame(),
               ignore_attr = FALSE
               )
})

test_that("create fails with existing table (append mode)", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  write_table(df, "new_table", mode = "create", con = con)

  # Expected error message
  expect_error(write_table(df, "new_table", mode = "create", con = con))
})

## insert ----------------------------------------------------------------------
test_that("should not insert into non existing table", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  expect_error(write_table(df, "new_table", mode = "insert", con = con))
})

test_that("insert without conflicts", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  DBI::dbWriteTable(con, "new_table", df, overwrite = TRUE)

  insert_df = data.frame(id = 3:4, name = c("Dave", "Eve"))

  insert_df |>
    write_table("new_table",
                mode = "insert",
                con = con,
                by = "id",
                conflict = "ignore"
                )

  after_insert_df =
    dplyr::tbl(con, "new_table") |>
    dplyr::collect() |>
    as.data.frame()

  expected_df = dplyr::rows_insert(df, insert_df,
                                   by = "id",
                                   conflict = "ignore"
                                   )
  expect_equal(after_insert_df, expected_df)
})

test_that("insert handles conflicts (ignore)", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  DBI::dbWriteTable(con, "new_table", df, overwrite = TRUE)

  insert_df = data.frame(id = 3, name = c("Dave"))

  insert_df |>
    write_table("new_table",
                mode = "insert",
                con = con,
                by = "id",
                conflict = "ignore"
                )

  after_insert_df =
    dplyr::tbl(con, "new_table") |>
    dplyr::collect() |>
    as.data.frame()

  expect_equal(after_insert_df, df)
})

## update ----------------------------------------------------------------------
test_that("update", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  write_table(df, "new_table", mode = "create", con = con)

  # Update existing rows
  update_df = data.frame(id = c(1, 3), name = c("Alicia", "Charles"))
  write_table(update_df,
              "new_table",
              mode = "update",
              con = con,
              unmatched = "ignore"
              )

  updated_df = dplyr::tbl(con, "new_table") |>
    dplyr::collect()

  expect_equal(updated_df$name[updated_df$id == 1], "Alicia")
  expect_equal(updated_df$name[updated_df$id == 3], "Charles")

  # Update with unmatched rows ignored
  DBI::dbRemoveTable(con, "new_table")
  write_table(df, "new_table", mode = "create", con = con)

  update_df_unmatched = data.frame(id = c(1, 4), name = c("Alicia", "David"))
  write_table(update_df_unmatched,
              "new_table",
              mode = "update",
              con = con,
              unmatched = "ignore"
              )
  updated_df =
    dplyr::tbl(con, "new_table") |>
    dplyr::collect()

  expect_equal(updated_df$name[updated_df$id == 1], "Alicia")
  expect_false("David" %in% updated_df$name)

  # Update from tbl_lazy
  DBI::dbRemoveTable(con, "new_table")
  write_table(df, "new_table", mode = "create", con = con)

  update_tbl_name =
    df |>
    dplyr::filter(id %in% c(1,3)) |>
    dplyr::mutate(name = c("Alicia", "Charles")) |>
    write_table("update_tbl", mode = "create", con = con)

  update_tbl = dplyr::tbl(con, update_tbl_name)

  write_table(update_tbl,
              "new_table",
              mode = "update",
              con = con,
              unmatched = "ignore"
              )

  updated_df =
    dplyr::tbl(con, "new_table") |>
    dplyr::collect()
  expect_equal(updated_df$name[updated_df$id == 1], "Alicia")
  expect_equal(updated_df$name[updated_df$id == 3], "Charles")

  # Update with no matches
  update_df_no_match = data.frame(id = c(4, 5), name = c("David", "Eve"))
  write_table(update_df_no_match,
              "new_table",
              mode = "update",
              con = con,
              unmatched = "ignore"
              )
  updated_df =
    dplyr::tbl(con, "new_table") |>
    dplyr::collect()
  expect_equal(nrow(updated_df), 3) # No rows should be updated

  # Update with empty data frame
  empty_df = data.frame(id = integer(), name = character())
  write_table(empty_df,
              "new_table",
              mode = "update",
              con = con,
              unmatched = "ignore"
              )
  updated_df =
    dplyr::tbl(con, "new_table") |>
    dplyr::collect()
  expect_equal(nrow(updated_df), 3) # No rows should be updated
})

## upsert ----------------------------------------------------------------------
# upsert is know to have a issue in dbplyr
test_that("upsert", {
  # con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  # on.exit(DBI::dbDisconnect(con))
  #
  # # with
  # df_initial = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  # DBI::dbWriteTable(con, "test_table", df_initial, overwrite = TRUE)
  #
  # # Upsert with new and existing IDs
  # df_upsert = data.frame(id = c(2, 4), name = c("Bobby", "David"))
  # write_table(df_upsert, "test_table", mode = "upsert", con = con, by = "id")
  #
  # expect_equal(
  #   dplyr::tbl(con, "test_table") |>
  #     dplyr::arrange(id) |>
  #     dplyr::collect() |>
  #     as.data.frame(),
  #   data.frame(id = 1:4, name = c("Alice", "Bobby", "Charlie", "David"))
  #   )
  #
  # # with tbl
  # DBI::dbRemoveTable(con, "test_table")
  # write_table(df_upsert, "upsert_table", mode = "create", con = con)
  #
  # write_table(dplyr::tbl(con, "upsert_table"),
  #             "test_table",
  #             mode = "upsert",
  #             by = "id"
  #             )
  #
  # expect_equal(
  #   dplyr::tbl(con, "test_table") |>
  #     dplyr::arrange(id) |>
  #     dplyr::collect() |>
  #     as.data.frame(),
  #   data.frame(id = 1:4, name = c("Alice", "Bobby", "Charlie", "David"))
  #   )
})

## patch -----------------------------------------------------------------------
test_that("patch", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  # df
  df = data.frame(id = 1:3,
                  name = c("Alice", "Bob", "Charlie"),
                  value = c(10, 20, 30)
                  )
  DBI::dbWriteTable(con, "new_table", df)

  df_patch = data.frame(id = c(1, 2),
                        name = c("alice", NA),
                        value = c(NA, 25)
                        )
  DBI::dbWriteTable(con, "patch_table", df_patch)


  write_table(df,
              "patch_table",
              mode = "patch",
              con = con,
              by = "id",
              unmatched = "ignore"
              )
  expected_df = data.frame(id = 1:2,
                           name = c("alice", "Bob"),
                           value = c(10, 25)
                           )

  expect_equal(
    dplyr::tbl(con, "patch_table") |>
    dplyr::collect() |>
    as.data.frame(),
    expected_df
  )

  # tbl
  DBI::dbRemoveTable(con, "patch_table")
  DBI::dbWriteTable(con, "patch_table", df_patch)

  write_table(dplyr::tbl(con, "new_table"),
              "patch_table",
              mode = "patch",
              con = con,
              by = "id",
              unmatched = "ignore"
              )
  expect_equal(
    dplyr::tbl(con, "patch_table") |>
    dplyr::collect() |>
    as.data.frame(),
    expected_df
  )

})

## delete ----------------------------------------------------------------------
test_that("delete mode works correctly", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  DBI::dbWriteTable(con, "test_table", df, overwrite = TRUE)

  # df
  delete_df = data.frame(id = c(2, 3))
  write_table(delete_df,
              "test_table",
              mode = "delete",
              con = con,
              unmatched = "ignore"
              )

  # Check the result
  result_df =
    dplyr::tbl(con, "test_table") |>
    dplyr::collect() |>
    as.data.frame()

  expected_df = data.frame(id = 1, name = "Alice")
  expect_equal(result_df, expected_df)

  # tbl
  DBI::dbWriteTable(con, "delete_table", delete_df)
  write_table(dplyr::tbl(con, "delete_table"),
              "test_table",
              mode = "delete",
              con = con,
              unmatched = "ignore"
              )

  # Check the result
  result_df =
    dplyr::tbl(con, "test_table") |>
    dplyr::collect() |>
    as.data.frame()

  expected_df = data.frame(id = 1, name = "Alice")
  expect_equal(result_df, expected_df)
})

## overwrite -------------------------------------------------------------------
test_that("overwrite", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  df = data.frame(id = 1:3, name = c("Alice", "Bob", "Charlie"))
  DBI::dbWriteTable(con, "test_table", df)

  # df
  overwrite_df = data.frame(id = c(2, 6), name = c("Bobby", "Frank"))
  write_table(overwrite_df,
              "test_table",
              mode = "overwrite",
              con = con
              )

  expect_equal(dplyr::tbl(con, "test_table") |>
                 dplyr::collect() |>
                 as.data.frame(),
               overwrite_df
               )

  # tbl
  DBI::dbWriteTable(con, "test_table", df, overwrite = TRUE)
  DBI::dbWriteTable(con, "or_table", overwrite_df)

  write_table(dplyr::tbl(con, "or_table"),
              "test_table",
              mode = "overwrite",
              con = con
              )
  expect_equal(dplyr::tbl(con, "test_table") |>
                 dplyr::collect() |>
                 as.data.frame(),
               overwrite_df
               )


  # append fails when table does not exist
  expect_error(write_table(overwrite_df,
                           "non_existent_table",
                           mode = "overwrite",
                           con = con
                           )
               )
})

## overwrite_schema ------------------------------------------------------------
test_that("overwrite_schema", {
  con = DBI::dbConnect(RSQLite::SQLite(), ":memory:")
  on.exit(DBI::dbDisconnect(con))

  # df with different schema
  df = data.frame(id = 1:3,
                  name = c("Alice", "Bob", "Charlie")
                  )
  DBI::dbWriteTable(con, "test_table", df)

  os_df = data.frame(id = 4:5,
                     name = c("Dave", "Eve"),
                     age = c(25, 30)
                     )
  write_table(os_df,
              "test_table",
              mode = "overwrite_schema",
              con = con
              )

  expect_true(DBI::dbExistsTable(con, "test_table"))
  expect_equal(DBI::dbGetQuery(con, "SELECT COUNT(*) as c FROM test_table")$c,
               2
               )
  expect_equal(colnames(dplyr::tbl(con, "test_table")),
               c("id", "name", "age")
               )
  expect_equal(DBI::dbGetQuery(con, "SELECT * FROM test_table"),
               os_df,
               ignore_attr = TRUE
               )

  # tbl with diff schema
  DBI::dbWriteTable(con, "test_table", df, overwrite = TRUE)

  DBI::dbWriteTable(con, "new_table", os_df)
  write_table(dplyr::tbl(con, "new_table"),
              "test_table",
              mode = "overwrite_schema",
              con = con
              )

  expect_true(DBI::dbExistsTable(con, "test_table"))
  expect_equal(DBI::dbGetQuery(con, "SELECT COUNT(*) as c FROM test_table")$c,
               2
               )
  expect_equal(colnames(dplyr::tbl(con, "test_table")),
               c("id", "name", "age")
               )
  expect_equal(DBI::dbGetQuery(con, "SELECT * FROM test_table"),
               os_df,
               ignore_attr = TRUE
               )

  # write empty data with a diff schema

  # df
  DBI::dbWriteTable(con, "test_table", head(df, 0), overwrite = TRUE)

  os_df = data.frame(id = 4:5,
                     name = c("Dave", "Eve"),
                     age = c(25, 30)
                     )
  write_table(head(os_df, 0),
              "test_table",
              mode = "overwrite_schema",
              con = con
              )

  expect_true(DBI::dbExistsTable(con, "test_table"))
  expect_equal(DBI::dbGetQuery(con, "SELECT COUNT(*) as c FROM test_table")$c,
               0
               )
  expect_equal(colnames(dplyr::tbl(con, "test_table")),
               c("id", "name", "age")
               )
  expect_equal(DBI::dbGetQuery(con, "SELECT * FROM test_table"),
               head(os_df, 0),
               ignore_attr = TRUE
               )

  # tbl with diff schema
  DBI::dbWriteTable(con, "test_table", df, overwrite = TRUE)

  DBI::dbWriteTable(con, "new_table", head(os_df, 0), overwrite = TRUE)
  write_table(dplyr::tbl(con, "new_table"),
              "test_table",
              mode = "overwrite_schema",
              con = con
              )

  expect_true(DBI::dbExistsTable(con, "test_table"))
  expect_equal(DBI::dbGetQuery(con, "SELECT COUNT(*) as c FROM test_table")$c,
               0
               )
  expect_equal(colnames(dplyr::tbl(con, "test_table")),
               c("id", "name", "age")
               )
  expect_equal(DBI::dbGetQuery(con, "SELECT * FROM test_table"),
               head(os_df, 0),
               ignore_attr = TRUE
               )
})

Try the writer package in your browser

Any scripts or data that you put into this service are public.

writer documentation built on April 4, 2025, 5:08 a.m.