From f1e9aa383a0daa42ff2711e8aca93bf2d8d6ef1e Mon Sep 17 00:00:00 2001 From: Jared Knowles Date: Wed, 5 Aug 2026 11:53:27 -0400 Subject: [PATCH] test: prove 'All Categories' passes through the geographic rollup Geographic totals are the expensive case cog-api#37 was filed about -- without this a caller issues one rollup per category and sums them. The pass-through was expected to work by construction; this asserts it rather than assuming it, including under per_capita and inflation adjustment. --- R/rollup.R | 6 +++- man/cog_geographic_rollup.Rd | 6 +++- tests/testthat/test-all-categories-rollup.R | 40 +++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test-all-categories-rollup.R diff --git a/R/rollup.R b/R/rollup.R index 3b7d5c1..834fc8b 100644 --- a/R/rollup.R +++ b/R/rollup.R @@ -19,7 +19,11 @@ #' `state`, `county`, `city`. Each element is a character vector of #' `canonical_govid` values. At least one layer required. #' @param category Single category name or character vector (passed through -#' to [cog_spending()]). +#' to [cog_spending()]), or the reserved `"All Categories"` for one summed +#' row per `(year, canonical_govid, subtype)` covering every category in the +#' concept's scope. `"All Categories"` is the efficient way to build a +#' geographic total: without it a caller must issue one rollup per category +#' and sum the results themselves. #' @param years Integer vector of years. #' @param per_capita If `TRUE`, per-capita uses each gov's own per-year #' population from `gov_population_yearly`. Govs with missing population diff --git a/man/cog_geographic_rollup.Rd b/man/cog_geographic_rollup.Rd index 497fde8..d157b5b 100644 --- a/man/cog_geographic_rollup.Rd +++ b/man/cog_geographic_rollup.Rd @@ -20,7 +20,11 @@ cog_geographic_rollup( `canonical_govid` values. At least one layer required.} \item{category}{Single category name or character vector (passed through -to [cog_spending()]).} +to [cog_spending()]), or the reserved `"All Categories"` for one summed +row per `(year, canonical_govid, subtype)` covering every category in the +concept's scope. `"All Categories"` is the efficient way to build a +geographic total: without it a caller must issue one rollup per category +and sum the results themselves.} \item{years}{Integer vector of years.} diff --git a/tests/testthat/test-all-categories-rollup.R b/tests/testthat/test-all-categories-rollup.R new file mode 100644 index 0000000..5e43f6e --- /dev/null +++ b/tests/testthat/test-all-categories-rollup.R @@ -0,0 +1,40 @@ +test_that('cog_geographic_rollup() accepts "All Categories" and agrees with per-category sums', { + skip_if_no_corpus() + govs <- cog_gov_search(name = NULL, state = "WI", type = 2L) + expect_gt(nrow(govs), 1L) + ids <- list(city = utils::head(govs$canonical_govid, 25L)) + + by_cat <- cog_geographic_rollup(ids, category = NULL, years = 2019L) + total <- cog_geographic_rollup(ids, category = "All Categories", years = 2019L) + + expect_setequal(unique(total$category), "All Categories") + # one row per (govid, subtype) that appears in the per-category result + key_by_cat <- unique(paste(by_cat$canonical_govid, by_cat$spend_subtype)) + key_total <- paste(total$canonical_govid, total$spend_subtype) + expect_setequal(key_total, key_by_cat) + + lhs <- tapply(by_cat$amt_nominal, paste(by_cat$canonical_govid, by_cat$spend_subtype), sum) + rhs <- tapply(total$amt_nominal, key_total, sum) + expect_equal(as.numeric(rhs[names(lhs)]), as.numeric(lhs), tolerance = 1e-8) +}) + +test_that('"All Categories" survives per_capita and inflation adjustment through the rollup', { + skip_if_no_corpus() + govs <- cog_gov_search(name = NULL, state = "WI", type = 2L) + ids <- list(city = utils::head(govs$canonical_govid, 10L)) + r <- cog_geographic_rollup(ids, category = "All Categories", years = 2019L, + per_capita = TRUE, adjust_to_year = 2020L) + expect_true(all(c("amt_per_capita_nominal", "amt_real", "amt_per_capita_real") %in% names(r))) + expect_setequal(unique(r$category), "All Categories") + expect_true(all(is.finite(r$amt_real))) +}) + +test_that('cog_geographic_rollup() still refuses expenditure_concept = "total" with "All Categories"', { + skip_if_no_corpus() + govs <- cog_gov_search(name = NULL, state = "WI", type = 2L) + ids <- list(city = utils::head(govs$canonical_govid, 5L)) + expect_error( + cog_geographic_rollup(ids, category = "All Categories", years = 2019L, + expenditure_concept = "total") + ) +})