tests/testthat/test-cardinality.R

# Tests for cardinality matching (Feature 6)

test_that("cardinality_match returns matching_result", {
  set.seed(42)
  left <- data.frame(id = 1:20, x = rnorm(20, 0, 1), y = rnorm(20, 0, 1))
  right <- data.frame(id = 21:50, x = rnorm(30, 0.5, 1), y = rnorm(30, 0.3, 1))
  result <- cardinality_match(left, right, vars = c("x", "y"),
                              max_std_diff = 0.3)

  expect_s3_class(result, "matching_result")
  expect_true(nrow(result$pairs) > 0)
  expect_true(!is.null(result$info$pruning_iterations))
  expect_true(!is.null(result$info$pairs_removed))
})

test_that("cardinality_match improves balance", {
  set.seed(42)
  left <- data.frame(id = 1:30, x = rnorm(30, 0, 1), y = rnorm(30, 0, 1))
  right <- data.frame(id = 31:80, x = rnorm(50, 1, 1), y = rnorm(50, 0.5, 1))

  result <- cardinality_match(left, right, vars = c("x", "y"),
                              max_std_diff = 0.25)

  # Balance should be within threshold (or close if pruning hit limits)
  bal <- balance_diagnostics(result, left, right, vars = c("x", "y"))
  max_imbalance <- max(abs(bal$var_stats$std_diff))
  # Should be improved from the full match
  expect_true(max_imbalance <= 0.5 || result$info$pairs_removed > 0)
})

test_that("cardinality_match with already-balanced data removes no pairs", {
  set.seed(42)
  left <- data.frame(id = 1:10, x = rnorm(10, 0, 1))
  right <- data.frame(id = 11:20, x = rnorm(10, 0, 1))

  result <- cardinality_match(left, right, vars = "x",
                              max_std_diff = 1.0)  # Very lenient

  # Should remove zero or very few pairs
  expect_true(result$info$pairs_removed <= 2)
})

test_that("cardinality_match validates max_std_diff", {
  left <- data.frame(id = 1:3, x = 1:3)
  right <- data.frame(id = 4:6, x = 4:6)
  expect_error(cardinality_match(left, right, vars = "x", max_std_diff = -0.1),
               "max_std_diff must be a positive number")
})

test_that("cardinality_match with tight threshold prunes pairs", {
  set.seed(42)
  left <- data.frame(id = 1:20, x = rnorm(20, 0, 1))
  right <- data.frame(id = 21:50, x = rnorm(30, 1, 1))
  result <- cardinality_match(left, right, vars = "x",
                              max_std_diff = 0.05)

  # With very tight threshold, some pruning should occur
  expect_true(result$info$pairs_removed >= 0)
  expect_true(!is.null(result$info$pruning_iterations))
})

test_that("cardinality_match preserves pair structure", {
  set.seed(42)
  left <- data.frame(id = 1:15, x = rnorm(15), y = rnorm(15))
  right <- data.frame(id = 16:35, x = rnorm(20), y = rnorm(20))
  result <- cardinality_match(left, right, vars = c("x", "y"),
                              max_std_diff = 0.2)

  expect_true("left_id" %in% names(result$pairs))
  expect_true("right_id" %in% names(result$pairs))
  expect_true("distance" %in% names(result$pairs))
})

Try the couplr package in your browser

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

couplr documentation built on March 3, 2026, 5:08 p.m.