IobConsecutiveEventsTests.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import Foundation
  2. import Testing
  3. @testable import Trio
  4. @Suite("Consecutive Pump Suspend/Resume Events Tests") struct IobConsecutiveEventsTests {
  5. // Helper function to create a basic basal profile
  6. func createBasicBasalProfile() -> [BasalProfileEntry] {
  7. [
  8. BasalProfileEntry(
  9. start: "00:00:00",
  10. minutes: 0,
  11. rate: 1
  12. )
  13. ]
  14. }
  15. @Test(
  16. "should treat two consecutive PumpSuspend events as a single, longer suspend from the first event"
  17. ) func consecutivePumpSuspendEvents() async throws {
  18. let basalprofile = createBasicBasalProfile()
  19. let now = Calendar.current.startOfDay(for: Date()) + 60.minutesToSeconds // Current time 01:00
  20. let suspendTime1 = now - 45.minutesToSeconds // Suspend 1 at 00:15
  21. let suspendTime2 = now - 30.minutesToSeconds // Suspend 2 at 00:30
  22. let resumeTime = now - 15.minutesToSeconds // Resume at 00:45
  23. // JS: reversed chronological order (newest first)
  24. let pumpHistory = [
  25. ComputedPumpHistoryEvent.forTest(
  26. type: .pumpResume,
  27. timestamp: resumeTime
  28. ),
  29. ComputedPumpHistoryEvent.forTest(
  30. type: .pumpSuspend,
  31. timestamp: suspendTime2
  32. ),
  33. ComputedPumpHistoryEvent.forTest(
  34. type: .pumpSuspend,
  35. timestamp: suspendTime1
  36. )
  37. ]
  38. var profile = Profile()
  39. profile.currentBasal = 1
  40. profile.maxDailyBasal = 1
  41. profile.dia = 3
  42. profile.basalprofile = basalprofile
  43. profile.suspendZerosIob = true
  44. let treatments = try IobHistory.calcTempTreatments(
  45. history: pumpHistory,
  46. profile: profile,
  47. clock: now,
  48. autosens: nil,
  49. zeroTempDuration: nil
  50. )
  51. // Check total insulin impact for the period:
  52. // It should produce -0.5U being suspended for 30m total
  53. #expect(treatments.netInsulin().isWithin(0.05, of: -0.5))
  54. }
  55. @Test(
  56. "should consider only the first PumpResume after a suspend event, ignoring subsequent consecutive resumes"
  57. ) func consecutivePumpResumeEvents() async throws {
  58. let basalprofile = createBasicBasalProfile()
  59. let now = Calendar.current.startOfDay(for: Date()) + 60.minutesToSeconds // Current time 01:00
  60. let suspendTime = now - 45.minutesToSeconds // Suspend at 00:15
  61. let resumeTime1 = now - 30.minutesToSeconds // Resume 1 at 00:30
  62. let resumeTime2 = now - 15.minutesToSeconds // Resume 2 at 00:45
  63. let pumpHistory = [
  64. ComputedPumpHistoryEvent.forTest(
  65. type: .pumpResume,
  66. timestamp: resumeTime2
  67. ),
  68. ComputedPumpHistoryEvent.forTest(
  69. type: .pumpResume,
  70. timestamp: resumeTime1
  71. ),
  72. ComputedPumpHistoryEvent.forTest(
  73. type: .pumpSuspend,
  74. timestamp: suspendTime
  75. )
  76. ]
  77. var profile = Profile()
  78. profile.currentBasal = 1
  79. profile.maxDailyBasal = 1
  80. profile.dia = 3
  81. profile.basalprofile = basalprofile
  82. profile.suspendZerosIob = true
  83. let treatments = try IobHistory.calcTempTreatments(
  84. history: pumpHistory,
  85. profile: profile,
  86. clock: now,
  87. autosens: nil,
  88. zeroTempDuration: nil
  89. )
  90. // Check total insulin impact for the period:
  91. // suspended for 15m, should be -0.25U
  92. #expect(treatments.netInsulin().isWithin(0.05, of: -0.25))
  93. }
  94. @Test(
  95. "should correctly process a complex sequence of suspend, suspend, resume, resume, suspend, resume events"
  96. ) func complexSequenceEvents() async throws {
  97. let basalprofile = createBasicBasalProfile()
  98. let now = Calendar.current.startOfDay(for: Date()) + 90.minutesToSeconds // Current time 01:30
  99. let suspend1 = now - 75.minutesToSeconds // Suspend 1 at 00:15
  100. let suspend2 = now - 60.minutesToSeconds // Suspend 2 at 00:30
  101. let resume1 = now - 45.minutesToSeconds // Resume 1 at 00:45
  102. let resume2 = now - 30.minutesToSeconds // Resume 2 at 01:00
  103. let suspend3 = now - 15.minutesToSeconds // Suspend 3 at 01:15
  104. let resume3 = now // Resume 3 at 01:30 (current time)
  105. let pumpHistory = [
  106. ComputedPumpHistoryEvent.forTest(
  107. type: .pumpResume,
  108. timestamp: resume3
  109. ),
  110. ComputedPumpHistoryEvent.forTest(
  111. type: .pumpSuspend,
  112. timestamp: suspend3
  113. ),
  114. ComputedPumpHistoryEvent.forTest(
  115. type: .pumpResume,
  116. timestamp: resume2
  117. ),
  118. ComputedPumpHistoryEvent.forTest(
  119. type: .pumpResume,
  120. timestamp: resume1
  121. ),
  122. ComputedPumpHistoryEvent.forTest(
  123. type: .pumpSuspend,
  124. timestamp: suspend2
  125. ),
  126. ComputedPumpHistoryEvent.forTest(
  127. type: .pumpSuspend,
  128. timestamp: suspend1
  129. )
  130. ]
  131. var profile = Profile()
  132. profile.currentBasal = 1
  133. profile.maxDailyBasal = 1
  134. profile.dia = 3
  135. profile.basalprofile = basalprofile
  136. profile.suspendZerosIob = true
  137. let treatments = try IobHistory.calcTempTreatments(
  138. history: pumpHistory,
  139. profile: profile,
  140. clock: now,
  141. autosens: nil,
  142. zeroTempDuration: nil
  143. )
  144. // Total insulin calculation:
  145. // Suspended for 45m total, should produce -0.75U
  146. #expect(treatments.netInsulin().isWithin(0.05, of: -0.75))
  147. }
  148. }