# Tests for buildConceptSetQuery(), buildConceptSetQueries(), collectCsFromCohort() # ============================================================ # Test helpers # ============================================================ make_item <- function(concept_id, include_descendants = FALSE, include_mapped = FALSE, is_excluded = FALSE) { list( concept = list(CONCEPT_ID = concept_id, DOMAIN_ID = "Condition"), includeDescendants = include_descendants, includeMapped = include_mapped, isExcluded = is_excluded ) } make_expr <- function(...) list(items = list(...)) # ============================================================ # buildConceptSetQuery — valid inputs # ============================================================ test_that("buildConceptSetQuery returns a character string", { expr <- make_expr(make_item(100L)) result <- buildConceptSetQuery(expr) expect_type(result, "character") expect_length(result, 1L) }) test_that("buildConceptSetQuery includes concept ids in SQL", { expr <- make_expr(make_item(100L), make_item(200L)) sql <- buildConceptSetQuery(expr) expect_match(sql, "100") expect_match(sql, "200") }) test_that("buildConceptSetQuery includes DISTINCT select", { sql <- buildConceptSetQuery(make_expr(make_item(100L))) expect_match(sql, "select distinct", ignore.case = TRUE) }) test_that("buildConceptSetQuery includes CONCEPT_ANCESTOR for descendants", { expr <- make_expr(make_item(300L, include_descendants = TRUE)) sql <- buildConceptSetQuery(expr) expect_match(sql, "CONCEPT_ANCESTOR", ignore.case = TRUE) }) test_that("buildConceptSetQuery includes CONCEPT_RELATIONSHIP for mapped", { expr <- make_expr(make_item(400L, include_mapped = TRUE)) sql <- buildConceptSetQuery(expr) expect_match(sql, "CONCEPT_RELATIONSHIP", ignore.case = TRUE) expect_match(sql, "Maps to") }) test_that("buildConceptSetQuery handles descAndMapped — ancestor + mapped join", { expr <- make_expr(make_item(500L, include_descendants = TRUE, include_mapped = TRUE)) sql <- buildConceptSetQuery(expr) expect_match(sql, "CONCEPT_ANCESTOR", ignore.case = TRUE) expect_match(sql, "CONCEPT_RELATIONSHIP", ignore.case = TRUE) }) test_that("buildConceptSetQuery uses LEFT JOIN for excluded concepts", { expr <- make_expr( make_item(100L), make_item(999L, is_excluded = TRUE) ) sql <- buildConceptSetQuery(expr) expect_match(sql, "LEFT JOIN", ignore.case = TRUE) expect_match(sql, "E.concept_id is null", ignore.case = TRUE) }) test_that("buildConceptSetQuery returns empty string for zero items", { sql <- buildConceptSetQuery(list(items = list())) expect_equal(sql, "") }) test_that("buildConceptSetQuery uses default placeholder schema", { sql <- buildConceptSetQuery(make_expr(make_item(1L))) expect_match(sql, "@vocabulary_database_schema") }) test_that("buildConceptSetQuery uses custom vocabulary schema", { sql <- buildConceptSetQuery(make_expr(make_item(1L)), vocabularyDatabaseSchema = "myschema") expect_match(sql, "myschema") expect_no_match(sql, "@vocabulary_database_schema") }) test_that("buildConceptSetQuery accepts a JSON character string", { expr <- make_expr(make_item(201826L)) json_str <- jsonlite::toJSON(expr, auto_unbox = TRUE) sql <- buildConceptSetQuery(json_str) expect_type(sql, "character") expect_match(sql, "201826") }) test_that("buildConceptSetQuery treats NULL flags the same as FALSE", { item <- list(concept = list(CONCEPT_ID = 100L, DOMAIN_ID = "Condition")) sql <- buildConceptSetQuery(list(items = list(item))) expect_match(sql, "100") expect_no_match(sql, "CONCEPT_ANCESTOR") expect_no_match(sql, "CONCEPT_RELATIONSHIP") }) # ============================================================ # buildConceptSetQuery — error cases # ============================================================ test_that("buildConceptSetQuery errors on empty schema string", { expect_error( buildConceptSetQuery(make_expr(make_item(1L)), vocabularyDatabaseSchema = ""), "empty" ) }) test_that("buildConceptSetQuery errors on NA schema", { expect_error( buildConceptSetQuery(make_expr(make_item(1L)), vocabularyDatabaseSchema = NA_character_), "NA or empty" ) }) test_that("buildConceptSetQuery errors when items element is missing", { expect_error( buildConceptSetQuery(list(foo = "bar")), "`items`" ) }) test_that("buildConceptSetQuery errors when concept element is missing from item", { bad <- list(items = list(list(includeDescendants = FALSE))) expect_error(buildConceptSetQuery(bad), "concept") }) test_that("buildConceptSetQuery errors when CONCEPT_ID is missing", { bad <- list(items = list(list(concept = list(DOMAIN_ID = "Condition")))) expect_error(buildConceptSetQuery(bad), "CONCEPT_ID") }) test_that("buildConceptSetQuery errors when CONCEPT_ID is non-numeric", { bad <- list(items = list(list(concept = list(CONCEPT_ID = "abc")))) expect_error(buildConceptSetQuery(bad), "numeric") }) test_that("buildConceptSetQuery errors when CONCEPT_ID is NA", { bad <- list(items = list(list(concept = list(CONCEPT_ID = NA_real_)))) expect_error(buildConceptSetQuery(bad), "NA") }) test_that("buildConceptSetQuery errors on invalid JSON string", { expect_error(buildConceptSetQuery("{not valid json}"), "JSON") }) test_that("buildConceptSetQuery treats NA logical flag as FALSE (no error)", { item <- list( concept = list(CONCEPT_ID = 100L), includeDescendants = NA ) # The validation warns; downstream isTRUE(NA) == FALSE so no crash expect_warning( buildConceptSetQuery(list(items = list(item))), "NA" ) }) # ============================================================ # buildConceptSetQueries # ============================================================ test_that("buildConceptSetQueries returns a named list", { cs_list <- list( diabetes = make_expr(make_item(201826L)), hypertension = make_expr(make_item(316866L)) ) result <- buildConceptSetQueries(cs_list) expect_type(result, "list") expect_named(result, c("diabetes", "hypertension")) }) test_that("buildConceptSetQueries each element is an SQL string", { cs_list <- list( cs1 = make_expr(make_item(100L)), cs2 = make_expr(make_item(200L)) ) result <- buildConceptSetQueries(cs_list) expect_match(result$cs1, "100") expect_match(result$cs2, "200") }) test_that("buildConceptSetQueries passes vocabularyDatabaseSchema through", { cs_list <- list(cs = make_expr(make_item(1L))) result <- buildConceptSetQueries(cs_list, vocabularyDatabaseSchema = "vocab") expect_match(result$cs, "vocab") }) # ============================================================ # collectCsFromCohort # ============================================================ make_cohort_donor <- function() { list( ConceptSets = list( list( name = "Type 2 Diabetes", expression = list(items = list( list(concept = list(CONCEPT_ID = 201826L), isExcluded = FALSE) )) ), list( name = "Hypertension!", expression = list(items = list( list(concept = list(CONCEPT_ID = 316866L), isExcluded = FALSE) )) ) ) ) } test_that("collectCsFromCohort returns a list", { result <- collectCsFromCohort(make_cohort_donor()) expect_type(result, "list") }) test_that("collectCsFromCohort returns correct number of concept sets", { result <- collectCsFromCohort(make_cohort_donor()) expect_length(result, 2L) }) test_that("collectCsFromCohort names are lowerCamelCase", { result <- collectCsFromCohort(make_cohort_donor()) expect_named(result, c("type2Diabetes", "hypertension")) }) test_that("collectCsFromCohort strips special characters from names", { donor <- list( ConceptSets = list( list( name = "My (Special) Concept!", expression = list(items = list(list(concept = list(CONCEPT_ID = 1L)))) ) ) ) result <- collectCsFromCohort(donor) # Name should not contain special chars expect_false(grepl("[^[:alnum:]]", names(result)[1])) }) test_that("collectCsFromCohort extracts expression with items", { result <- collectCsFromCohort(make_cohort_donor()) expect_true(!is.null(result$type2Diabetes$items)) }) test_that("collectCsFromCohort errors when input is not a list", { expect_error(collectCsFromCohort("not a list"), "named list") }) test_that("collectCsFromCohort errors when input is an unnamed list", { expect_error(collectCsFromCohort(list("a")), "named list") }) test_that("collectCsFromCohort errors when ConceptSets element is missing", { expect_error(collectCsFromCohort(list(foo = 1)), "ConceptSets") })