TempTargetStorageTests.swift 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import CoreData
  2. import Foundation
  3. import Swinject
  4. import Testing
  5. @testable import Trio
  6. @Suite("TempTargetStorage Tests", .serialized) struct TempTargetsStorageTests: Injectable {
  7. @Injected() var storage: TempTargetsStorage!
  8. let resolver: Resolver
  9. var coreDataStack: CoreDataStack!
  10. var testContext: NSManagedObjectContext!
  11. init() async throws {
  12. // Create test context
  13. coreDataStack = try await CoreDataStack.createForTests()
  14. testContext = coreDataStack.newTaskContext()
  15. // Create assembler with test assembly
  16. let assembler = Assembler([
  17. StorageAssembly(),
  18. ServiceAssembly(),
  19. APSAssembly(),
  20. NetworkAssembly(),
  21. UIAssembly(),
  22. SecurityAssembly(),
  23. TestAssembly(testContext: testContext) // Add our test assembly last to override TempTargetStorage
  24. ])
  25. resolver = assembler.resolver
  26. injectServices(resolver)
  27. }
  28. @Test("Storage is correctly initialized") func testStorageInitialization() {
  29. // Verify storage exists
  30. #expect(storage != nil, "TempTargetsStorage should be injected")
  31. // Verify it's the correct type
  32. #expect(
  33. storage is BaseTempTargetsStorage, "Storage should be of type BaseTempTargetsStorage"
  34. )
  35. }
  36. @Test("Store and retrieve temp target") func testStoreAndRetrieveTempTarget() async throws {
  37. // Given
  38. let testTarget = TempTarget(
  39. name: "Test Target",
  40. createdAt: Date(),
  41. targetTop: 120,
  42. targetBottom: 120,
  43. duration: 60,
  44. enteredBy: "Test",
  45. reason: "Testing",
  46. isPreset: false,
  47. halfBasalTarget: 160
  48. )
  49. // When
  50. try await storage.storeTempTarget(tempTarget: testTarget)
  51. // Then verify stored entries
  52. let storedEntries = try await coreDataStack.fetchEntitiesAsync(
  53. ofType: TempTargetStored.self,
  54. onContext: testContext,
  55. predicate: NSPredicate(format: "name == %@", "Test Target"),
  56. key: "date",
  57. ascending: false
  58. ) as? [TempTargetStored]
  59. #expect(storedEntries?.isEmpty == false, "Should have stored entries")
  60. #expect(storedEntries?.count == 1, "Should have exactly one entry")
  61. let storedTarget = storedEntries?.first
  62. #expect(storedTarget?.name == "Test Target", "Name should match")
  63. #expect(storedTarget?.target == 120, "Target should match")
  64. #expect(storedTarget?.duration == 60, "Duration should match")
  65. }
  66. @Test("Delete temp target Preset") func testDeleteTempTarget() async throws {
  67. // Given
  68. let testTarget = TempTarget(
  69. name: "Delete Test",
  70. createdAt: Date(),
  71. targetTop: 120,
  72. targetBottom: 120,
  73. duration: 60,
  74. enteredBy: "Test",
  75. reason: "Testing deletion of a preset",
  76. isPreset: true,
  77. halfBasalTarget: 160
  78. )
  79. // Store the target
  80. try await storage.storeTempTarget(tempTarget: testTarget)
  81. // Get the stored target's ObjectID
  82. let storedEntries = try await coreDataStack.fetchEntitiesAsync(
  83. ofType: TempTargetStored.self,
  84. onContext: testContext,
  85. predicate: NSPredicate(format: "name == %@", "Delete Test"),
  86. key: "date",
  87. ascending: false
  88. ) as? [TempTargetStored]
  89. guard let objectID = storedEntries?.first?.objectID else {
  90. throw TestError("Failed to get stored target's ObjectID")
  91. }
  92. // When
  93. await storage.deleteTempTargetPreset(objectID)
  94. // Then verify deletion
  95. let remainingEntries = try await coreDataStack.fetchEntitiesAsync(
  96. ofType: TempTargetStored.self,
  97. onContext: testContext,
  98. predicate: NSPredicate(format: "name == %@", "Delete Test"),
  99. key: "date",
  100. ascending: false
  101. ) as? [TempTargetStored]
  102. #expect(remainingEntries?.isEmpty == true, "Should have no entries after deletion")
  103. }
  104. @Test("Get temp targets not yet uploaded to Nightscout") func testGetTempTargetsNotYetUploadedToNightscout() async throws {
  105. // Given
  106. let testTarget = TempTarget(
  107. name: "NS Test",
  108. createdAt: Date(),
  109. targetTop: 120,
  110. targetBottom: 120,
  111. duration: 45,
  112. enteredBy: "Test",
  113. reason: "Testing NS Upload",
  114. isPreset: true,
  115. enabled: true,
  116. halfBasalTarget: 160
  117. )
  118. // When
  119. try await storage.storeTempTarget(tempTarget: testTarget)
  120. let notUploadedTargets = try await storage.getTempTargetsNotYetUploadedToNightscout()
  121. // Then
  122. #expect(!notUploadedTargets.isEmpty, "Should have targets not uploaded to NS")
  123. let target = notUploadedTargets[0]
  124. #expect(target.eventType == .nsTempTarget, "Event type should be NS temp target")
  125. #expect(target.duration == 45, "Duration should match")
  126. #expect(target.targetTop == 120, "Target top should match target")
  127. #expect(target.targetBottom == 120, "Target bottom should match target")
  128. }
  129. @Test(
  130. "Main chart predicate excludes presets saved after the predicate was built"
  131. ) func testMainChartPredicateExcludesNewPresets() async throws {
  132. // Given a predicate anchored before the entries exist, like the chart controller keeps between foregrounds
  133. let anchoredPredicate = NSPredicate.tempTargetsForMainChart
  134. let preset = TempTarget(
  135. name: "Chart Preset",
  136. createdAt: Date(),
  137. targetTop: 80,
  138. targetBottom: 80,
  139. duration: 20,
  140. enteredBy: "Test",
  141. reason: "Testing chart predicate",
  142. isPreset: true,
  143. enabled: false,
  144. halfBasalTarget: 160
  145. )
  146. let scheduled = TempTarget(
  147. name: "Chart Scheduled",
  148. createdAt: Date().addingTimeInterval(3600),
  149. targetTop: 90,
  150. targetBottom: 90,
  151. duration: 30,
  152. enteredBy: "Test",
  153. reason: "Testing chart predicate",
  154. isPreset: false,
  155. enabled: false,
  156. halfBasalTarget: 160
  157. )
  158. let running = TempTarget(
  159. name: "Chart Running",
  160. createdAt: Date(),
  161. targetTop: 135,
  162. targetBottom: 135,
  163. duration: 120,
  164. enteredBy: "Test",
  165. reason: "Testing chart predicate",
  166. isPreset: false,
  167. enabled: true,
  168. halfBasalTarget: 160
  169. )
  170. // When
  171. try await storage.storeTempTarget(tempTarget: preset)
  172. try await storage.storeTempTarget(tempTarget: scheduled)
  173. try await storage.storeTempTarget(tempTarget: running)
  174. // Then
  175. let matches = try await coreDataStack.fetchEntitiesAsync(
  176. ofType: TempTargetStored.self,
  177. onContext: testContext,
  178. predicate: anchoredPredicate,
  179. key: "date",
  180. ascending: false
  181. ) as? [TempTargetStored]
  182. let names = matches?.map(\.name)
  183. #expect(names?.contains("Chart Preset") == false, "Never activated preset should not match the chart predicate")
  184. #expect(names?.contains("Chart Scheduled") == true, "Scheduled temp target should match the chart predicate")
  185. #expect(names?.contains("Chart Running") == true, "Running temp target should match the chart predicate")
  186. }
  187. @Test("Chart visibility keeps running and scheduled temp targets only") func testIsVisibleInChart() async throws {
  188. await testContext.perform {
  189. let preset = TempTargetStored(context: testContext)
  190. preset.date = Date()
  191. preset.enabled = false
  192. preset.isPreset = true
  193. let enactedPreset = TempTargetStored(context: testContext)
  194. enactedPreset.date = Date()
  195. enactedPreset.enabled = true
  196. enactedPreset.isPreset = true
  197. let scheduled = TempTargetStored(context: testContext)
  198. scheduled.date = Date().addingTimeInterval(3600)
  199. scheduled.enabled = false
  200. scheduled.isPreset = false
  201. let running = TempTargetStored(context: testContext)
  202. running.date = Date()
  203. running.enabled = true
  204. running.isPreset = false
  205. let cancelled = TempTargetStored(context: testContext)
  206. cancelled.date = Date().addingTimeInterval(-600)
  207. cancelled.enabled = false
  208. cancelled.isPreset = false
  209. #expect(!preset.isVisibleInChart, "Never activated preset should not be visible")
  210. #expect(enactedPreset.isVisibleInChart, "Enacted preset should be visible")
  211. #expect(scheduled.isVisibleInChart, "Scheduled temp target should be visible")
  212. #expect(running.isVisibleInChart, "Running temp target should be visible")
  213. #expect(!cancelled.isVisibleInChart, "Cancelled temp target should not be visible")
  214. }
  215. }
  216. }