# =========================================================================== # buildConceptSetQuery — input validation # =========================================================================== test_that("buildConceptSetQuery errors on empty vocabularyDatabaseSchema", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)) )) expect_error( buildConceptSetQuery(expr, vocabularyDatabaseSchema = ""), "vocabularyDatabaseSchema.*must not be NA or empty" ) }) test_that("buildConceptSetQuery errors on NA vocabularyDatabaseSchema", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)) )) expect_error( buildConceptSetQuery(expr, vocabularyDatabaseSchema = NA_character_), "vocabularyDatabaseSchema.*must not be NA or empty" ) }) test_that("buildConceptSetQuery errors on non-string vocabularyDatabaseSchema", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)) )) expect_error( buildConceptSetQuery(expr, vocabularyDatabaseSchema = c("a", "b")), "vocabularyDatabaseSchema.*single character string" ) }) test_that("buildConceptSetQuery errors on non-list, non-JSON input", { expect_error( buildConceptSetQuery(42), "conceptSetExpression.*must be a list" ) }) test_that("buildConceptSetQuery errors on invalid JSON", { expect_error( buildConceptSetQuery("{not valid json!!!}"), "not valid JSON" ) }) test_that("buildConceptSetQuery errors when items missing", { expect_error( buildConceptSetQuery(list()), "must contain an `items` element" ) }) test_that("buildConceptSetQuery errors when items not a list", { expect_error( buildConceptSetQuery(list(items = "not_a_list")), "items.*must be a list" ) }) test_that("buildConceptSetQuery errors on item without concept", { expr <- list(items = list( list(includeDescendants = TRUE) )) expect_error( buildConceptSetQuery(expr), "must contain a `concept` element" ) }) test_that("buildConceptSetQuery errors on missing CONCEPT_ID", { expr <- list(items = list( list(concept = list()) )) expect_error( buildConceptSetQuery(expr), "must contain a `CONCEPT_ID`" ) }) test_that("buildConceptSetQuery errors on non-numeric CONCEPT_ID", { expr <- list(items = list( list(concept = list(CONCEPT_ID = "abc")) )) expect_error( buildConceptSetQuery(expr), "CONCEPT_ID.*must be a single non-NA numeric" ) }) test_that("buildConceptSetQuery errors on non-integer CONCEPT_ID", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 1.5)) )) expect_error( buildConceptSetQuery(expr), "CONCEPT_ID.*whole number" ) }) test_that("buildConceptSetQuery errors on NA CONCEPT_ID", { expr <- list(items = list( list(concept = list(CONCEPT_ID = NA_real_)) )) expect_error( buildConceptSetQuery(expr), "CONCEPT_ID.*non-NA" ) }) test_that("buildConceptSetQuery errors on non-logical flag", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100), includeDescendants = "yes") )) expect_error( buildConceptSetQuery(expr), "includeDescendants.*must be a single logical" ) }) test_that("buildConceptSetQuery warns on NA flag", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100), includeDescendants = NA) )) expect_warning( buildConceptSetQuery(expr), "includeDescendants.*is NA" ) }) # =========================================================================== # buildConceptSetQuery — SQL generation # =========================================================================== test_that("buildConceptSetQuery returns empty string for empty items", { result <- buildConceptSetQuery(list(items = list())) expect_equal(result, "") }) test_that("buildConceptSetQuery generates SQL for plain concept", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 201820)) )) sql <- buildConceptSetQuery( expr, conceptSetName = "test_cs", vocabularyDatabaseSchema = "cdm_v5" ) expect_true(nchar(sql) > 0) expect_true(grepl("201820", sql)) expect_true(grepl("test_cs", sql)) expect_true(grepl("cdm_v5", sql)) expect_true(grepl("concept_id", sql, ignore.case = TRUE)) }) test_that("buildConceptSetQuery generates SQL for includeDescendants", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 201820), includeDescendants = TRUE) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("CONCEPT_ANCESTOR", sql, ignore.case = TRUE)) expect_true(grepl("ancestor_concept_id", sql, ignore.case = TRUE)) }) test_that("buildConceptSetQuery generates SQL for includeMapped", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100), includeMapped = TRUE) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("CONCEPT_RELATIONSHIP", sql, ignore.case = TRUE)) expect_true(grepl("Maps to", sql)) }) test_that("buildConceptSetQuery generates SQL for descendants + mapped", { expr <- list(items = list( list( concept = list(CONCEPT_ID = 100), includeDescendants = TRUE, includeMapped = TRUE ) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("CONCEPT_ANCESTOR", sql, ignore.case = TRUE)) expect_true(grepl("CONCEPT_RELATIONSHIP", sql, ignore.case = TRUE)) }) test_that("buildConceptSetQuery handles exclusion with anti-join", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 201820), includeDescendants = TRUE), list(concept = list(CONCEPT_ID = 999), isExcluded = TRUE) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("LEFT JOIN", sql, ignore.case = TRUE)) expect_true(grepl("IS NULL", sql, ignore.case = TRUE)) expect_true(grepl("999", sql)) }) test_that("buildConceptSetQuery returns empty when all items excluded", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100), isExcluded = TRUE) )) result <- buildConceptSetQuery(expr) expect_equal(result, "") }) test_that("buildConceptSetQuery parses JSON input", { json <- '{"items":[{"concept":{"CONCEPT_ID":316866},"includeDescendants":true}]}' sql <- buildConceptSetQuery(json, conceptSetName = "htn", vocabularyDatabaseSchema = "vocab") expect_true(nchar(sql) > 0) expect_true(grepl("316866", sql)) expect_true(grepl("htn", sql)) }) test_that("buildConceptSetQuery handles multiple plain concepts", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)), list(concept = list(CONCEPT_ID = 200)), list(concept = list(CONCEPT_ID = 300)) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("100", sql)) expect_true(grepl("200", sql)) expect_true(grepl("300", sql)) }) test_that("buildConceptSetQuery uses default conceptSetName", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("plug", sql)) }) test_that("buildConceptSetQuery uses default vocabularyDatabaseSchema token", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)) )) sql <- buildConceptSetQuery(expr) expect_true(grepl("vocabulary_database_schema", sql)) }) # =========================================================================== # buildConceptSetQueries — batch # =========================================================================== test_that("buildConceptSetQueries returns named list", { csList <- list( diabetes = list(items = list( list(concept = list(CONCEPT_ID = 201820), includeDescendants = TRUE) )), hypertension = list(items = list( list(concept = list(CONCEPT_ID = 316866)) )) ) result <- buildConceptSetQueries(csList, vocabularyDatabaseSchema = "vocab") expect_true(is.list(result)) expect_named(result, c("diabetes", "hypertension")) expect_true(all(vapply(result, is.character, logical(1L)))) expect_true(nchar(result$diabetes) > 0) expect_true(nchar(result$hypertension) > 0) }) test_that("buildConceptSetQueries uses custom names", { csList <- list( a = list(items = list(list(concept = list(CONCEPT_ID = 1L)))), b = list(items = list(list(concept = list(CONCEPT_ID = 2L)))) ) result <- buildConceptSetQueries( csList, conceptSetNames = c("custom_a", "custom_b"), vocabularyDatabaseSchema = "vocab" ) expect_true(grepl("custom_a", result[[1]])) expect_true(grepl("custom_b", result[[2]])) }) test_that("buildConceptSetQueries handles empty items gracefully", { csList <- list( filled = list(items = list( list(concept = list(CONCEPT_ID = 100)) )), empty = list(items = list()) ) result <- buildConceptSetQueries(csList, vocabularyDatabaseSchema = "vocab") expect_true(nchar(result$filled) > 0) expect_equal(result$empty, "") }) # =========================================================================== # createConceptSetTempTable — input validation # =========================================================================== test_that("createConceptSetTempTable errors on NULL connection", { expect_error( createConceptSetTempTable( connection = NULL, csQueries = list(a = "SELECT 1"), vocabularyDatabaseSchema = "vocab" ), "connection.*must be a DatabaseConnector" ) }) test_that("createConceptSetTempTable errors on non-DatabaseConnector connection", { expect_error( createConceptSetTempTable( connection = "not_a_connection", csQueries = list(a = "SELECT 1"), vocabularyDatabaseSchema = "vocab" ), "connection.*must be a DatabaseConnector" ) }) test_that("createConceptSetTempTable errors on empty vocabularyDatabaseSchema", { fake_conn <- structure(list(), class = "DatabaseConnectorConnection") expect_error( createConceptSetTempTable( connection = fake_conn, csQueries = list(a = "SELECT 1"), vocabularyDatabaseSchema = "" ), "vocabularyDatabaseSchema.*non-empty" ) }) test_that("createConceptSetTempTable errors on NA vocabularyDatabaseSchema", { fake_conn <- structure(list(), class = "DatabaseConnectorConnection") expect_error( createConceptSetTempTable( connection = fake_conn, csQueries = list(a = "SELECT 1"), vocabularyDatabaseSchema = NA_character_ ), "vocabularyDatabaseSchema.*non-empty" ) }) test_that("createConceptSetTempTable errors on empty tempTableName", { fake_conn <- structure(list(), class = "DatabaseConnectorConnection") expect_error( createConceptSetTempTable( connection = fake_conn, csQueries = list(a = "SELECT 1"), vocabularyDatabaseSchema = "vocab", tempTableName = "" ), "tempTableName.*non-empty" ) }) test_that("createConceptSetTempTable errors on NA tempTableName", { fake_conn <- structure(list(), class = "DatabaseConnectorConnection") expect_error( createConceptSetTempTable( connection = fake_conn, csQueries = list(a = "SELECT 1"), vocabularyDatabaseSchema = "vocab", tempTableName = NA_character_ ), "tempTableName.*non-empty" ) }) # =========================================================================== # buildConceptSetQuery — additional edge cases # =========================================================================== test_that("buildConceptSetQuery errors on item that is not a list", { expr <- list(items = list("not_a_list")) expect_error( buildConceptSetQuery(expr), "must be a list" ) }) test_that("buildConceptSetQuery errors on concept that is not a list", { expr <- list(items = list( list(concept = "not_a_list") )) expect_error( buildConceptSetQuery(expr), "concept must be a list" ) }) test_that("buildConceptSetQuery errors on character vector conceptSetExpression", { expect_error( buildConceptSetQuery(c("a", "b")), "single non-NA JSON string" ) }) test_that("buildConceptSetQuery errors on NA character conceptSetExpression", { expect_error( buildConceptSetQuery(NA_character_), "single non-NA JSON string" ) }) test_that("buildConceptSetQuery handles excluded items with descendants", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100), includeDescendants = TRUE), list(concept = list(CONCEPT_ID = 200), isExcluded = TRUE, includeDescendants = TRUE) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("LEFT JOIN", sql, ignore.case = TRUE)) expect_true(grepl("CONCEPT_ANCESTOR", sql, ignore.case = TRUE)) }) test_that("buildConceptSetQuery handles excluded items with mapped", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)), list(concept = list(CONCEPT_ID = 200), isExcluded = TRUE, includeMapped = TRUE) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("LEFT JOIN", sql, ignore.case = TRUE)) expect_true(grepl("Maps to", sql)) }) test_that("buildConceptSetQuery handles excluded items with both flags", { expr <- list(items = list( list(concept = list(CONCEPT_ID = 100)), list(concept = list(CONCEPT_ID = 200), isExcluded = TRUE, includeDescendants = TRUE, includeMapped = TRUE) )) sql <- buildConceptSetQuery(expr, vocabularyDatabaseSchema = "vocab") expect_true(grepl("LEFT JOIN", sql, ignore.case = TRUE)) })