ProfileJavascriptTests.swift 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import Foundation
  2. import Testing
  3. @testable import Trio
  4. struct ProfileGeneratorTests {
  5. // Base test inputs that match the JavaScript test setup
  6. private func createBaseInputs() -> (
  7. PumpSettings,
  8. BGTargets,
  9. [BasalProfileEntry],
  10. InsulinSensitivities,
  11. Preferences,
  12. CarbRatios,
  13. [TempTarget],
  14. String
  15. ) {
  16. let pumpSettings = PumpSettings(
  17. insulinActionCurve: 10,
  18. maxBolus: 10,
  19. maxBasal: 2
  20. )
  21. let bgTargets = BGTargets(
  22. units: .mgdL,
  23. userPreferredUnits: .mgdL,
  24. targets: [
  25. BGTargetEntry(low: 100, high: 120, start: "00:00", offset: 0)
  26. ]
  27. )
  28. let basalProfile = [
  29. BasalProfileEntry(start: "00:00", minutes: 0, rate: 1.0)
  30. ]
  31. let isf = InsulinSensitivities(
  32. units: .mgdL,
  33. userPreferredUnits: .mgdL,
  34. sensitivities: [
  35. InsulinSensitivityEntry(sensitivity: 100, offset: 0, start: "00:00")
  36. ]
  37. )
  38. let preferences = Preferences()
  39. let carbRatios = CarbRatios(
  40. units: .grams,
  41. schedule: [
  42. CarbRatioEntry(start: "00:00", offset: 0, ratio: 20)
  43. ]
  44. )
  45. let tempTargets: [TempTarget] = []
  46. let model = "523"
  47. return (pumpSettings, bgTargets, basalProfile, isf, preferences, carbRatios, tempTargets, model)
  48. }
  49. @Test("Basic profile generation should create profile with correct values") func testBasicProfileGeneration() throws {
  50. let inputs = createBaseInputs()
  51. let profile = try ProfileGenerator.generate(
  52. pumpSettings: inputs.0,
  53. bgTargets: inputs.1,
  54. basalProfile: inputs.2,
  55. isf: inputs.3,
  56. preferences: inputs.4,
  57. carbRatios: inputs.5,
  58. tempTargets: inputs.6,
  59. model: inputs.7,
  60. clock: Date()
  61. )
  62. #expect(profile.maxIob == 0)
  63. #expect(profile.dia == 10)
  64. #expect(profile.sens == 100)
  65. #expect(profile.currentBasal == 1)
  66. #expect(profile.maxBg == 100)
  67. #expect(profile.minBg == 100)
  68. #expect(profile.carbRatio == 20)
  69. }
  70. @Test("Profile with active temp target should use temp target values") func testProfileWithTempTarget() throws {
  71. var inputs = createBaseInputs()
  72. // Create temp target 5 minutes ago that lasts 20 minutes
  73. let currentTime = Date()
  74. let creationDate = currentTime.addingTimeInterval(-5 * 60)
  75. let tempTarget = TempTarget(
  76. name: "Eating Soon",
  77. createdAt: creationDate,
  78. targetTop: 80,
  79. targetBottom: 80,
  80. duration: 20,
  81. enteredBy: "Test",
  82. reason: "Eating Soon",
  83. isPreset: nil,
  84. enabled: nil,
  85. halfBasalTarget: nil
  86. )
  87. inputs.6 = [tempTarget]
  88. let profile = try ProfileGenerator.generate(
  89. pumpSettings: inputs.0,
  90. bgTargets: inputs.1,
  91. basalProfile: inputs.2,
  92. isf: inputs.3,
  93. preferences: inputs.4,
  94. carbRatios: inputs.5,
  95. tempTargets: inputs.6,
  96. model: inputs.7,
  97. clock: currentTime
  98. )
  99. #expect(profile.maxIob == 0)
  100. #expect(profile.dia == 10)
  101. #expect(profile.sens == 100)
  102. #expect(profile.currentBasal == 1)
  103. #expect(profile.maxBg == 80)
  104. #expect(profile.minBg == 80)
  105. #expect(profile.carbRatio == 20)
  106. #expect(profile.temptargetSet == true)
  107. }
  108. @Test("Profile with expired temp target should use default values") func testProfileWithExpiredTempTarget() throws {
  109. var inputs = createBaseInputs()
  110. // Create temp target 90 minutes ago
  111. let currentTime = Date()
  112. let creationDate = currentTime.addingTimeInterval(-90 * 60)
  113. let tempTarget = TempTarget(
  114. name: "Eating Soon",
  115. createdAt: creationDate,
  116. targetTop: 80,
  117. targetBottom: 80,
  118. duration: 20,
  119. enteredBy: "Test",
  120. reason: "Eating Soon",
  121. isPreset: nil,
  122. enabled: nil,
  123. halfBasalTarget: nil
  124. )
  125. inputs.6 = [tempTarget]
  126. let profile = try ProfileGenerator.generate(
  127. pumpSettings: inputs.0,
  128. bgTargets: inputs.1,
  129. basalProfile: inputs.2,
  130. isf: inputs.3,
  131. preferences: inputs.4,
  132. carbRatios: inputs.5,
  133. tempTargets: inputs.6,
  134. model: inputs.7,
  135. clock: currentTime
  136. )
  137. #expect(profile.maxIob == 0)
  138. #expect(profile.dia == 10)
  139. #expect(profile.sens == 100)
  140. #expect(profile.currentBasal == 1)
  141. #expect(profile.maxBg == 100)
  142. #expect(profile.minBg == 100)
  143. #expect(profile.carbRatio == 20)
  144. }
  145. @Test("Profile with zero duration temp target should use default values") func testProfileWithZeroDurationTempTarget() throws {
  146. var inputs = createBaseInputs()
  147. // Create temp target 5 minutes ago with 0 duration
  148. let currentTime = Date()
  149. let creationDate = currentTime.addingTimeInterval(-5 * 60)
  150. let tempTarget = TempTarget(
  151. name: "Eating Soon",
  152. createdAt: creationDate,
  153. targetTop: 80,
  154. targetBottom: 80,
  155. duration: 0,
  156. enteredBy: "Test",
  157. reason: "Eating Soon",
  158. isPreset: nil,
  159. enabled: nil,
  160. halfBasalTarget: nil
  161. )
  162. inputs.6 = [tempTarget]
  163. let profile = try ProfileGenerator.generate(
  164. pumpSettings: inputs.0,
  165. bgTargets: inputs.1,
  166. basalProfile: inputs.2,
  167. isf: inputs.3,
  168. preferences: inputs.4,
  169. carbRatios: inputs.5,
  170. tempTargets: inputs.6,
  171. model: inputs.7,
  172. clock: currentTime
  173. )
  174. #expect(profile.maxIob == 0)
  175. #expect(profile.dia == 10)
  176. #expect(profile.sens == 100)
  177. #expect(profile.currentBasal == 1)
  178. #expect(profile.maxBg == 100)
  179. #expect(profile.minBg == 100)
  180. #expect(profile.carbRatio == 20)
  181. }
  182. @Test("Profile generation with invalid DIA should throw error") func testInvalidDIA() throws {
  183. var inputs = createBaseInputs()
  184. inputs.0 = PumpSettings(
  185. insulinActionCurve: 1,
  186. maxBolus: 10,
  187. maxBasal: 2
  188. )
  189. #expect(throws: ProfileError.invalidDIA(value: 1)) {
  190. _ = try ProfileGenerator.generate(
  191. pumpSettings: inputs.0,
  192. bgTargets: inputs.1,
  193. basalProfile: inputs.2,
  194. isf: inputs.3,
  195. preferences: inputs.4,
  196. carbRatios: inputs.5,
  197. tempTargets: inputs.6,
  198. model: inputs.7,
  199. clock: Date()
  200. )
  201. }
  202. }
  203. @Test("Profile generation with zero basal rate should throw error") func testCurrentBasalZero() throws {
  204. var inputs = createBaseInputs()
  205. inputs.2 = [
  206. BasalProfileEntry(start: "00:00", minutes: 0, rate: 0.0)
  207. ]
  208. // the reason it throws this error is due to some complex logic
  209. // in Javascript around the handling of nil and 0 basal rate entries
  210. #expect(throws: ProfileError.invalidMaxDailyBasal(value: 0)) {
  211. _ = try ProfileGenerator.generate(
  212. pumpSettings: inputs.0,
  213. bgTargets: inputs.1,
  214. basalProfile: inputs.2,
  215. isf: inputs.3,
  216. preferences: inputs.4,
  217. carbRatios: inputs.5,
  218. tempTargets: inputs.6,
  219. model: inputs.7,
  220. clock: Date()
  221. )
  222. }
  223. }
  224. @Test("Profile should store model string correctly") func testModelString() throws {
  225. var inputs = createBaseInputs()
  226. inputs.7 = "\"554\"\n"
  227. let profile = try ProfileGenerator.generate(
  228. pumpSettings: inputs.0,
  229. bgTargets: inputs.1,
  230. basalProfile: inputs.2,
  231. isf: inputs.3,
  232. preferences: inputs.4,
  233. carbRatios: inputs.5,
  234. tempTargets: inputs.6,
  235. model: inputs.7,
  236. clock: Date()
  237. )
  238. #expect(profile.model == "554")
  239. }
  240. }