polscm32 пре 1 година
родитељ
комит
0a06fa5441

+ 5 - 6
Trio/Sources/APS/Storage/CarbsStorage.swift

@@ -13,7 +13,6 @@ protocol CarbsStorage {
     func storeCarbs(_ carbs: [CarbsEntry], areFetchedFromRemote: Bool) async throws
     func deleteCarbsEntryStored(_ treatmentObjectID: NSManagedObjectID) async
     func syncDate() -> Date
-    func recent() -> [CarbsEntry]
     func getCarbsNotYetUploadedToNightscout() async throws -> [NightscoutTreatment]
     func getFPUsNotYetUploadedToNightscout() async throws -> [NightscoutTreatment]
     func getCarbsNotYetUploadedToHealth() async throws -> [CarbsEntry]
@@ -282,12 +281,12 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
         Date().addingTimeInterval(-1.days.timeInterval)
     }
 
-    func recent() -> [CarbsEntry] {
-        storage.retrieve(OpenAPS.Monitor.carbHistory, as: [CarbsEntry].self)?.reversed() ?? []
-    }
-
     func deleteCarbsEntryStored(_ treatmentObjectID: NSManagedObjectID) async {
-        let taskContext = CoreDataStack.shared.newTaskContext()
+        // Use injected context if available, otherwise create new task context
+        let taskContext = context != CoreDataStack.shared.newTaskContext()
+            ? context
+            : CoreDataStack.shared.newTaskContext()
+
         taskContext.name = "deleteContext"
         taskContext.transactionAuthor = "deleteCarbs"
 

+ 19 - 8
Trio/Sources/Services/RemoteControl/TrioRemoteControl+Meal.swift

@@ -41,15 +41,26 @@ extension TrioRemoteControl {
         }
 
         let pushMessageDate = Date(timeIntervalSince1970: pushMessage.timestamp)
-        let recentCarbEntries = carbsStorage.recent()
-        let carbsAfterPushMessage = recentCarbEntries.filter { $0.createdAt > pushMessageDate }
+        let taskContext = CoreDataStack.shared.newTaskContext()
+        let results = try await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: CarbEntryStored.self,
+            onContext: taskContext,
+            predicate: NSPredicate(format: "createdAt > %@", pushMessageDate as NSDate),
+            key: "createdAt",
+            ascending: false
+        )
 
-        if !carbsAfterPushMessage.isEmpty {
-            await logError(
-                "Command rejected: newer carb entries have been logged since the command was sent.",
-                pushMessage: pushMessage
-            )
-            return
+        await taskContext.perform {
+            guard let recentCarbEntries = results as? [CarbEntryStored] else { return }
+            if !recentCarbEntries.isEmpty {
+                Task {
+                    await self.logError(
+                        "Command rejected: newer carb entries have been logged since the command was sent.",
+                        pushMessage: pushMessage
+                    )
+                    return
+                }
+            }
         }
 
         let actualDate: Date?

+ 170 - 7
TrioTests/CoreDataTests/CarbsStorageTests.swift

@@ -23,7 +23,7 @@ import Testing
             NetworkAssembly(),
             UIAssembly(),
             SecurityAssembly(),
-            TestAssembly(testContext: testContext) // Add our test assembly last to override CarbsStorage
+            TestAssembly(testContext: testContext)
         ])
 
         resolver = assembler.resolver
@@ -31,15 +31,178 @@ import Testing
     }
 
     @Test("Storage is correctly initialized") func testStorageInitialization() {
-        // Verify storage exists
         #expect(storage != nil, "CarbsStorage should be injected")
+        #expect(storage is BaseCarbsStorage, "Storage should be of type BaseCarbsStorage")
+        #expect(storage.updatePublisher != nil, "Update publisher should be available")
+    }
 
-        // Verify it's the correct type
-        #expect(
-            storage is BaseCarbsStorage, "Storage should be of type BaseCarbsStorage"
+    @Test("Store and retrieve carbs entries") func testStoreAndRetrieveCarbs() async throws {
+        // Given
+        let testEntries = [
+            CarbsEntry(
+                id: UUID().uuidString,
+                createdAt: Date(),
+                actualDate: Date(),
+                carbs: 20,
+                fat: 0,
+                protein: 0,
+                note: "Test meal",
+                enteredBy: "Test",
+                isFPU: false,
+                fpuID: nil
+            )
+        ]
+
+        // When
+        try await storage.storeCarbs(testEntries, areFetchedFromRemote: false)
+        let recentEntries = try await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: CarbEntryStored.self,
+            onContext: testContext,
+            predicate: NSPredicate(format: "TRUEPREDICATE"),
+            key: "date",
+            ascending: false
         )
 
-        // Verify we can access the update publisher
-        #expect(storage.updatePublisher != nil, "Update publisher should be available")
+        guard let recentEntries = recentEntries as? [CarbEntryStored] else {
+            throw TestError("Failed to get recent entries")
+        }
+
+        // Then
+        #expect(!recentEntries.isEmpty, "Should have stored entries")
+        #expect(recentEntries.count == 1, "Should have exactly one entry")
+        #expect(recentEntries[0].carbs == 20, "Carbs value should match")
+        #expect(recentEntries[0].fat == 0, "Fat value should match")
+        #expect(recentEntries[0].protein == 0, "Protein value should match")
+        #expect(recentEntries[0].note == "Test meal", "Note should match")
+    }
+
+    @Test("Delete carbs entry") func testDeleteCarbsEntry() async throws {
+        // Given
+        let testEntry = CarbsEntry(
+            id: UUID().uuidString,
+            createdAt: Date(),
+            actualDate: Date(),
+            carbs: 30,
+            fat: nil,
+            protein: nil,
+            note: "Delete test",
+            enteredBy: "Test",
+            isFPU: false,
+            fpuID: nil
+        )
+
+        // When
+        try await storage.storeCarbs([testEntry], areFetchedFromRemote: false)
+
+        // Get the stored entry's ObjectID
+        let storedEntries = try await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: CarbEntryStored.self,
+            onContext: testContext,
+            predicate: NSPredicate(format: "carbs == 30"),
+            key: "date",
+            ascending: false
+        ) as? [CarbEntryStored]
+
+        guard let objectID = storedEntries?.first?.objectID else {
+            throw TestError("Failed to get stored entry's ObjectID")
+        }
+
+        // Delete the entry
+        await storage.deleteCarbsEntryStored(objectID)
+
+        // Then - verify deletion
+        let remainingEntries = try await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: CarbEntryStored.self,
+            onContext: testContext,
+            predicate: NSPredicate(format: "carbs == 30"),
+            key: "date",
+            ascending: false
+        ) as? [CarbEntryStored]
+
+        #expect(remainingEntries?.isEmpty == true, "Should have no entries after deletion")
+    }
+
+    @Test("Get carbs not yet uploaded to Nightscout") func testGetCarbsNotYetUploadedToNightscout() async throws {
+        // Given
+        let testEntry = CarbsEntry(
+            id: UUID().uuidString,
+            createdAt: Date(),
+            actualDate: Date(),
+            carbs: 40,
+            fat: nil,
+            protein: nil,
+            note: "NS test",
+            enteredBy: "Test",
+            isFPU: false,
+            fpuID: nil
+        )
+
+        // When
+        try await storage.storeCarbs([testEntry], areFetchedFromRemote: false)
+        let notUploadedEntries = try await storage.getCarbsNotYetUploadedToNightscout()
+
+        // Then
+        #expect(!notUploadedEntries.isEmpty, "Should have entries not uploaded to NS")
+        #expect(notUploadedEntries[0].carbs == 40, "Carbs value should match")
+    }
+
+    @Test("Get FPUs not yet uploaded to Nightscout") func testGetFPUsNotYetUploadedToNightscout() async throws {
+        // Given
+        let fpuID = UUID().uuidString
+        let testEntry = CarbsEntry(
+            id: UUID().uuidString,
+            createdAt: Date(),
+            actualDate: Date(),
+            carbs: 30,
+            fat: 20,
+            protein: 10,
+            note: "FPU test",
+            enteredBy: "Test",
+            isFPU: false,
+            fpuID: fpuID
+        )
+
+        // When
+        try await storage.storeCarbs([testEntry], areFetchedFromRemote: false)
+
+        // First verify all stored entries
+        let allStoredEntries = try await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: CarbEntryStored.self,
+            onContext: testContext,
+            predicate: NSPredicate(format: "fpuID == %@", fpuID),
+            key: "date",
+            ascending: true
+        ) as? [CarbEntryStored]
+
+        // Then verify the stored entries
+        #expect(allStoredEntries?.isEmpty == false, "Should have stored entries")
+        #expect(allStoredEntries?.count ?? 0 > 1, "Should have multiple entries due to FPU splitting")
+
+        // Original carb-non-fpu entry should be stored with original fat and protein values and isFPU set to false
+        let carbNonFpuEntry = allStoredEntries?.first(where: { $0.isFPU == false })
+        #expect(carbNonFpuEntry != nil, "Should have one carb non-fpu entry")
+        #expect(carbNonFpuEntry?.carbs == 30, "Original carbs should match")
+        #expect(carbNonFpuEntry?.protein == 10, "Original carbs should match")
+        #expect(carbNonFpuEntry?.fat == 20, "Original carbs should match")
+
+        // Additional carb-fpu entries should be created for fat/protein with isFPU set to true and the carbs set to the amount of each carbEquivalent
+        let carbFpuEntry = allStoredEntries?.filter { $0.isFPU == true }
+        #expect(carbFpuEntry?.isEmpty == false, "Should have additional carb-fpu entries")
+
+        // Now test the Nightscout upload function
+        let notUploadedFPUs = try await storage.getFPUsNotYetUploadedToNightscout()
+
+        // Then verify Nightscout entries
+        #expect(!notUploadedFPUs.isEmpty, "Should have FPUs not uploaded to NS")
+        let fpu = notUploadedFPUs[0]
+        #expect(fpu.carbs ?? 0 < 30, "Original carbs value should match")
+        #expect(fpu.protein == 0, "Protein value should match")
+        #expect(fpu.fat == 0, "Fat value should match")
+
+        // Verify all entries share the same fpuID
+        #expect(
+            allStoredEntries?.allSatisfy { $0.fpuID?.uuidString == fpuID } == true,
+            "All entries should share the same fpuID"
+        )
     }
 }