DeterminationStorageTests.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import CoreData
  2. import Foundation
  3. import Swinject
  4. import Testing
  5. @testable import Trio
  6. @Suite(.serialized) struct DeterminationStorageTests: Injectable {
  7. @Injected() var storage: DeterminationStorage!
  8. let resolver: Resolver
  9. let coreDataStack = CoreDataStack.createForTests()
  10. let testContext: NSManagedObjectContext
  11. init() {
  12. // Create test context
  13. // As we are only using this single test context to initialize our in-memory DeterminationStorage we need to perform the Unit Tests serialized
  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 Storage
  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, "DeterminationStorage should be injected")
  31. // Verify it's the correct type
  32. #expect(storage is BaseDeterminationStorage, "Storage should be of type BaseDeterminationStorage")
  33. }
  34. @Test("Test fetchLastDeterminationObjectID with different predicates") func testFetchLastDeterminationWithPredicates() async throws {
  35. // Given
  36. let date = Date()
  37. let id = UUID()
  38. // Create a mock determination
  39. await testContext.perform {
  40. let determination = OrefDetermination(context: testContext)
  41. determination.id = id
  42. determination.deliverAt = date
  43. determination.timestamp = date
  44. determination.enacted = true
  45. determination.isUploadedToNS = true
  46. try? testContext.save()
  47. }
  48. // Tests with predicates that we use the most for this function
  49. // 1. Test within 30 minutes
  50. let results = await storage.fetchLastDeterminationObjectID(predicate: NSPredicate.predicateFor30MinAgoForDetermination)
  51. #expect(results.count == 1, "Should find 1 determination within 30 minutes")
  52. // Get NSManagedObjectID from exactDateResults
  53. try await testContext.perform {
  54. do {
  55. guard let results = results.first,
  56. let object = try testContext.existingObject(with: results) as? OrefDetermination
  57. else {
  58. throw TestError("Failed to fetch determination")
  59. }
  60. #expect(object.timestamp == date, "Determination within 30 minutes should have the same timestamp as date")
  61. #expect(object.deliverAt == date, "Determination within 30 minutes should have the same deliverAt as date")
  62. #expect(object.enacted == true, "Determination within 30 minutes should be enacted")
  63. #expect(object.isUploadedToNS == true, "Determination within 30 minutes should be uploaded to NS")
  64. #expect(object.id == id, "Determination within 30 minutes should have the same id")
  65. } catch {
  66. throw TestError("Failed to fetch determination")
  67. }
  68. }
  69. // 2. Test enacted determinations
  70. let enactedPredicate = NSPredicate.enactedDetermination
  71. let enactedResults = await storage.fetchLastDeterminationObjectID(predicate: enactedPredicate)
  72. #expect(enactedResults.count == 1, "Should find 1 enacted determination")
  73. // Get NSManagedObjectID from enactedResults
  74. try await testContext.perform {
  75. do {
  76. guard let results = enactedResults.first,
  77. let object = try testContext.existingObject(with: results) as? OrefDetermination
  78. else {
  79. throw TestError("Failed to fetch determination")
  80. }
  81. #expect(object.enacted == true, "Enacted determination should be enacted")
  82. #expect(object.isUploadedToNS == true, "Enacted determination should be uploaded to NS")
  83. #expect(object.id == id, "Enacted determination should have the same id")
  84. #expect(object.timestamp == date, "Enacted determination should have the same timestamp")
  85. #expect(object.deliverAt == date, "Enacted determination should have the same deliverAt")
  86. // Delete the determination
  87. testContext.delete(object)
  88. try testContext.save()
  89. } catch {
  90. throw TestError("Failed to fetch determination")
  91. }
  92. }
  93. }
  94. // @Test("Test complete forecast hierarchy prefetching") func testForecastHierarchyPrefetching() async throws {
  95. // // Given
  96. // let date = Date()
  97. // let backgroundContext = CoreDataStack.shared.newTaskContext()
  98. // let forecastTypes = ["iob", "cob", "zt", "uam"]
  99. //
  100. // // Create test determination with complete forecast hierarchy
  101. // let determination = OrefDetermination(context: backgroundContext)
  102. // determination.id = UUID()
  103. // determination.deliverAt = date
  104. //
  105. // // Create all forecast types with values
  106. // for type in forecastTypes {
  107. // let forecast = Forecast(context: backgroundContext)
  108. // forecast.id = UUID()
  109. // forecast.date = date
  110. // forecast.type = type
  111. // forecast.orefDetermination = determination
  112. //
  113. // // Add test values
  114. // for i in 0 ..< 5 {
  115. // let value = ForecastValue(context: backgroundContext)
  116. // value.index = Int32(i)
  117. // value.value = Int32(100 + (i * 10))
  118. // value.forecast = forecast
  119. // }
  120. // }
  121. //
  122. // try await backgroundContext.save()
  123. //
  124. // // When - Fetch complete hierarchy
  125. // let request = NSFetchRequest<OrefDetermination>(entityName: "OrefDetermination")
  126. // request.predicate = NSPredicate(format: "SELF = %@", determination.objectID)
  127. // request.relationshipKeyPathsForPrefetching = ["forecasts", "forecasts.forecastValues"]
  128. //
  129. // let fetchedDetermination = try await backgroundContext.perform {
  130. // try request.execute().first
  131. // }
  132. //
  133. // // Then
  134. // #expect(fetchedDetermination != nil)
  135. // #expect(fetchedDetermination?.forecasts?.count == 4)
  136. //
  137. // let forecasts = fetchedDetermination?.forecasts?.allObjects as? [Forecast] ?? []
  138. // for forecast in forecasts {
  139. // #expect(forecastTypes.contains(forecast.type ?? ""))
  140. // #expect(forecast.forecastValues?.count == 5)
  141. //
  142. // let values = forecast.forecastValuesArray
  143. // #expect(values.count == 5)
  144. // for (index, value) in values.enumerated() {
  145. // #expect(value.value == Int32(100 + (index * 10)))
  146. // }
  147. // }
  148. //
  149. // // Cleanup
  150. // await backgroundContext.perform {
  151. // backgroundContext.delete(determination)
  152. // try? backgroundContext.save()
  153. // }
  154. // }
  155. // @Test("Test forecast handling with multiple forecast types") func testMultipleForecastTypes() async throws {
  156. // // Given
  157. // let date = Date()
  158. // let backgroundContext = CoreDataStack.shared.newTaskContext()
  159. // var determinationId: NSManagedObjectID?
  160. // let forecastTypes = ["iob", "cob", "zt", "uam"]
  161. // var forecastIds: [NSManagedObjectID] = []
  162. //
  163. // // Create test data with multiple forecast types
  164. // await backgroundContext.perform {
  165. // let determination = OrefDetermination(context: backgroundContext)
  166. // determination.id = UUID()
  167. // determination.deliverAt = date
  168. //
  169. // // Create forecasts for each type
  170. // for type in forecastTypes {
  171. // let forecast = Forecast(context: backgroundContext)
  172. // forecast.id = UUID()
  173. // forecast.date = date
  174. // forecast.type = type
  175. // forecast.orefDetermination = determination
  176. //
  177. // // Add some values
  178. // for i in 0 ..< 3 {
  179. // let value = ForecastValue(context: backgroundContext)
  180. // value.index = Int32(i)
  181. // value.value = Int32(100 + i * 10)
  182. // value.forecast = forecast
  183. // }
  184. //
  185. // forecastIds.append(forecast.objectID)
  186. // }
  187. //
  188. // try? backgroundContext.save()
  189. // determinationId = determination.objectID
  190. // }
  191. //
  192. // guard let determinationId = determinationId else {
  193. // throw TestError("Failed to create test data")
  194. // }
  195. //
  196. // // When - Fetch all forecasts
  197. // let allForecastIds = await storage.getForecastIDs(for: determinationId, in: backgroundContext)
  198. //
  199. // // Then
  200. // #expect(allForecastIds.count == forecastTypes.count, "Should have found all forecast types")
  201. //
  202. // // Test each forecast type
  203. // for forecastId in forecastIds {
  204. // // When - Fetch values for this forecast
  205. // let valueIds = await storage.getForecastValueIDs(for: forecastId, in: backgroundContext)
  206. //
  207. // // Then
  208. // #expect(valueIds.count == 3, "Each forecast should have 3 values")
  209. //
  210. // // When - Fetch complete objects
  211. // let (_, forecast, values) = await storage.fetchForecastObjects(
  212. // for: (UUID(), forecastId, valueIds),
  213. // in: backgroundContext
  214. // )
  215. //
  216. // // Then
  217. // #expect(forecast != nil, "Should have found the forecast")
  218. // #expect(forecastTypes.contains(forecast?.type ?? ""), "Should have valid forecast type")
  219. // #expect(values.count == 3, "Should have all forecast values")
  220. // }
  221. //
  222. // // Test Nightscout format with multiple forecasts
  223. // let nsFormat = await storage.getOrefDeterminationNotYetUploadedToNightscout([determinationId])
  224. // #expect(nsFormat?.predictions?.iob?.count == 3, "Should have IOB predictions")
  225. // #expect(nsFormat?.predictions?.cob?.count == 3, "Should have COB predictions")
  226. // #expect(nsFormat?.predictions?.zt?.count == 3, "Should have ZT predictions")
  227. // #expect(nsFormat?.predictions?.uam?.count == 3, "Should have UAM predictions")
  228. //
  229. // // Cleanup
  230. // await CoreDataStack.shared.deleteObject(identifiedBy: determinationId)
  231. // }
  232. }