## Tests for swcEcon package — all self-contained, < 10 seconds total library(testthat) library(swcEcon) # ============================================================ # 1. calc_bcr # ============================================================ test_that("calc_bcr returns correct class and BCR > 1 for viable project", { r <- calc_bcr(investment = 20, annual_benefit = 6, annual_omc = 0.8, life = 20, discount_rate = 0.12) expect_s3_class(r, "swcEcon_bcr") expect_gt(r$bcr, 1.0) expect_true(r$pv_benefits > r$pv_costs) }) test_that("calc_bcr BCR < 1 for non-viable project", { r <- calc_bcr(investment = 100, annual_benefit = 1, annual_omc = 0, life = 5, discount_rate = 0.12) expect_lt(r$bcr, 1.0) }) test_that("calc_bcr benefit_lag reduces BCR", { r0 <- calc_bcr(20, 6, 0.8, 20, 0.12, benefit_lag = 0) r2 <- calc_bcr(20, 6, 0.8, 20, 0.12, benefit_lag = 2) expect_lt(r2$bcr, r0$bcr) }) test_that("calc_bcr residual_value increases BCR", { r0 <- calc_bcr(20, 6, 0.8, 20, 0.12) r1 <- calc_bcr(20, 6, 0.8, 20, 0.12, residual_value = 5) expect_gt(r1$bcr, r0$bcr) }) test_that("calc_bcr stops on non-positive investment", { expect_error(calc_bcr(-5, 6), "positive") }) test_that("calc_bcr stops on invalid discount rate", { expect_error(calc_bcr(20, 6, discount_rate = 1.5), "proportion") }) test_that("calc_bcr vector benefits work", { bv <- c(3, 4, 5, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6) r <- calc_bcr(20, bv, life = 20) expect_s3_class(r, "swcEcon_bcr") expect_length(r$bcr, 1L) }) # ============================================================ # 2. calc_npv # ============================================================ test_that("calc_npv positive for viable project", { r <- calc_npv(20, 6, 0.8, 20, 0.12) expect_s3_class(r, "swcEcon_npv") expect_gt(r$npv, 0) expect_equal(ncol(r$cashflows), 6L) expect_equal(nrow(r$cashflows), 20L) }) test_that("calc_npv negative for unviable project", { r <- calc_npv(200, 1, 0, 5, 0.12) expect_lt(r$npv, 0) }) # ============================================================ # 3. calc_irr # ============================================================ test_that("calc_irr exceeds 12% for good project", { r <- calc_irr(20, 6, 0.8, 20) expect_true(r$converged) expect_gt(r$irr_pct, 12) }) test_that("calc_irr converged = FALSE when no root", { r <- calc_irr(1000, 1, 0, 5) expect_false(isTRUE(r$converged)) }) test_that("calc_irr IRR decreases with higher investment", { r1 <- calc_irr(20, 6, 0.8, 20) r2 <- calc_irr(40, 6, 0.8, 20) if (r1$converged && r2$converged) expect_gt(r1$irr, r2$irr) }) # ============================================================ # 4. calc_pbp # ============================================================ test_that("calc_pbp correct class and finite result", { r <- calc_pbp(20, 6, 0.8, 20, 0.12) expect_s3_class(r, "swcEcon_pbp") expect_true(is.finite(r$simple_pbp)) expect_gt(r$discounted_pbp, r$simple_pbp) }) test_that("calc_pbp short PBP gives adoption message", { r <- calc_pbp(10, 8, 0, 20) expect_true(grepl("likely", r$adoption, ignore.case = TRUE)) }) # ============================================================ # 5. calc_mrr # ============================================================ test_that("calc_mrr correct MRR for positive case", { r <- calc_mrr(nb_with = 18000, nb_without = 11000, cost_with = 16000, cost_without = 11500) expect_s3_class(r, "swcEcon_mrr") expect_equal(r$mrr, round((18000-11000)/(16000-11500)*100, 2)) }) test_that("calc_mrr recommends when MRR > min_mrr", { r <- calc_mrr(18000, 11000, 16000, 11500, min_mrr = 100) expect_true(grepl("Recommend", r$recommendation)) }) test_that("calc_mrr does not recommend when MRR < min_mrr", { r <- calc_mrr(12000, 11000, 16000, 11500, min_mrr = 100) expect_true(grepl("not recommend|Do not", r$recommendation, ignore.case = TRUE)) }) test_that("calc_mrr zero marginal cost with positive benefit dominates", { r <- calc_mrr(15000, 11000, 11500, 11500) expect_true(is.infinite(r$mrr) || grepl("Dominates", r$recommendation)) }) # ============================================================ # 6. calc_mbcr # ============================================================ test_that("calc_mbcr correct value", { r <- calc_mbcr(total_benefit = 80, operating_cost = 12, capital_cost = 20) expect_s3_class(r, "swcEcon_mbcr") expect_equal(r$mbcr, round((80 - 12) / 20, 4)) }) # ============================================================ # 7. calc_soil_loss_cost # ============================================================ test_that("calc_soil_loss_cost correct structure", { r <- calc_soil_loss_cost(R = 720, K = 0.25, LS = 4.2, C_pre = 0.35, C_post = 0.18, P_pre = 1.0, P_post = 0.5, area_ha = 500) expect_s3_class(r, "swcEcon_soil") expect_true(r$soil_loss_pre > r$soil_loss_post) expect_true(r$pct_reduction > 0) expect_true(r$annual_benefit_inr > 0) }) test_that("calc_soil_loss_cost uses usle_india_soils data", { data(usle_india_soils, package = "swcEcon") K <- usle_india_soils[usle_india_soils$soil_series == "Vertisols", "k_mean"] r <- calc_soil_loss_cost(R = 650, K = K, LS = 3.5, C_pre = 0.3, C_post = 0.15, area_ha = 200) expect_s3_class(r, "swcEcon_soil") }) # ============================================================ # 8. calc_nutrient_cost # ============================================================ test_that("calc_nutrient_cost returns positive costs", { r <- calc_nutrient_cost(soil_loss_t_ha = 12, area_ha = 200) expect_true(r$total_cost_inr > 0) expect_equal(r$total_cost_inr, r$cost_n_inr + r$cost_p_inr + r$cost_k_inr) }) # ============================================================ # 9. calc_water_value # ============================================================ test_that("calc_water_value returns correct class and positive benefit", { r <- calc_water_value(area_ha = 500, rainfall_mm = 720, rc_pre = 0.35, rc_post = 0.20) expect_s3_class(r, "swcEcon_water") expect_gt(r$annual_benefit_inr, 0) expect_gte(r$runoff_reduced_m3, 0) }) test_that("calc_water_value uses rainfall_erosivity_india data", { data(rainfall_erosivity_india, package = "swcEcon") rf <- rainfall_erosivity_india[ rainfall_erosivity_india$district == "Pune", "annual_rf_mm"] r <- calc_water_value(500, rf) expect_s3_class(r, "swcEcon_water") }) # ============================================================ # 10. calc_irrigation_benefit # ============================================================ test_that("calc_irrigation_benefit positive net benefit", { r <- calc_irrigation_benefit(80, 1.6, 18000, 8000) expect_gt(r$net_benefit_inr, 0) expect_equal(r$gross_benefit_inr - r$additional_cost_inr, r$net_benefit_inr) }) # ============================================================ # 11. calc_employment # ============================================================ test_that("calc_employment correct EGR", { r <- calc_employment(45000, 50, 250) expect_equal(r$egr_days_per_lakh, 45000 / 50) expect_true(is.character(r$mgnregs_60pct_norm)) }) # ============================================================ # 12. sensitivity_analysis # ============================================================ test_that("sensitivity_analysis returns correct structure", { r <- sensitivity_analysis(20, 6, 0.8, 20, 0.12) expect_s3_class(r, "swcEcon_sens") expect_equal(nrow(r$scenarios), 8L) expect_true(all(c("scenario","bcr","npv","robust") %in% names(r$scenarios))) }) test_that("sensitivity_analysis base case BCR matches calc_bcr", { r <- sensitivity_analysis(20, 6, 0.8, 20, 0.12) base <- calc_bcr(20, 6, 0.8, 20, 0.12)$bcr expect_equal(r$scenarios$bcr[1L], round(base, 3)) }) # ============================================================ # 13. calc_switching_value # ============================================================ test_that("calc_switching_value returns positive values for viable project", { r <- calc_switching_value(20, 6, 0.8, 20, 0.12) expect_s3_class(r, "swcEcon_sv") expect_gt(r$sv_cost_pct, 0) expect_gt(r$sv_benefit_pct, 0) }) # ============================================================ # 14. monte_carlo_swc # ============================================================ test_that("monte_carlo_swc returns correct structure", { mc <- monte_carlo_swc(inv_mean = 20, ben_mean = 6, omc_mean = 0.8, n_sim = 200, seed = 42) expect_s3_class(mc, "swcEcon_mc") expect_length(mc$bcr_sim, 200L) expect_gte(mc$p_bcr_gt1, 0) expect_lte(mc$p_bcr_gt1, 1) }) test_that("monte_carlo_swc reproducible with seed", { mc1 <- monte_carlo_swc(20, 6, 0.8, n_sim = 100, seed = 99) mc2 <- monte_carlo_swc(20, 6, 0.8, n_sim = 100, seed = 99) expect_equal(mc1$bcr_sim, mc2$bcr_sim) }) # ============================================================ # 15. run_swc_pipeline # ============================================================ test_that("run_swc_pipeline returns correct class", { pl <- suppressMessages( run_swc_pipeline(20, 6, 0.8, 20, 0.12, include_sensitivity = FALSE, include_monte_carlo = FALSE)) expect_s3_class(pl, "swcEcon_pipeline") expect_s3_class(pl$summary, "data.frame") expect_true(nrow(pl$summary) > 0) expect_true(all(c("indicator","value","status") %in% names(pl$summary))) }) # ============================================================ # 16. generate_swc_report # ============================================================ test_that("generate_swc_report creates valid HTML file", { pl <- suppressMessages( run_swc_pipeline(20, 6, 0.8, include_sensitivity = FALSE)) tmp <- tempfile(fileext = ".html") on.exit(unlink(tmp)) generate_swc_report(pl, output_file = tmp, author = "test") expect_true(file.exists(tmp)) expect_gt(file.size(tmp), 3000L) html <- paste(readLines(tmp, warn = FALSE), collapse = "\n") expect_true(grepl("", html, fixed = TRUE)) expect_true(grepl("swcEcon", html, fixed = TRUE)) }) test_that("generate_swc_report stops on wrong class", { expect_error(generate_swc_report(list(a = 1)), "swcEcon_pipeline") }) # ============================================================ # 17. Datasets # ============================================================ test_that("swc_benchmarks has correct dimensions and values", { data(swc_benchmarks, package = "swcEcon") expect_equal(nrow(swc_benchmarks), 10L) expect_equal(ncol(swc_benchmarks), 10L) expect_true(all(swc_benchmarks$bcr_min >= 1.0)) expect_true(all(swc_benchmarks$bcr_max > swc_benchmarks$bcr_min)) }) test_that("usle_india_soils has 8 soil orders and valid K values", { data(usle_india_soils, package = "swcEcon") expect_equal(nrow(usle_india_soils), 8L) expect_true(all(usle_india_soils$k_mean > 0)) expect_true(all(usle_india_soils$k_mean <= 0.65)) }) test_that("rainfall_erosivity_india has 20 districts", { data(rainfall_erosivity_india, package = "swcEcon") expect_equal(nrow(rainfall_erosivity_india), 20L) expect_true(all(rainfall_erosivity_india$r_factor > 0)) }) test_that("swc_cost_norms has 18 measures with valid costs", { data(swc_cost_norms, package = "swcEcon") expect_equal(nrow(swc_cost_norms), 18L) expect_true(all(swc_cost_norms$norm_2024_inr > swc_cost_norms$norm_2015_inr)) }) test_that("watershed_projects has 50 rows and computed BCR column", { data(watershed_projects, package = "swcEcon") expect_equal(nrow(watershed_projects), 50L) expect_true("bcr" %in% names(watershed_projects)) expect_true(all(!is.na(watershed_projects$bcr))) }) test_that("farmer_adoption has 120 rows and valid adoption column", { data(farmer_adoption, package = "swcEcon") expect_equal(nrow(farmer_adoption), 120L) expect_true(all(farmer_adoption$adopted_swc %in% c(0L, 1L))) expect_true(sum(is.na(farmer_adoption$yield_post_kharif)) == sum(farmer_adoption$adopted_swc == 0L)) })