# ======================================================================= # Tests for the 0.3.0 response-level layer. # Fast structural tests run everywhere; calibration and bootstrap tests # need mirt and are skipped on CRAN to keep check time short. # ======================================================================= test_that("simulate_aidif_responses returns paired integer scores", { sim <- simulate_aidif_responses(n_per_group = 50, n_items = 5, n_cat = 4, dasb_items = 2, seed = 1) expect_s3_class(sim, "aidif_responses") expect_identical(dim(sim$human), c(100L, 5L)) expect_identical(dim(sim$ai), dim(sim$human)) expect_identical(names(sim$human), names(sim$ai)) expect_true(all(unlist(sim$human) %in% 0:3)) expect_true(all(unlist(sim$ai) %in% 0:3)) expect_identical(levels(sim$group), c("reference", "focal")) expect_equal(unname(sim$truth$dasb), c(0, 0.5, 0, 0, 0)) expect_output(print(sim), "True DASB items: item2") }) test_that("simulation is reproducible and leaves the RNG stream alone", { set.seed(99); before <- stats::runif(1) set.seed(99) s1 <- simulate_aidif_responses(n_per_group = 30, n_items = 4, seed = 7) after <- stats::runif(1) s2 <- simulate_aidif_responses(n_per_group = 30, n_items = 4, seed = 7) expect_identical(s1$human, s2$human) expect_identical(s1$ai, s2$ai) expect_identical(before, after) }) test_that("agreement controls human-AI concordance", { lo <- simulate_aidif_responses(n_per_group = 400, n_items = 5, agreement = 0, dasb_items = integer(0), ai_drift = 0, seed = 3) hi <- simulate_aidif_responses(n_per_group = 400, n_items = 5, agreement = 0.95, dasb_items = integer(0), ai_drift = 0, seed = 3) agree <- function(s) mean(as.matrix(s$human) == as.matrix(s$ai)) expect_gt(agree(hi), agree(lo) + 0.15) }) test_that("simulator rejects bad arguments", { expect_error(simulate_aidif_responses(n_cat = 1), "n_cat") expect_error(simulate_aidif_responses(agreement = 1), "agreement") expect_error(simulate_aidif_responses(n_items = 4, dasb_items = 5), "dasb_items") expect_error(simulate_aidif_responses(ai_slope = 0), "ai_slope") }) test_that("response input checks catch structural problems", { sim <- simulate_aidif_responses(n_per_group = 40, n_items = 4, seed = 1) expect_error(aiDIF:::.check_responses(sim$human, sim$ai[, 1:3], sim$group, c("reference", "focal")), "same dimensions") bad <- sim$ai; names(bad)[1] <- "x" expect_error(aiDIF:::.check_responses(sim$human, bad, sim$group, c("reference", "focal")), "same item names") expect_error(aiDIF:::.check_responses(sim$human, sim$ai, sim$group[-1], c("reference", "focal")), "length") g3 <- as.character(sim$group); g3[1] <- "other" expect_error(aiDIF:::.check_responses(sim$human, sim$ai, g3, NULL), "exactly two") expect_error(aiDIF:::.check_responses(sim$human, sim$ai, sim$group, c("reference", "nope")), "must name") frac <- sim$human; frac[1, 1] <- 0.5 expect_error(aiDIF:::.check_responses(frac, sim$ai, sim$group, c("reference", "focal")), "integer item scores") }) test_that("mismatched category sets are refused", { sim <- simulate_aidif_responses(n_per_group = 60, n_items = 4, n_cat = 3, seed = 2) ai <- sim$ai ai[sim$group == "focal" & ai[, 1] == 2, 1] <- 1L # focal AI loses cat 2 expect_error(aiDIF:::.check_responses(sim$human, ai, sim$group, c("reference", "focal")), "same score categories") }) test_that("raw DASB variance helper matches scoring_bias_test", { eg <- make_aidif_eg() cc <- lapply(eg$human$var.cov, function(v) 0.3 * as.matrix(v)) d <- scoring_bias_test(eg$human, eg$ai, design = "paired", cross_cov = cc) v <- aiDIF:::.raw_dasb_var(eg$human, eg$ai, cc) expect_equal(unname(d$se), sqrt(v)) }) test_that("DASB print keeps item labels", { eg <- make_aidif_eg() expect_output(print(scoring_bias_test(eg$human, eg$ai)), "item3") }) # ---- mirt-dependent ------------------------------------------------------- test_that("robust engine reproduces robustDIF", { skip_if_not_installed("robustDIF") eg <- make_aidif_eg() cmp <- compare_robustdif(eg$human) expect_identical(cmp$scale_by, c("pooled", "ref", "focal")) expect_true(all(cmp$abs_diff < 1e-8)) }) test_that("fit_aidif_responses recovers planted DASB (paired, graded)", { skip_on_cran() skip_if_not_installed("mirt") sim <- simulate_aidif_responses(n_per_group = 600, n_items = 6, n_cat = 3, dasb_items = 4, dasb_mag = 0.8, dif_items = 1, seed = 11) fit <- suppressWarnings(fit_aidif_responses( sim$human, sim$ai, sim$group, groups = c("reference", "focal"), n_boot = 30, seed = 1)) expect_s3_class(fit, "aidif") expect_s3_class(fit$cross_cov, "aidif_cross_cov") sb <- fit$scoring_bias expect_identical(rownames(sb)[7:8], c("item4_d1", "item4_d2")) expect_true(all(sb[c("item4_d1", "item4_d2"), "DASB"] > 0.4)) expect_true(all(sb[c("item4_d1", "item4_d2"), "p_val"] < 0.05)) # Paired SEs are smaller than independence SEs on the same data. ind <- fit_aidif(as_aidif(fit$calibration$human, c("reference", "focal")), as_aidif(fit$calibration$ai, c("reference", "focal"))) expect_true(stats::median(sb$se / ind$scoring_bias$se) < 1) # Cross-condition correlations are positive for paired scoring. expect_gt(stats::median(diag(attr(fit$cross_cov, "correlation")$group.1)), 0.2) }) test_that("bootstrap SEs and independent design run", { skip_on_cran() skip_if_not_installed("mirt") sim <- simulate_aidif_responses(n_per_group = 400, n_items = 6, dasb_items = integer(0), seed = 5) fb <- suppressWarnings(fit_aidif_responses( sim$human, sim$ai, sim$group, groups = c("reference", "focal"), n_boot = 25, se = "bootstrap", metric = "linked", seed = 2)) expect_identical(attr(fb$scoring_bias, "se_type"), "bootstrap") expect_true(all(is.finite(fb$scoring_bias$se) & fb$scoring_bias$se > 0)) expect_output(print(fb$scoring_bias), "bootstrap") fi <- fit_aidif_responses(sim$human, sim$ai, sim$group, groups = c("reference", "focal"), design = "independent") expect_identical(fi$design, "independent") expect_null(fi$cross_cov) expect_error(fit_aidif_responses(sim$human, sim$ai, sim$group, design = "independent", se = "bootstrap"), "paired design only") expect_error(fit_aidif_responses(sim$human, sim$ai, sim$group, cross_cov = list()), "Do not pass") })