From 9a4f8a44cd8a9d142213efdced9e5beca13421d3 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 25 Mar 2026 16:40:31 -0500 Subject: [PATCH 1/3] Update unit tests for new 2024 data and functions --- R/check.R | 33 ++++++++++++++++++++ R/lookup.R | 4 +++ R/tax_bill.R | 3 +- tests/testthat/test-check.R | 15 +++++++++ tests/testthat/test-lookup.R | 57 ++++++++++++++++++++++++++++++++++ tests/testthat/test-tax_bill.R | 44 +++++++++++++------------- 6 files changed, 134 insertions(+), 22 deletions(-) diff --git a/R/check.R b/R/check.R index 2962d89a..794279d4 100644 --- a/R/check.R +++ b/R/check.R @@ -231,3 +231,36 @@ check_tif_dt_str <- function(tif_dt) { return(TRUE) } + + +check_pin_tif_dt_str <- function(pin_tif_dt) { + stopifnot( + is.data.frame(pin_tif_dt), + data.table::is.data.table(pin_tif_dt) + ) + + pin_tif_dt_str <- c( + "year" = "numeric", "pin" = "character", "tax_code" = "character", + "agency_num" = "character", "agency_name" = "character", + "agency_major_type" = "character", "agency_minor_type" = "character", + "tif_share" = "numeric" + ) + + if (!identical(pin_tif_dt_str, sapply(pin_tif_dt, mode))) { + stop( + "pin_tif_dt must be in the same format as the data returned by ", + "lookup_pin_tif(). Ensure all column names and types are the same" + ) + } + + keys <- c("year", "pin", "agency_num") + if (!all(keys %in% data.table::key(pin_tif_dt))) { + stop( + "pin_tif_dt must have the same data.table keys as the data returned by ", + "lookup_pin_tif(). Please ensure that the year, pin, and agency_num ", + "columns are set as data.table keys" + ) + } + + return(TRUE) +} diff --git a/R/lookup.R b/R/lookup.R index da3c5d65..b40e6853 100644 --- a/R/lookup.R +++ b/R/lookup.R @@ -364,6 +364,10 @@ lookup_tif <- function(year, tax_code, conn = ptaxsim_db_conn) { check_db_sync(conn) ) + # Make sure to remove any years after 2023 from the year vector, since + # otherwise we risk silently returning null TIF shares for post-2024 TIFs + year <- year[year <= 2023] + tif_share <- NULL dt <- DBI::dbGetQuery( conn, diff --git a/R/tax_bill.R b/R/tax_bill.R index 66fb2226..75401862 100644 --- a/R/tax_bill.R +++ b/R/tax_bill.R @@ -147,7 +147,8 @@ tax_bill <- function(year_vec, stopifnot( check_agency_dt_str(agency_dt), check_pin_dt_str(pin_dt), - check_tif_dt_str(tif_dt) + check_tif_dt_str(tif_dt), + check_pin_tif_dt_str(pin_tif_dt) ) # Create data.table from inputs. Use the Cartesian product if the inputs are diff --git a/tests/testthat/test-check.R b/tests/testthat/test-check.R index a4a4ce64..d65a7dc9 100644 --- a/tests/testthat/test-check.R +++ b/tests/testthat/test-check.R @@ -21,6 +21,8 @@ assign("ptaxsim_db_conn_wrong", ptaxsim_db_conn_wrong, envir = .GlobalEnv) pins <- c("14081020190000", "09274240240000", "07101010391078") years <- c(2019, 2019, 2018) tax_codes <- c("73105", "22031", "35011") +# pin_tif_distribution is only populated starting in 2024 +pin_tif_dt_years <- 2024 test_that("lookups return correct checks", { expect_true(check_agency_dt_str(lookup_agency(years, tax_codes))) @@ -29,6 +31,7 @@ test_that("lookups return correct checks", { expect_true(check_pin_dt_str(lookup_pin(years[1], pins))) expect_true(check_tif_dt_str(lookup_tif(years, tax_codes))) expect_true(check_tif_dt_str(lookup_tif(years[1], tax_codes))) + expect_true(check_pin_tif_dt_str(lookup_pin_tif(pin_tif_dt_years, pins))) }) @@ -44,6 +47,10 @@ test_that("wrong column types throws error", { tif <- lookup_tif(years, tax_codes) %>% mutate(agency_num = as.integer(agency_num)) expect_error(check_tif_dt_str(tif)) + + pin_tif <- lookup_pin_tif(pin_tif_dt_years, pins) %>% + mutate(agency_num = as.integer(agency_num)) + expect_error(check_pin_tif_dt_str(pin_tif)) }) test_that("outputs fail if not keyed data.table", { @@ -70,6 +77,14 @@ test_that("outputs fail if not keyed data.table", { expect_equal(key(tif), c("year", "tax_code", "agency_num")) setkey(tif, NULL) expect_error(check_tif_dt_str(tif)) + + pin_tif <- lookup_pin_tif(pin_tif_dt_years, pins) %>% + as_tibble() + expect_error(check_pin_tif_dt_str(pin_tif)) + pin_tif <- lookup_pin_tif(pin_tif_dt_years, pins) + expect_equal(key(pin_tif), c("year", "pin", "agency_num")) + setkey(pin_tif, NULL) + expect_error(check_pin_tif_dt_str(pin_tif)) }) diff --git a/tests/testthat/test-lookup.R b/tests/testthat/test-lookup.R index 521fe6a3..9cf41e63 100644 --- a/tests/testthat/test-lookup.R +++ b/tests/testthat/test-lookup.R @@ -94,6 +94,11 @@ test_that("lookup values/data are correct", { ) }) +test_that("lookup_tif returns 0 rows for years >= 2024", { + expect_equal(nrow(lookup_tif(2024:max_year, "73105")), 0) + expect_equal(nrow(lookup_tif(2022:2023, "73105")), 2) +}) + test_that("function returns expect data type/structure", { expect_s3_class( lookup_tif(2018, "73105"), @@ -204,6 +209,58 @@ test_that("bad/incorrect inputs throw errors", { }) +context("test lookup_pin_tif()") + +##### TEST lookup_pin_tif() ##### + +# Use PINs known to be in TIFs in 2024 +pin_in_24_tif <- "01301000160000" +pin_in_24_tif_2 <- "01303000090000" +# A random PIN that is not in a TIF in 2024 +pin_not_in_24_tif <- "01011000020000" + +test_that("lookup_pin_tif returns 0 rows for pre-2024 years", { + expect_equal(nrow(lookup_pin_tif(2023, pin_in_24_tif)), 0) +}) + +test_that("lookup_pin_tif returns rows for 2024+", { + expect_equal(nrow(lookup_pin_tif(2024, pin_in_24_tif)), 1) +}) + +test_that("lookup_pin_tif returns expected data type/structure", { + pin_tif <- lookup_pin_tif(2024, pin_in_24_tif) + expect_s3_class(pin_tif, c("data.frame", "data.table")) + expect_named(pin_tif, c( + "year", "pin", "tax_code", "agency_num", + "agency_name", "agency_major_type", "agency_minor_type", "tif_share" + )) +}) + +test_that("lookup_pin_tif returns correct values/data", { + expect_equal( + lookup_pin_tif(2024, pin_in_24_tif)$tif_share, + 0.777, + tolerance = 0.001 + ) +}) + +test_that("lookup_pin_tif returns 0 rows for PIN not in any TIF", { + expect_equal(nrow(lookup_pin_tif(2024, pin_not_in_24_tif)), 0) +}) + +test_that("lookup_pin_tif bad/incorrect inputs throw errors", { + expect_error(lookup_pin_tif("2024", pin_in_24_tif)) # year not numeric + expect_error(lookup_pin_tif(2024, 2153010581083)) # PIN not character + expect_error(lookup_pin_tif(2024, "021530105810")) # PIN wrong length +}) + +test_that("lookup_pin_tif handles multiple PINs", { + # A second PIN that is in a TIF district in 2024 + result <- lookup_pin_tif(2024, c(pin_in_24_tif, pin_in_24_tif_2)) + expect_equal(nrow(result), 2) +}) + + context("test lookup_agency()") ##### TEST lookup_agency() ##### diff --git a/tests/testthat/test-tax_bill.R b/tests/testthat/test-tax_bill.R index d50a865b..db7a8c0d 100644 --- a/tests/testthat/test-tax_bill.R +++ b/tests/testthat/test-tax_bill.R @@ -18,31 +18,32 @@ det_dt <- sample_tax_bills_detail pins <- c( "14081020190000", "09274240240000", "07101010391078" ) -years <- c(2019, 2019, 2018) +years <- 2024:2022 test_that("bad/incorrect vector inputs throw errors", { - expect_error(tax_bill("2019", pins[1])) + expect_error(tax_bill("2024", pins[1])) expect_error(tax_bill(numeric(0), pins[1])) - expect_error(tax_bill(2019, 14081020190000)) - expect_error(tax_bill(c(2000, 2019), pins[1])) - expect_error(tax_bill(2019, c("1408102019000", pins[2]))) - expect_error(tax_bill(2019, pins[1], tax_code_vec = 73105)) - expect_error(tax_bill(2019, pins[1], tax_code_vec = "5454")) - expect_error(tax_bill(2019, pins[2], simplify = "yes")) - expect_error(tax_bill(2018:2019, pins[2:3], simplify = c(TRUE, FALSE))) + expect_error(tax_bill(years[1], 14081020190000)) + expect_error(tax_bill(c(2000, years[1]), pins[1])) + expect_error(tax_bill(years[1], c("1408102019000", pins[2]))) + expect_error(tax_bill(years[1], pins[1], tax_code_vec = 73105)) + expect_error(tax_bill(years[1], pins[1], tax_code_vec = "5454")) + expect_error(tax_bill(years[1], pins[2], simplify = "yes")) + expect_error(tax_bill(years[2:1], pins[2:3], simplify = c(TRUE, FALSE))) }) test_that("bad/incorrect data frame inputs throw errors", { - expect_error(tax_bill(2019, pins[1], pin_dt = c(23000, 959051))) - expect_error(tax_bill(2019, pins[1], agency_dt = c("3232", "TIF"))) - expect_error(tax_bill(2019, pins[1], tif_dt = c(23000, 959051))) + expect_error(tax_bill(years[1], pins[1], pin_dt = c(23000, 959051))) + expect_error(tax_bill(years[1], pins[1], agency_dt = c("3232", "TIF"))) + expect_error(tax_bill(years[1], pins[1], tif_dt = c(23000, 959051))) + expect_error(tax_bill(years[1], pins[1], pin_tif_dt = c(23000, 959051))) }) test_that("non-unique values to main args throws error", { - expect_error(tax_bill(2019, pins[c(1, 1)])) - expect_error(tax_bill(c(2010, 2010), pins[1])) + expect_error(tax_bill(years[1], pins[c(1, 1)])) + expect_error(tax_bill(c(years[c(1, 1)]), pins[1])) expect_error( - tax_bill(c(2010, 2010), pins[c(1, 2)], tax_code_vec = c("73105")) + tax_bill(years[c(1, 1)], pins[c(1, 2)], tax_code_vec = c("73105")) ) }) @@ -52,16 +53,17 @@ test_that("incorrect size inputs throw errors", { }) test_that("data frame inputs throw errors when required cols missing", { - expect_error(tax_bill(2019, pins[1], pin_dt = data.table())) - expect_error(tax_bill(2019, pins[1], agency_dt = data.table())) - expect_error(tax_bill(2019, pins[1], tif_dt = data.table())) + expect_error(tax_bill(years[1], pins[1], pin_dt = data.table())) + expect_error(tax_bill(years[1], pins[1], agency_dt = data.table())) + expect_error(tax_bill(years[1], pins[1], tif_dt = data.table())) + expect_error(tax_bill(years[1], pins[1], pin_tif_dt = data.table())) }) test_that("function returns expected data type/structure", { - expect_s3_class(tax_bill(2018, pins[1]), "data.frame") - expect_s3_class(tax_bill(2018, pins[1]), "data.table") + expect_s3_class(tax_bill(years[1], pins[1]), "data.frame") + expect_s3_class(tax_bill(years[1], pins[1]), "data.table") expect_equal( - key(tax_bill(2018, pins[1])), + key(tax_bill(years[1], pins[1])), c("year", "pin", "agency_num") ) expect_named( From ef48dc8421564ec080b571e9f63b9ab89228e49a Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Fri, 27 Mar 2026 09:52:24 -0500 Subject: [PATCH 2/3] Fix a few final lines of `test-tax_bill.R` --- tests/testthat/test-tax_bill.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-tax_bill.R b/tests/testthat/test-tax_bill.R index db7a8c0d..70954767 100644 --- a/tests/testthat/test-tax_bill.R +++ b/tests/testthat/test-tax_bill.R @@ -67,7 +67,7 @@ test_that("function returns expected data type/structure", { c("year", "pin", "agency_num") ) expect_named( - tax_bill(2018:2019, pins[1:2]), + tax_bill(years[1:2], pins[1:2]), c( "year", "pin", "class", "tax_code", "av", "eav", "agency_num", "agency_name", "agency_major_type", "agency_minor_type", @@ -79,7 +79,7 @@ test_that("function returns expected data type/structure", { 0 ) expect_named( - tax_bill(2018:2019, pins[1:2], simplify = FALSE), + tax_bill(years[1:2], pins[1:2], simplify = FALSE), c( "year", "pin", "class", "tax_code", "av", "eav", "exe_total", "agency_num", "agency_name", "agency_major_type", "agency_minor_type", @@ -94,8 +94,8 @@ test_that("function returns expected data type/structure", { sum(is.na(tax_bill(years[1], pins[1], simplify = FALSE))), 0 ) - expect_equal(dim(tax_bill(years[1], pins[1], simplify = TRUE)), c(12, 12)) - expect_equal(dim(tax_bill(years[1], pins[1], simplify = FALSE)), c(10, 25)) + expect_equal(dim(tax_bill(years[1], pins[1], simplify = TRUE)), c(11, 12)) + expect_equal(dim(tax_bill(years[1], pins[1], simplify = FALSE)), c(9, 25)) }) test_that("returned amount/output correct for single PIN", { From 28caaabecafc8629ee618f8d2ac02a9e514f5cdf Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 30 Mar 2026 11:34:50 -0500 Subject: [PATCH 3/3] Add to vetdis calc in tests --- tests/testthat/test-lookup.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-lookup.R b/tests/testthat/test-lookup.R index 9cf41e63..d65dbcc0 100644 --- a/tests/testthat/test-lookup.R +++ b/tests/testthat/test-lookup.R @@ -163,7 +163,10 @@ test_that("lookup values/data are correct", { expect_equivalent( lookup_pin(2018:max_year, sum_df$pin) %>% mutate( - exe_vet_dis = exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70, + exe_vet_dis = ( + exe_vet_dis_lt50 + exe_vet_dis_50_69 + + exe_vet_dis_ge70 + exe_vet_dis_100 + ), across(starts_with("exe_"), ~ .x != 0) ) %>% select(year, pin, exe_homeowner:exe_disabled, exe_vet_dis) %>%