MinimedPumpManagerTests.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. //
  2. // MinimedPumpManagerTests.swift
  3. // MinimedKitTests
  4. //
  5. // Created by Pete Schwamb on 5/3/20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import XCTest
  9. import RileyLinkBLEKit
  10. @testable import MinimedKit
  11. import LoopKit
  12. class MinimedPumpManagerTests: XCTestCase {
  13. var rlProvider: MockRileyLinkProvider!
  14. var mockPumpManagerDelegate: MockPumpManagerDelegate!
  15. var pumpManager: MinimedPumpManager!
  16. var mockMessageSender: MockPumpMessageSender!
  17. // Date simulation
  18. private var dateFormatter = ISO8601DateFormatter()
  19. private var simulatedDate: Date = ISO8601DateFormatter().date(from: "2023-01-06T23:45:57Z")!
  20. private var dateSimulationOffset: TimeInterval = 0
  21. private func setSimulatedDate(from dateString: String) {
  22. simulatedDate = dateFormatter.date(from: dateString)!
  23. dateSimulationOffset = 0
  24. }
  25. private func timeTravel(_ time: TimeInterval) {
  26. dateSimulationOffset += time
  27. }
  28. private func dateGenerator() -> Date {
  29. return self.simulatedDate + dateSimulationOffset
  30. }
  31. override func setUpWithError() throws {
  32. let device = MockRileyLinkDevice()
  33. rlProvider = MockRileyLinkProvider(devices: [device])
  34. let rlManagerState = RileyLinkConnectionState(autoConnectIDs: [])
  35. mockMessageSender = MockPumpMessageSender()
  36. let pumpID = "636781"
  37. mockMessageSender.pumpID = pumpID
  38. let state = MinimedPumpManagerState(
  39. isOnboarded: true,
  40. useMySentry: true,
  41. pumpColor: .blue,
  42. pumpID: pumpID,
  43. pumpModel: .model522,
  44. pumpFirmwareVersion: "VER 2.4A1.1",
  45. pumpRegion: .northAmerica,
  46. rileyLinkConnectionState: rlManagerState,
  47. timeZone: .currentFixed,
  48. suspendState: .resumed(Date()),
  49. insulinType: .novolog,
  50. lastTuned: simulatedDate,
  51. lastValidFrequency: nil,
  52. basalSchedule: BasalSchedule(entries: [BasalScheduleEntry(index: 0, timeOffset: 0, rate: 1.0)]))
  53. var pumpState = state.pumpState
  54. pumpState.awakeUntil = .distantFuture
  55. let pumpOps = MockPumpOps(pumpState: pumpState, pumpSettings: state.pumpSettings, messageSender: mockMessageSender)
  56. pumpManager = MinimedPumpManager(state: state, rileyLinkDeviceProvider: rlProvider, pumpOps: pumpOps, dateGenerator: dateGenerator)
  57. mockPumpManagerDelegate = MockPumpManagerDelegate()
  58. pumpManager.pumpManagerDelegate = mockPumpManagerDelegate
  59. }
  60. func testBolusWithInvalidResponse() {
  61. let exp = expectation(description: "enactBolus callback")
  62. pumpManager.enactBolus(units: 2.3, activationType: .manualNoRecommendation) { error in
  63. XCTAssertNotNil(error)
  64. exp.fulfill()
  65. }
  66. waitForExpectations(timeout: 2)
  67. }
  68. func testBolusWithUncertainResponseIsReported() {
  69. mockMessageSender.responses = [
  70. .readPumpStatus: [mockMessageSender.makeMockResponse(.readPumpStatus, ReadPumpStatusMessageBody(bolusing: false, suspended: false))],
  71. .bolus: [mockMessageSender.ack], // Second ack missing will cause PumpOpsError.noReponse during second exchange
  72. ]
  73. let exp = expectation(description: "enactBolus callback")
  74. pumpManager.enactBolus(units: 2.3, activationType: .manualNoRecommendation) { error in
  75. XCTAssertNotNil(error)
  76. exp.fulfill()
  77. }
  78. waitForExpectations(timeout: 2)
  79. XCTAssertEqual(mockPumpManagerDelegate.reportedPumpEvents.count, 1)
  80. let report = mockPumpManagerDelegate.reportedPumpEvents.first!
  81. XCTAssertEqual(report.events.count, 1)
  82. let event = report.events.first!
  83. XCTAssertEqual(event.type, .bolus)
  84. XCTAssertEqual(event.dose!.deliveredUnits, 2.3)
  85. }
  86. func testPendingBolusRemovedIfMissingFromHistory() {
  87. mockMessageSender.responses = [
  88. .readPumpStatus: [mockMessageSender.makeMockResponse(.readPumpStatus, ReadPumpStatusMessageBody(bolusing: false, suspended: false))],
  89. .bolus: [mockMessageSender.ack, mockMessageSender.ack],
  90. ]
  91. var exp = expectation(description: "enactBolus callback")
  92. pumpManager.enactBolus(units: 3.2, activationType: .manualNoRecommendation) { error in
  93. XCTAssertNil(error)
  94. exp.fulfill()
  95. }
  96. waitForExpectations(timeout: 2)
  97. XCTAssertEqual(mockPumpManagerDelegate.reportedPumpEvents.count, 1)
  98. timeTravel(.minutes(2))
  99. timeTravel(.seconds(8))
  100. // Setup responses for successful history fetch
  101. mockPumpManagerDelegate.historyFetchStartDate = simulatedDate
  102. var dateComponents = DateComponents()
  103. dateComponents.calendar = Calendar(identifier: Calendar.Identifier.gregorian)
  104. dateComponents.year = 2023
  105. dateComponents.month = 1
  106. dateComponents.day = 6
  107. dateComponents.hour = 17
  108. dateComponents.minute = 48
  109. dateComponents.second = 05
  110. let pumpStatusResponse = mockMessageSender.makeMockResponse(.readPumpStatus, ReadPumpStatusMessageBody(bolusing: false, suspended: false))
  111. let historyPageResponsesHex = [
  112. "0116012f6f094617331a306f094617001601306f09461701010100336f29461733002b740946170016012b740946173300304c0a4617001601304c0a461733002b",
  113. "02600a46170016012b600a4617330a2b650a46170016012b650a4617331c2d6a0a46170016012d6a0a461701010100316a2a4617010101002c6f2a461701010100",
  114. "0334742a4617010404001f772a461733002d790a46170016002d790a46170101010031792a4617010101002c422b4617331c30470b461700160130470b46170101",
  115. "04010033472b461733142b4c0b46170016012b4c0b461733002b510b46170016012b510b4617330e29560b461700160129560b4617331c2c5b0b46170016012c5b",
  116. "050b461733102b650b46170016012b650b4617330e2b6a0b46170016012b6a0b461733122f6f0b46170016012f6f0b461733102b740b46170016012b740b461733",
  117. "060c2b790b46170016012b790b4617330e2b420c46170016012b420c4617330030470c461700160130470c461733182d4c0c46170016012d4c0c4617010707000d",
  118. "07502c4617330022510c461700160022510c4617331a295b0c4617001601295b0c4617010101002e652c4617010101002c6a2c461733162d6f0c46170016012d6f",
  119. "080c461733082c740c46170016012c740c461733002b790c46170016012b790c4617330a30420d461700160130420d4617331a2a470d46170016012a470d461701",
  120. "092525000a482d4617331a2d5b0d46170016012d5b0d461701010100315b2d461733142d650d46170016012d650d4617331a2b6a0d46170016012b6a0d46170101",
  121. "0a01002c6f2d461733182b740d46170016012b740d461733002e790d46170016012e790d461733002d510e46170016012d510e461733042d600e46170016012d60",
  122. "0b0e461733002b650e46170016012b650e461733042d6f0e46170016012d6f0e4617330e2d740e46170016012d740e461733022d790e46170016012d790e461733",
  123. "0c002b420f46170016012b420f461733022e470f46170016012e470f461733040e480f46170016010e480f461733082c4c0f46170016012c4c0f4617330c2b510f",
  124. "0d46170016012b510f46173300295b0f4617001601295b0f461733062d600f46170016012d600f4617330a1f650f46170016011f650f461733002c6a0f46170016",
  125. "0e002c6a0f4617010101002c6f2f4617010202002c742f4617010101002c792f461733082e421046170016012e4210461733022b471046170016012b4710461733",
  126. "0f00204c104617001601204c10461733002a601046170016012a6010461733002074104617001601207410461733002b4c1146170016012b4c11461733002b6011",
  127. "9046170016012b6011461700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000656b"
  128. ]
  129. let mockHistoryPageResponses = historyPageResponsesHex.map { hex in
  130. mockMessageSender.makeMockResponse(.getHistoryPage, CarelinkLongMessageBody(rxData: Data(hexadecimalString: hex)!)!)
  131. }
  132. mockMessageSender.responses = [
  133. .getBattery: [mockMessageSender.makeMockResponse(.getBattery, GetBatteryCarelinkMessageBody(status: .normal, volts: 1.44))],
  134. .readPumpStatus: [pumpStatusResponse, pumpStatusResponse],
  135. .readTime: [mockMessageSender.makeMockResponse(.readTime, ReadTimeCarelinkMessageBody(dateComponents: dateComponents))],
  136. .readRemainingInsulin: [mockMessageSender.makeMockResponse(.readRemainingInsulin, ReadRemainingInsulinMessageBody(reservoirVolume: 115.7, insulinBitPackingScale: PumpModel.model522.insulinBitPackingScale))],
  137. .getHistoryPage: [mockMessageSender.ack, mockHistoryPageResponses.first!],
  138. .pumpAck: Array(mockHistoryPageResponses.dropFirst())
  139. ]
  140. exp = expectation(description: "ensureCurrentPumpData callback")
  141. pumpManager.ensureCurrentPumpData { date in
  142. exp.fulfill()
  143. }
  144. waitForExpectations(timeout: 3)
  145. }
  146. }