testthat::test_that("extract_covariates2 splits, trims, dedups, and handles NULL", { expect_null(extract_covariates2(NULL)) x <- c("A:B", " C*D ", "E", "E", "F : G") out <- extract_covariates2(x) expect_true(all(c("A", "B", "C", "D", "E", "F", "G") %in% out)) expect_equal(length(out), length(unique(out))) # No operators, order preserved after trim expect_equal(extract_covariates2(c("AGE", " SEX ")), c("AGE", "SEX")) }) testthat::test_that("as_simple_formula2 builds intended formula; empty covars behavior is robust", { frm <- as_simple_formula2("Y", c("A", "B:C", "D*E")) # Compare while ignoring whitespace around operators (especially `*`) expect_identical(gsub("\\s+", "", deparse(frm)), "Y~1+A+B:C+D*E") expect_identical(environment(frm), globalenv()) # If you implemented intercept-only support, assert it; otherwise assert an error. intercept_ok <- FALSE out <- try(as_simple_formula2("Y", character(0)), silent = TRUE) if (!inherits(out, "try-error")) { expect_identical(gsub("\\s+", "", deparse(out)), "Y~1") intercept_ok <- TRUE } if (!intercept_ok) { expect_error(as_simple_formula2("Y", character(0))) } }) # ----------------------- beeca-dependent tests ------------------------------ # These exercise gcomp_responder and gcomp_responder_multi without mocking. # They skip cleanly if beeca is not available. testthat::test_that("gcomp_responder runs, drops visit from model terms, returns stable shape", { testthat::skip_if_not_installed("beeca") set.seed(1) dat <- data.frame( Y = rbinom(160, 1, 0.45), TRT = factor( sample(c("Placebo", "Drug"), 160, TRUE), levels = c("Placebo", "Drug") ), BASE = rnorm(160), VIS = sample(c("W4", "W8"), 160, TRUE) ) vars <- list( outcome = "Y", group = "TRT", covariates = c("BASE", "TRT:BASE", "VIS"), visit = "VIS" ) out <- gcomp_responder( data = dat, vars = vars, reference_levels = "Placebo", var_method = "Ge", type = "HC0", contrast = "diff" ) # Ensure VIS was not in the model terms frm <- stats::as.formula(paste0( vars$outcome, " ~ 1 + ", paste0( setdiff( unique(c(vars$group, extract_covariates2(vars$covariates))), vars$visit ), collapse = " + " ) )) m <- stats::glm(frm, data = dat, family = binomial()) terms_used <- attr(stats::terms(m), "term.labels") expect_false(any(grepl("^VIS$", terms_used))) # Output structure: at least one trt_* and lsm_*; each has est/se/df expect_true(any(grepl("^trt_", names(out)))) expect_true(any(grepl("^lsm_", names(out)))) for (nm in names(out)) { expect_true(all(c("est", "se", "df") %in% names(out[[nm]]))) expect_type(out[[nm]]$est, "double") expect_type(out[[nm]]$se, "double") expect_true(is.na(out[[nm]]$df)) } }) testthat::test_that("gcomp_responder defaults reference to first factor level (smoke)", { testthat::skip_if_not_installed("beeca") set.seed(2) dat <- data.frame( Y = rbinom(80, 1, 0.5), TRT = factor( rep(c("Placebo", "Drug"), each = 40), levels = c("Placebo", "Drug") ), BASE = rnorm(80), VIS = "W4" ) vars <- list( outcome = "Y", group = "TRT", covariates = c("BASE", "VIS"), visit = "VIS" ) # Should not error and should return structured results when reference not supplied out <- gcomp_responder(dat, vars) expect_true(length(out) > 0) expect_true(all(vapply( out, function(x) all(c("est", "se", "df") %in% names(x)), logical(1) ))) }) testthat::test_that("gcomp_responder validates that group is a factor (if implemented)", { testthat::skip_if_not_installed("beeca") dat <- data.frame( Y = rbinom(10, 1, 0.5), TRT = rep(c("Placebo", "Drug"), each = 5), # character, not factor BASE = rnorm(10), VIS = "W4" ) vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") # If validation added, expect a clear error; otherwise allow skip. err <- try( gcomp_responder(dat, vars, reference_levels = "Placebo"), silent = TRUE ) if (inherits(err, "try-error")) { expect_match(as.character(err), "(?i)factor|categorical") } else { testthat::skip( "group-factor validation not implemented; skipping assertion." ) } }) testthat::test_that("gcomp_responder errors for invalid reference level (if implemented)", { testthat::skip_if_not_installed("beeca") dat <- data.frame( Y = rbinom(20, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), each = 10)), BASE = rnorm(20), VIS = "W8" ) vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") err <- try( gcomp_responder(dat, vars, reference_levels = "ActiveX"), silent = TRUE ) if (inherits(err, "try-error")) { expect_match(as.character(err), "(?i)reference.*level|not.*in.*levels") } else { testthat::skip( "reference-level validation not implemented; skipping assertion." ) } }) testthat::test_that("gcomp_responder validates contrast against allowed set (either here or in beeca)", { testthat::skip_if_not_installed("beeca") dat <- data.frame( Y = rbinom(30, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), each = 15)), BASE = rnorm(30), VIS = "W8" ) vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") expect_error( gcomp_responder( dat, vars, reference_levels = "Placebo", contrast = "weird" ), regexp = "(?i)contrast|allowed|supported|'arg' should be one of" ) }) testthat::test_that("gcomp_responder works with no covariates after extraction (NULL covariates)", { testthat::skip_if_not_installed("beeca") set.seed(4) dat <- data.frame( Y = rbinom(60, 1, 0.45), TRT = factor( sample(c("Placebo", "Drug"), 60, TRUE), levels = c("Placebo", "Drug") ), AVISIT = "W12" ) vars <- list( outcome = "Y", group = "TRT", covariates = NULL, visit = "AVISIT" ) out <- gcomp_responder(dat, vars, reference_levels = "Placebo") expect_true(any(grepl("^lsm_", names(out)))) }) testthat::test_that("gcomp_responder_multi applies per-visit and suffixes names", { testthat::skip_if_not_installed("beeca") set.seed(5) dat <- data.frame( Y = rbinom(50, 1, 0.5), TRT = factor( sample(c("Placebo", "Drug"), 50, TRUE), levels = c("Placebo", "Drug") ), BASE = rnorm(50), AVISIT = factor(sample(c("W4", "W8"), 50, TRUE)) # unsorted by design ) vars <- list( outcome = "Y", group = "TRT", covariates = "BASE", visit = "AVISIT" ) out <- gcomp_responder_multi(dat, vars, reference_levels = "Placebo") v <- unique(dat$AVISIT) # Every visit should suffix at least one trt_* and one lsm_* for (vv in v) { expect_true(any(grepl(paste0("^trt_.*_", vv, "$"), names(out)))) expect_true(any(grepl(paste0("^lsm_.*_", vv, "$"), names(out)))) } # Sanity: entries have est/se/df any_nm <- names(out)[1] expect_true(all(c("est", "se", "df") %in% names(out[[any_nm]]))) }) # ============================================================================= # Additional comprehensive tests for gcomp_responder_multi # ============================================================================= testthat::test_that("gcomp_responder_multi handles three visits correctly", { testthat::skip_if_not_installed("beeca") set.seed(101) dat <- data.frame( Y = rbinom(150, 1, 0.45), TRT = factor( rep(c("Placebo", "Drug"), 75), levels = c("Placebo", "Drug") ), BASE = rnorm(150), AVISIT = factor(rep(c("W4", "W8", "W12"), 50)) ) vars <- list( outcome = "Y", group = "TRT", covariates = "BASE", visit = "AVISIT" ) out <- gcomp_responder_multi(dat, vars, reference_levels = "Placebo") # Should have results for all 3 visits expect_true(any(grepl("_W4$", names(out)))) expect_true(any(grepl("_W8$", names(out)))) expect_true(any(grepl("_W12$", names(out)))) }) testthat::test_that("gcomp_responder_multi handles single visit gracefully", { testthat::skip_if_not_installed("beeca") set.seed(102) dat <- data.frame( Y = rbinom(80, 1, 0.5), TRT = factor( rep(c("Placebo", "Drug"), 40), levels = c("Placebo", "Drug") ), BASE = rnorm(80), AVISIT = factor(rep("W24", 80)) ) vars <- list( outcome = "Y", group = "TRT", covariates = "BASE", visit = "AVISIT" ) out <- gcomp_responder_multi(dat, vars, reference_levels = "Placebo") # Should still work with suffixed names expect_true(any(grepl("_W24$", names(out)))) expect_true(length(out) >= 2) # At least trt and lsm }) testthat::test_that("gcomp_responder_multi passes additional arguments to gcomp_responder", { testthat::skip_if_not_installed("beeca") set.seed(103) dat <- data.frame( Y = rbinom(100, 1, 0.5), TRT = factor( rep(rep(c("Placebo", "Drug"), each = 25), 2), levels = c("Placebo", "Drug") ), BASE = rnorm(100), AVISIT = factor(rep(c("W4", "W8"), each = 50)) ) vars <- list( outcome = "Y", group = "TRT", covariates = "BASE", visit = "AVISIT" ) # Test with different var_method and type out <- gcomp_responder_multi( dat, vars, reference_levels = "Placebo", var_method = "Ge", type = "HC0", contrast = "diff" ) expect_true(length(out) > 0) expect_true(all(vapply(out, function(x) "est" %in% names(x), logical(1)))) }) testthat::test_that("gcomp_responder_multi works with multiple covariates", { testthat::skip_if_not_installed("beeca") set.seed(104) dat <- data.frame( Y = rbinom(120, 1, 0.45), TRT = factor( rep(rep(c("Placebo", "Drug"), each = 30), 2), levels = c("Placebo", "Drug") ), BASE = rnorm(120), AGE = rnorm(120, 50, 10), SEX = factor(sample(c("M", "F"), 120, replace = TRUE)), AVISIT = factor(rep(c("W4", "W8"), each = 60)) ) vars <- list( outcome = "Y", group = "TRT", covariates = c("BASE", "AGE", "SEX"), visit = "AVISIT" ) out <- gcomp_responder_multi(dat, vars, reference_levels = "Placebo") expect_true(length(out) > 0) for (nm in names(out)) { expect_true(all(c("est", "se", "df") %in% names(out[[nm]]))) } }) testthat::test_that("gcomp_responder_multi returns consistent estimate types", { testthat::skip_if_not_installed("beeca") set.seed(105) dat <- data.frame( Y = rbinom(100, 1, 0.5), TRT = factor( rep(rep(c("Placebo", "Drug"), each = 25), 2), levels = c("Placebo", "Drug") ), BASE = rnorm(100), AVISIT = factor(rep(c("W4", "W8"), each = 50)) ) vars <- list( outcome = "Y", group = "TRT", covariates = "BASE", visit = "AVISIT" ) out <- gcomp_responder_multi(dat, vars, reference_levels = "Placebo") # All estimates should be numeric for (nm in names(out)) { expect_type(out[[nm]]$est, "double") expect_type(out[[nm]]$se, "double") # Standard error should be positive expect_true(out[[nm]]$se > 0) } }) testthat::test_that("gcomp_responder_multi handles unbalanced data across visits", { testthat::skip_if_not_installed("beeca") set.seed(106) # Create unbalanced data - more observations at W8 dat <- rbind( data.frame( Y = rbinom(40, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), 20), levels = c("Placebo", "Drug")), BASE = rnorm(40), AVISIT = factor(rep("W4", 40)) ), data.frame( Y = rbinom(80, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), 40), levels = c("Placebo", "Drug")), BASE = rnorm(80), AVISIT = factor(rep("W8", 80)) ) ) dat$AVISIT <- factor(dat$AVISIT, levels = c("W4", "W8")) vars <- list( outcome = "Y", group = "TRT", covariates = "BASE", visit = "AVISIT" ) out <- gcomp_responder_multi(dat, vars, reference_levels = "Placebo") expect_true(any(grepl("_W4$", names(out)))) expect_true(any(grepl("_W8$", names(out)))) }) # ============================================================================= # Input validation edge case tests (01-03 hardening) # ============================================================================= testthat::test_that("gcomp_responder errors on single-arm data", { testthat::skip_if_not_installed("beeca") set.seed(200) dat <- data.frame( Y = rbinom(40, 1, 0.5), TRT = factor(rep("Placebo", 40), levels = c("Placebo", "Drug")), BASE = rnorm(40), VIS = "W4" ) vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") expect_error( gcomp_responder(dat, vars, reference_levels = "Placebo"), class = "rbmiUtils_error_validation" ) }) testthat::test_that("gcomp_responder errors on missing columns", { testthat::skip_if_not_installed("beeca") dat <- data.frame( Y = rbinom(20, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), 10)), VIS = "W4" ) vars <- list(outcome = "Y", group = "TRT", covariates = "NONEXISTENT", visit = "VIS") expect_error( gcomp_responder(dat, vars, reference_levels = "Placebo"), class = "rbmiUtils_error_validation" ) }) testthat::test_that("gcomp_responder errors on zero-variance outcome", { testthat::skip_if_not_installed("beeca") dat <- data.frame( Y = rep(1, 40), TRT = factor(rep(c("Placebo", "Drug"), 20)), BASE = rnorm(40), VIS = "W4" ) vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") expect_error( gcomp_responder(dat, vars, reference_levels = "Placebo"), class = "rbmiUtils_error_validation" ) }) testthat::test_that("gcomp_responder errors on non-numeric outcome", { testthat::skip_if_not_installed("beeca") dat <- data.frame( Y = rep(c("yes", "no"), 20), TRT = factor(rep(c("Placebo", "Drug"), 20)), BASE = rnorm(40), VIS = "W4", stringsAsFactors = FALSE ) vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") expect_error( gcomp_responder(dat, vars, reference_levels = "Placebo"), class = "rbmiUtils_error_type" ) }) testthat::test_that("gcomp_responder errors on non-dataframe input", { testthat::skip_if_not_installed("beeca") vars <- list(outcome = "Y", group = "TRT", covariates = "BASE", visit = "VIS") expect_error( gcomp_responder(list(a = 1), vars, reference_levels = "Placebo"), class = "rbmiUtils_error_type" ) }) testthat::test_that("gcomp_binary errors on single-arm data", { testthat::skip_if_not_installed("beeca") set.seed(201) dat <- data.frame( CRIT1FLN = rbinom(40, 1, 0.5), TRT = factor(rep("Placebo", 40), levels = c("Placebo", "Drug")), BASE = rnorm(40), STRATA = factor(rep("A", 40)), REGION = factor(rep("US", 40)) ) expect_error( gcomp_binary(data = dat), class = "rbmiUtils_error_validation" ) }) testthat::test_that("gcomp_binary errors on missing columns", { testthat::skip_if_not_installed("beeca") dat <- data.frame( CRIT1FLN = rbinom(20, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), 10)) ) expect_error( gcomp_binary(data = dat), class = "rbmiUtils_error_validation" ) }) testthat::test_that("gcomp_binary errors on non-numeric outcome", { testthat::skip_if_not_installed("beeca") dat <- data.frame( CRIT1FLN = rep(c("yes", "no"), 20), TRT = factor(rep(c("Placebo", "Drug"), 20)), BASE = rnorm(40), STRATA = factor(rep("A", 40)), REGION = factor(rep("US", 40)), stringsAsFactors = FALSE ) expect_error( gcomp_binary(data = dat), class = "rbmiUtils_error_type" ) }) testthat::test_that("gcomp_binary errors on zero-variance outcome", { testthat::skip_if_not_installed("beeca") dat <- data.frame( CRIT1FLN = rep(0, 40), TRT = factor(rep(c("Placebo", "Drug"), 20)), BASE = rnorm(40), STRATA = factor(rep("A", 40)), REGION = factor(rep("US", 40)) ) expect_error( gcomp_binary(data = dat), class = "rbmiUtils_error_validation" ) }) testthat::test_that("gcomp_responder_multi errors when visit_var not in data", { dat <- data.frame( Y = rbinom(20, 1, 0.5), TRT = factor(rep(c("Placebo", "Drug"), 10)) ) vars <- list(outcome = "Y", group = "TRT", covariates = NULL, visit = "MISSING_VIS") expect_error( gcomp_responder_multi(dat, vars), class = "rbmiUtils_error_validation" ) })