# Use context for organization (optional but good practice) context("Calculation Functions") # --- Test calculate_daily_load --- test_that("calculate_daily_load aggregates correctly", { # Create sample activity data sample_acts <- dplyr::tibble( id = c("1", "2", "3"), type = rep("Run", 3), start_date_local = lubridate::ymd_hms("2023-01-01 10:00:00", "2023-01-01 12:00:00", "2023-01-02 09:00:00"), date = lubridate::as_date(c("2023-01-01", "2023-01-01", "2023-01-02")), moving_time = c(1800, 3600, 2700), # 30, 60, 45 mins distance = c(5000, 10000, 8000), # 5k, 10k, 8k duration_mins = c(30, 60, 45), distance_km = c(5, 10, 8) ) # Test aggregation by duration daily_duration <- calculate_daily_load(sample_acts, load_metric = "duration_mins") expect_equal(nrow(daily_duration), 2) # Should have 2 unique dates expect_equal(daily_duration$load[daily_duration$date == "2023-01-01"], 90) # 30 + 60 expect_equal(daily_duration$load[daily_duration$date == "2023-01-02"], 45) # Test aggregation by distance daily_distance <- calculate_daily_load(sample_acts, load_metric = "distance_km") expect_equal(daily_distance$load[daily_distance$date == "2023-01-01"], 15) # 5 + 10 expect_equal(daily_distance$load[daily_distance$date == "2023-01-02"], 8) # Test handling of empty input empty_result <- calculate_daily_load(sample_acts[0, ], load_metric = "duration_mins") expect_equal(nrow(empty_result), 0) expect_true("date" %in% names(empty_result)) expect_true("load" %in% names(empty_result)) # Test that missing dates are filled with 0 sample_acts_gap <- dplyr::tibble( date = lubridate::as_date(c("2023-01-01", "2023-01-03")), duration_mins = c(30, 60) ) daily_load_gap <- calculate_daily_load(sample_acts_gap, load_metric = "duration_mins") expect_equal(nrow(daily_load_gap), 3) # Jan 1, 2, 3 expect_equal(daily_load_gap$load[daily_load_gap$date == "2023-01-02"], 0) }) # --- Test calculate_acwr --- test_that("calculate_acwr computes rolling means and ratio", { # Create sample daily load data (enough for chronic period) set.seed(123) days <- 40 sample_load <- dplyr::tibble( date = lubridate::today() - lubridate::days(days:1), load = sample(50:150, days, replace = TRUE) ) acwr_result <- calculate_acwr(sample_load, acute_period = 7, chronic_period = 28) # Check output structure expect_true(all(c("date", "acute_load", "chronic_load", "acwr") %in% names(acwr_result))) expect_equal(nrow(acwr_result), days - 28 + 1) # Should lose first chronic_period-1 rows # Check a specific calculation (manual calculation needed for verification) # For day 28 (first valid row): # acute = mean(load[22:28]) # chronic = mean(load[1:28]) manual_acute_day28 <- mean(sample_load$load[22:28]) manual_chronic_day28 <- mean(sample_load$load[1:28]) manual_acwr_day28 <- manual_acute_day28 / manual_chronic_day28 expect_equal(acwr_result$acute_load[1], manual_acute_day28) expect_equal(acwr_result$chronic_load[1], manual_chronic_day28) expect_equal(acwr_result$acwr[1], manual_acwr_day28) # Test handling insufficient data short_load <- sample_load[1:20,] # Less than chronic period expect_warning(calculate_acwr(short_load, chronic_period=28)) empty_acwr <- suppressWarnings(calculate_acwr(short_load, chronic_period=28)) expect_equal(nrow(empty_acwr), 0) }) # --- Test Placeholder Functions (Example: TSS) --- test_that("calculate_tss requires user_ftp and calculates placeholder", { sample_acts <- dplyr::tibble( id = "1", type="Ride", date=lubridate::today(), moving_time = 3600, duration_hours = 1, average_watts = 200 ) # Expect error if ftp is missing expect_error(calculate_tss(sample_acts, user_ftp = NULL), "requires 'user_ftp'") # Expect warning about placeholder expect_warning(tss_result <- calculate_tss(sample_acts, user_ftp = 250), "placeholder") expect_true("tss_load" %in% names(tss_result)) # Check placeholder calc: 1hr * (200/250)^2 * 100 = 1 * 0.8^2 * 100 = 64 expect_equal(tss_result$tss_load, 64) }) # --- Test categorize_load_exposure --- test_that("categorize_load_exposure assigns zones correctly", { sample_acwr <- dplyr::tibble( date = lubridate::today() + 0:4, acwr = c(0.5, 1.0, 1.4, 1.6, NA) # Under, Sweet, Caution, Danger, NA ) categorized <- categorize_load_exposure(sample_acwr) expect_equal(categorized$acwr_zone, c("Underload", "Sweet Spot", "Caution Zone", "Danger Zone", NA)) }) # ADD MORE TESTS for other calculation functions (EF, Decoupling, PBs) # These will be more complex and might require sample stream data.