tests/testthat/test-pre-recovery-events.R

library(testthat)
library(cgmguru)

make_cgm <- function(gl, start = "2026-01-01 00:15:00", step_mins = 5) {
  data.frame(
    id = "A",
    time = as.POSIXct(start, tz = "UTC") + seq_along(gl) * 0 + (seq_along(gl) - 1) * step_mins * 60,
    gl = gl
  )
}

test_that("hypoglycemic detailed end is last low reading before confirmed recovery", {
  df <- make_cgm(c(60, 62, 65, 80, 82, 84, 86, 88))

  res <- detect_hypoglycemic_events(
    df,
    start_gl = 70,
    dur_length = 15,
    end_length = 15,
    return_interpolated = TRUE
  )


  expect_equal(sum(res$events_total$total_episodes), 1)
  expect_equal(nrow(res$events_detailed), 1)
  expect_equal(res$events_detailed$start_time[1], df$time[1])
  expect_equal(res$events_detailed$end_time[1], df$time[3])
  expect_equal(res$events_detailed$end_index[1], 3)
  expect_true(is.data.frame(res$interpolated_data))
  expect_named(res$interpolated_data, c("id", "time", "gl"))
})

test_that("hyperglycemic detailed end is last high reading before confirmed recovery", {
  df <- make_cgm(c(190, 195, 200, 170, 165, 160, 155))

  res <- detect_hyperglycemic_events(
    df,
    start_gl = 180,
    dur_length = 15,
    end_length = 15,
    end_gl = 180,
    return_interpolated = TRUE
  )

  expect_equal(sum(res$events_total$total_episodes), 1)
  expect_equal(nrow(res$events_detailed), 1)
  expect_equal(res$events_detailed$start_time[1], df$time[1])
  expect_equal(res$events_detailed$end_time[1], df$time[3])
  expect_equal(res$events_detailed$end_index[1], 3)
  expect_true(is.data.frame(res$interpolated_data))
  expect_named(res$interpolated_data, c("id", "time", "gl"))
})

test_that("extended hyperglycemic detailed end is last high reading before confirmed recovery", {
  cases <- list(
    list(step_mins = 5, gl = c(rep(260, 18), 240, rep(170, 4)), end_index = 19),
    list(step_mins = 15, gl = c(rep(260, 6), 240, rep(170, 3)), end_index = 7)
  )

  for (case in cases) {
    df <- make_cgm(case$gl, step_mins = case$step_mins)

    res <- detect_hyperglycemic_events(df, reading_minutes = case$step_mins)

    expect_equal(sum(res$events_total$total_episodes), 1)
    expect_equal(nrow(res$events_detailed), 1)
    expect_equal(res$events_detailed$start_time[1], df$time[1])
    expect_equal(res$events_detailed$end_time[1], df$time[case$end_index])
    expect_equal(res$events_detailed$end_glucose[1], df$gl[case$end_index])
    expect_equal(res$events_detailed$start_index[1], 1)
    expect_equal(res$events_detailed$end_index[1], case$end_index)
  }
})

test_that("broken recovery reports the end before final successful recovery", {
  hypo_df <- make_cgm(c(50, 51, 52, 80, 82, 50, 51, 52, 80, 82, 84, 86, 88))
  hypo <- detect_hypoglycemic_events(hypo_df, start_gl = 70, dur_length = 15, end_length = 15)

  expect_equal(sum(hypo$events_total$total_episodes), 1)
  expect_equal(nrow(hypo$events_detailed), 1)
  expect_equal(hypo$events_detailed$end_time[1], hypo_df$time[8])
  expect_equal(hypo$events_detailed$duration_below_54_minutes[1], 30)

  hyper_df <- make_cgm(c(190, 195, 200, 170, 165, 190, 195, 200, 170, 165, 160, 155))
  hyper <- detect_hyperglycemic_events(hyper_df, start_gl = 180, dur_length = 15, end_length = 15, end_gl = 180)

  expect_equal(sum(hyper$events_total$total_episodes), 1)
  expect_equal(nrow(hyper$events_detailed), 1)
  expect_equal(hyper$events_detailed$end_time[1], hyper_df$time[8])
})

test_that("events without confirmed recovery are counted at segment end like iglu", {
  hypo_df <- make_cgm(c(60, 62, 65, 80, 82, 60))
  hyper_df <- make_cgm(c(190, 195, 200, 170, 165, 190))

  hypo <- detect_hypoglycemic_events(hypo_df, start_gl = 70, dur_length = 15, end_length = 15)
  hyper <- detect_hyperglycemic_events(hyper_df, start_gl = 180, dur_length = 15, end_length = 15, end_gl = 180)

  expect_equal(sum(hypo$events_total$total_episodes), 1)
  expect_equal(nrow(hypo$events_detailed), 1)
  expect_equal(sum(hyper$events_total$total_episodes), 1)
  expect_equal(nrow(hyper$events_detailed), 1)
})

test_that("detect_all_events keeps counts aligned with standalone confirmed events", {
  hypo_df <- make_cgm(c(50, 51, 52, 80, 82, 84, 86, 88))
  hypo <- detect_hypoglycemic_events(hypo_df, start_gl = 54, dur_length = 15, end_length = 15)
  all_hypo <- detect_all_events(hypo_df, reading_minutes = 5)
  all_hypo_lv2 <- subset(all_hypo$glycemic_event_summary, type == "hypo" & level == "lv2")

  expect_equal(all_hypo$subject_summary$hypo_lv2_total_episodes,
               sum(hypo$events_total$total_episodes))
  expect_equal(all_hypo_lv2$avg_minutes_below_54_per_episode, 15)

  hyper_df <- make_cgm(c(190, 195, 200, 170, 165, 160, 155, 150))
  hyper <- detect_hyperglycemic_events(hyper_df, start_gl = 180, dur_length = 15, end_length = 15, end_gl = 180)
  all_hyper <- detect_all_events(hyper_df, reading_minutes = 5)

  expect_equal(all_hyper$subject_summary$hyper_lv1_total_episodes,
               sum(hyper$events_total$total_episodes))
})

test_that("15-minute sampling detects one-point hypoglycemic event and reports pre-recovery end", {
  df <- make_cgm(c(60, 80, 82, 84), step_mins = 15)

  res <- detect_hypoglycemic_events(df, reading_minutes = 15,
                                    start_gl = 70, dur_length = 15, end_length = 15)

  expect_equal(sum(res$events_total$total_episodes), 1)
  expect_equal(nrow(res$events_detailed), 1)
  expect_equal(res$events_detailed$start_time[1], df$time[1])
  expect_equal(res$events_detailed$end_time[1], df$time[1])
  expect_equal(res$events_detailed$start_index[1], 1)
  expect_equal(res$events_detailed$end_index[1], 1)
})

test_that("15-minute sampling detects one-point hyperglycemic event and reports pre-recovery end", {
  df <- make_cgm(c(190, 170, 165, 160), step_mins = 15)

  res <- detect_hyperglycemic_events(df, reading_minutes = 15,
                                     start_gl = 180, dur_length = 15,
                                     end_length = 15, end_gl = 180)

  expect_equal(sum(res$events_total$total_episodes), 1)
  expect_equal(nrow(res$events_detailed), 1)
  expect_equal(res$events_detailed$start_time[1], df$time[1])
  expect_equal(res$events_detailed$end_time[1], df$time[1])
  expect_equal(res$events_detailed$start_index[1], 1)
  expect_equal(res$events_detailed$end_index[1], 1)
})

test_that("15-minute sampling detect_all_events counts one-point events after recovery confirmation", {
  hypo_df <- make_cgm(c(50, 80, 82, 84), step_mins = 15)
  hypo <- detect_hypoglycemic_events(hypo_df, reading_minutes = 15,
                                    start_gl = 54, dur_length = 15, end_length = 15)
  all_hypo <- detect_all_events(hypo_df, reading_minutes = 15)
  all_hypo_lv2 <- subset(all_hypo$glycemic_event_summary, type == "hypo" & level == "lv2")
  expect_equal(all_hypo$subject_summary$hypo_lv2_total_episodes,
               sum(hypo$events_total$total_episodes))
  expect_equal(all_hypo_lv2$avg_minutes_below_54_per_episode, 15)

  hyper_df <- make_cgm(c(190, 170, 165, 160, 155), step_mins = 15)
  hyper <- detect_hyperglycemic_events(hyper_df, reading_minutes = 15,
                                       start_gl = 180, dur_length = 15,
                                       end_length = 15, end_gl = 180)
  all_hyper <- detect_all_events(hyper_df, reading_minutes = 15)
  expect_equal(all_hyper$subject_summary$hyper_lv1_total_episodes,
               sum(hyper$events_total$total_episodes))
})

test_that("extended hypoglycemia requires more than 120 minutes", {
  short_df <- make_cgm(c(rep(60, 24), rep(80, 3)))
  full_df <- make_cgm(c(rep(60, 25), rep(80, 3)))

  short <- detect_hypoglycemic_events(short_df, reading_minutes = 5,
                                      start_gl = 70, dur_length = 120,
                                      end_length = 15)
  full <- detect_hypoglycemic_events(full_df, reading_minutes = 5,
                                     start_gl = 70, dur_length = 120,
                                     end_length = 15)

  expect_equal(sum(short$events_total$total_episodes), 0)
  expect_equal(sum(full$events_total$total_episodes), 1)

  all_short <- detect_all_events(short_df, reading_minutes = 5)
  all_full <- detect_all_events(full_df, reading_minutes = 5)

  expect_equal(all_short$subject_summary$hypo_extended_total_episodes, 0)
  expect_equal(all_full$subject_summary$hypo_extended_total_episodes, 1)
})

test_that("default extended hyperglycemia uses 120-minute window and 90-minute requirement", {
  short_df <- make_cgm(c(rep(260, 17), rep(170, 3)))
  full_df <- make_cgm(c(rep(260, 18), rep(170, 3)))

  short <- detect_hyperglycemic_events(short_df, reading_minutes = 5,
                                       type = "extended")
  full <- detect_hyperglycemic_events(full_df, reading_minutes = 5,
                                      type = "extended")

  expect_equal(sum(short$events_total$total_episodes), 0)
  expect_equal(sum(full$events_total$total_episodes), 1)

  all_short <- detect_all_events(short_df, reading_minutes = 5)
  all_full <- detect_all_events(full_df, reading_minutes = 5)

  expect_equal(all_short$subject_summary$hyper_extended_total_episodes, 0)
  expect_equal(all_full$subject_summary$hyper_extended_total_episodes, 1)
})

Try the cgmguru package in your browser

Any scripts or data that you put into this service are public.

cgmguru documentation built on July 8, 2026, 9:06 a.m.