# Phase 1: Energy Metrics Calculation Tests # Tests for core energy burden and NER calculation functions test_that("ner_func calculates Net Energy Return correctly", { # Standard case expect_equal(ner_func(50000, 5000), 9) # At poverty threshold (10% burden = NER of 9) expect_equal(ner_func(100000, 10000), 9) # Higher burden (20% burden = NER of 4) expect_equal(ner_func(10000, 2000), 4) }) test_that("energy_burden_func calculates energy burden correctly", { # Standard case: 10% burden expect_equal(energy_burden_func(50000, 5000), 0.1) # 5% burden expect_equal(energy_burden_func(100000, 5000), 0.05) # 20% burden expect_equal(energy_burden_func(10000, 2000), 0.2) }) test_that("neb_func is an alias for energy_burden_func", { # neb_func is actually an alias for energy_burden_func in the package # So it returns S/G, not (G-S)/G expect_equal(neb_func(50000, 5000), 0.1) # 10% spent on energy expect_equal(neb_func(100000, 10000), 0.1) expect_equal(neb_func(10000, 2000), 0.2) # 20% spent on energy # Verify it's the same as energy_burden_func expect_equal(neb_func(50000, 5000), energy_burden_func(50000, 5000)) }) test_that("energy burden and NER are inverse functions", { income <- 50000 cost <- 5000 eb <- energy_burden_func(income, cost) ner <- ner_func(income, cost) # Test inverse relationship: eb = 1 / (ner + 1) expect_equal(eb, 1 / (ner + 1)) # Test inverse: ner = (1 / eb) - 1 expect_equal(ner, (1 / eb) - 1) }) test_that("energy poverty threshold (NER = 9) matches 10% burden", { # NER of 9 should equal 10% energy burden income <- 100000 cost <- 10000 ner_value <- ner_func(income, cost) eb_value <- energy_burden_func(income, cost) expect_equal(ner_value, 9) expect_equal(eb_value, 0.1) }) test_that("ner_func handles zero income", { # Zero income means all spending comes from elsewhere # NER = (0 - S) / S = -1 expect_equal(ner_func(0, 1000), -1) expect_equal(ner_func(0, 5000), -1) }) test_that("ner_func handles zero energy cost", { # Zero energy cost means infinite return expect_equal(ner_func(50000, 0), Inf) expect_equal(ner_func(100000, 0), Inf) }) test_that("ner_func handles negative income", { # Negative income (debt/losses) still produces valid NER expect_equal(ner_func(-5000, 2000), -3.5) # (-5000 - 2000) / 2000 # Should be finite expect_true(is.finite(ner_func(-1000, 1000))) }) test_that("energy_burden_func handles zero income", { # Zero income means infinite burden expect_equal(energy_burden_func(0, 1000), Inf) }) test_that("energy_burden_func handles zero cost", { # Zero cost means zero burden expect_equal(energy_burden_func(50000, 0), 0) }) test_that("eroi_func calculates Energy Return on Investment", { # EROI = G / S expect_equal(eroi_func(50000, 5000), 10) expect_equal(eroi_func(100000, 10000), 10) expect_equal(eroi_func(10000, 2000), 5) }) test_that("eroi_func and ner_func relationship", { # EROI = NER + 1 income <- 50000 cost <- 5000 eroi <- eroi_func(income, cost) ner <- ner_func(income, cost) expect_equal(eroi, ner + 1) }) test_that("dear_func calculates Discretionary Energy Affordability Ratio", { # DEAR = (G - S) / G # This is the same as NEB (net energy burden) expect_equal(dear_func(50000, 5000), 0.9) expect_equal(dear_func(100000, 10000), 0.9) }) test_that("all metrics handle same input consistently", { income <- 75000 cost <- 6000 # Calculate all metrics eb <- energy_burden_func(income, cost) ner <- ner_func(income, cost) eroi <- eroi_func(income, cost) neb <- neb_func(income, cost) dear <- dear_func(income, cost) # Verify relationships expect_equal(eb, cost / income) expect_equal(ner, (income - cost) / cost) expect_equal(eroi, income / cost) expect_equal(neb, cost / income) # neb_func is alias for energy_burden_func expect_equal(dear, (income - cost) / income) # DEAR is net energy burden # Verify mathematical relationships expect_equal(eroi, ner + 1) expect_equal(eb + dear, 1) # Energy burden + DEAR = 1 expect_equal(eb, 1 / eroi) expect_equal(neb, eb) # neb_func is alias for energy_burden_func }) test_that("metrics work with vectors", { incomes <- c(50000, 60000, 70000) costs <- c(5000, 6000, 7000) ner_values <- ner_func(incomes, costs) expect_equal(length(ner_values), 3) expect_equal(ner_values[1], 9) expect_equal(ner_values[2], 9) expect_equal(ner_values[3], 9) }) test_that("vectorized calculations match element-wise", { incomes <- c(50000, 100000, 25000) costs <- c(5000, 10000, 5000) # Vectorized ner_vec <- ner_func(incomes, costs) # Element-wise ner_elem <- c( ner_func(incomes[1], costs[1]), ner_func(incomes[2], costs[2]), ner_func(incomes[3], costs[3]) ) expect_equal(ner_vec, ner_elem) }) test_that("metrics preserve NA values appropriately", { incomes <- c(50000, NA, 70000) costs <- c(5000, 6000, NA) ner_values <- ner_func(incomes, costs) expect_true(is.na(ner_values[2])) # NA income -> NA NER expect_true(is.na(ner_values[3])) # NA cost -> NA NER expect_false(is.na(ner_values[1])) # Valid inputs -> valid NER }) test_that("extreme values don't cause overflow", { # Very large income large_income <- 1e12 cost <- 1000 ner <- ner_func(large_income, cost) expect_true(is.finite(ner)) expect_gt(ner, 0) # Very small income small_income <- 0.01 small_cost <- 0.001 ner_small <- ner_func(small_income, small_cost) expect_true(is.finite(ner_small)) }) test_that("energy burden never exceeds 1 for positive inputs", { # Unless cost > income incomes <- c(50000, 100000, 25000) costs <- c(5000, 10000, 5000) eb_values <- energy_burden_func(incomes, costs) expect_true(all(eb_values <= 1)) expect_true(all(eb_values >= 0)) }) test_that("energy burden can exceed 1 when cost > income", { # Cost exceeds income eb <- energy_burden_func(10000, 15000) expect_gt(eb, 1) expect_equal(eb, 1.5) }) test_that("NER is negative when cost exceeds income", { # NER = (G - S) / S # When S > G, numerator is negative ner <- ner_func(10000, 15000) expect_lt(ner, 0) expect_equal(ner, -5000 / 15000) }) test_that("metrics handle recycling correctly", { # Single income, multiple costs income <- 50000 costs <- c(2000, 5000, 10000) ner_values <- ner_func(income, costs) expect_equal(length(ner_values), 3) expect_equal(ner_values[1], (50000 - 2000) / 2000) expect_equal(ner_values[2], (50000 - 5000) / 5000) expect_equal(ner_values[3], (50000 - 10000) / 10000) }) test_that("calculate_weighted_metrics works with sample data", { data <- create_sample_lead_data(n = 100) # Test that we can calculate metrics without errors expect_silent({ data$ner <- ner_func(data$income, data$energy_cost) data$energy_burden <- energy_burden_func(data$income, data$energy_cost) }) # Check that all values are finite (for valid data) valid_rows <- data$income > 0 & data$energy_cost > 0 expect_true(all(is.finite(data$ner[valid_rows]))) expect_true(all(is.finite(data$energy_burden[valid_rows]))) }) test_that("poverty rate calculation based on NER threshold", { data <- create_sample_lead_data(n = 200) data$ner <- ner_func(data$income, data$energy_cost) # Count households below poverty threshold (NER < 9) poverty_count <- sum(data$ner < 9, na.rm = TRUE) total_count <- sum(!is.na(data$ner)) poverty_rate <- poverty_count / total_count # Poverty rate should be between 0 and 1 expect_gte(poverty_rate, 0) expect_lte(poverty_rate, 1) # With random data, should have some variation expect_gt(poverty_count, 0) # Should have some in poverty expect_lt(poverty_count, total_count) # Not all in poverty })