| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import CoreData
- import Foundation
- import Swinject
- import Testing
- @testable import Trio
- @Suite("GlucoseStorage Tests", .serialized) struct GlucoseStorageTests: Injectable {
- @Injected() var storage: GlucoseStorage!
- let resolver: Resolver
- var coreDataStack: CoreDataStack!
- var testContext: NSManagedObjectContext!
- init() async throws {
- // Create test context
- // As we are only using this single test context to initialize our in-memory DeterminationStorage we need to perform the Unit Tests serialized
- coreDataStack = try await CoreDataStack.createForTests()
- testContext = coreDataStack.newTaskContext()
- // Create assembler with test assembly
- let assembler = Assembler([
- StorageAssembly(),
- ServiceAssembly(),
- APSAssembly(),
- NetworkAssembly(),
- UIAssembly(),
- SecurityAssembly(),
- TestAssembly(testContext: testContext) // Add our test assembly last to override Storage
- ])
- resolver = assembler.resolver
- injectServices(resolver)
- }
- @Test("Storage is correctly initialized") func testStorageInitialization() {
- // Verify storage exists
- #expect(storage != nil, "GlucoseStorage should be injected")
- // Verify it's the correct type
- #expect(storage is BaseGlucoseStorage, "Storage should be of type BaseGlucoseStorage")
- }
- @Test("Store and retrieve glucose entries") func testStoreAndRetrieveGlucose() async throws {
- // Given
- let testGlucose = [
- BloodGlucose(direction: BloodGlucose.Direction.flat, date: 123, dateString: Date(), glucose: 126)
- ]
- // When
- try await storage.storeGlucose(testGlucose)
- // Then verify stored entries
- let storedEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: GlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 126"),
- key: "date",
- ascending: false
- ) as? [GlucoseStored]
- #expect(storedEntries?.isEmpty == false, "Should have stored entries")
- #expect(storedEntries?.count == 1, "Should have exactly one entry")
- #expect(storedEntries?[0].glucose == 126, "Glucose value should match")
- #expect(storedEntries?[0].direction == "Flat", "Direction should match")
- }
- @Test("Delete glucose entry") func testDeleteGlucose() async throws {
- // Given
- let testGlucose = [
- BloodGlucose(direction: BloodGlucose.Direction.flat, date: 123, dateString: Date(), glucose: 140)
- ]
- try await storage.storeGlucose(testGlucose)
- // Get the stored entry's ObjectID
- let storedEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: GlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 140"),
- key: "date",
- ascending: false
- ) as? [GlucoseStored]
- guard let objectID = storedEntries?.first?.objectID else {
- throw TestError("Failed to get stored entry's ObjectID")
- }
- #expect(storedEntries.isNotNilNotEmpty == true, "Should have exactly one (test) entry")
- // When
- await storage.deleteGlucose(objectID)
- // Then verify deletion
- let remainingEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: GlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 140"),
- key: "date",
- ascending: false
- ) as? [GlucoseStored]
- #expect(remainingEntries?.isEmpty == true, "Should have no entries after deletion")
- // Finally verify that it stored a copy
- let archivedEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: DeletedGlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 140"),
- key: "date",
- ascending: false
- ) as? [DeletedGlucoseStored]
- #expect(archivedEntries?.isEmpty == false, "Should have archived entries after deletion")
- }
- @Test("Get glucose not yet uploaded to Nightscout") func testGetGlucoseNotYetUploadedToNightscout() async throws {
- // Given
- let testGlucose = [
- BloodGlucose(direction: BloodGlucose.Direction.flat, date: 123, dateString: Date(), glucose: 160)
- ]
- try await storage.storeGlucose(testGlucose)
- // When
- let notUploadedEntries = try await storage.getGlucoseNotYetUploadedToNightscout()
- // Then
- #expect(!notUploadedEntries.isEmpty, "Should have entries not uploaded to NS")
- #expect(notUploadedEntries[0].glucose == 160, "Glucose value should match")
- }
- @Test(
- "Test glucose alarms",
- .enabled(if: false, "Flaky test, disabled while investigating")
- ) func testGlucoseAlarms() async throws {
- // Given
- let lowGlucose = [
- BloodGlucose(direction: BloodGlucose.Direction.flat, date: 123, dateString: Date(), glucose: 55)
- ]
- let highGlucose = [
- BloodGlucose(direction: BloodGlucose.Direction.flat, date: 123, dateString: Date(), glucose: 271)
- ]
- let normalGlucose = [
- BloodGlucose(direction: BloodGlucose.Direction.flat, date: 123, dateString: Date(), glucose: 100)
- ]
- // When - Test low glucose
- try await storage.storeGlucose(lowGlucose)
- var storedEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: GlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 55"),
- key: "date",
- ascending: false
- ) as? [GlucoseStored]
- // Then
- #expect(storedEntries?.first?.glucose == 55, "Low glucose value should match")
- #expect(storage.alarm == .low, "Should trigger low glucose alarm") // default low limit is 72 mg/dL
- // When - Test high glucose
- try await storage.storeGlucose(highGlucose)
- storedEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: GlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 271"),
- key: "date",
- ascending: false
- ) as? [GlucoseStored]
- // Then
- #expect(storedEntries?.first?.glucose == 271, "High glucose value should match")
- #expect(storage.alarm == .high, "Should trigger high glucose alarm") // default high limit is 270 mg/dL
- // When - Test normal glucose
- try await storage.storeGlucose(normalGlucose)
- storedEntries = try await coreDataStack.fetchEntitiesAsync(
- ofType: GlucoseStored.self,
- onContext: testContext,
- predicate: NSPredicate(format: "glucose == 100"),
- key: "date",
- ascending: false
- ) as? [GlucoseStored]
- // Then
- #expect(storedEntries?.first?.glucose == 100, "Normal glucose value should match")
- #expect(storage.alarm == nil, "Should not trigger any alarm")
- }
- }
|