polscm32 1 gadu atpakaļ
vecāks
revīzija
8b21c6c4ec

+ 0 - 40
Trio.xcodeproj/project.pbxproj

@@ -324,12 +324,6 @@
 		BDA25F1E2D26D5DD00035F34 /* GlucoseChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA25F1D2D26D5D800035F34 /* GlucoseChartView.swift */; };
 		BDA25F202D26D5FE00035F34 /* CarbsInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA25F1F2D26D5FB00035F34 /* CarbsInputView.swift */; };
 		BDA25F222D26D62800035F34 /* BolusInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA25F212D26D62200035F34 /* BolusInputView.swift */; };
-		BDA28BD72D5D5C5500549AFB /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA28BD62D5D5C5500549AFB /* TestError.swift */; };
-		BDA28BD92D5D5C5600549AFB /* TestError 2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA28BD82D5D5C5600549AFB /* TestError 2.swift */; };
-		BDA28BDC2D5D5C7500549AFB /* BolusCalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA28BDB2D5D5C7500549AFB /* BolusCalculatorTests.swift */; };
-		BDA28BDF2D5D5C9600549AFB /* TestAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA28BDE2D5D5C9600549AFB /* TestAssembly.swift */; };
-		BDA28BE12D5D5CB300549AFB /* DeterminationStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA28BE02D5D5CB300549AFB /* DeterminationStorageTests.swift */; };
-		BDA28BE32D5D5CCB00549AFB /* PumpHistoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA28BE22D5D5CCB00549AFB /* PumpHistoryStorageTests.swift */; };
 		BDA6CC882CAF219B00F942F9 /* TempTargetSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA6CC872CAF219800F942F9 /* TempTargetSetup.swift */; };
 		BDA7593E2D37CFC400E649A4 /* CarbEntryEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7593D2D37CFC000E649A4 /* CarbEntryEditorView.swift */; };
 		BDAE40002D372BAD009C12B1 /* WatchState+Requests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDAE3FFF2D372BA8009C12B1 /* WatchState+Requests.swift */; };
@@ -1056,12 +1050,6 @@
 		BDA25F1D2D26D5D800035F34 /* GlucoseChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseChartView.swift; sourceTree = "<group>"; };
 		BDA25F1F2D26D5FB00035F34 /* CarbsInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbsInputView.swift; sourceTree = "<group>"; };
 		BDA25F212D26D62200035F34 /* BolusInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusInputView.swift; sourceTree = "<group>"; };
-		BDA28BD62D5D5C5500549AFB /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
-		BDA28BD82D5D5C5600549AFB /* TestError 2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TestError 2.swift"; sourceTree = "<group>"; };
-		BDA28BDB2D5D5C7500549AFB /* BolusCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusCalculatorTests.swift; sourceTree = "<group>"; };
-		BDA28BDE2D5D5C9600549AFB /* TestAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAssembly.swift; sourceTree = "<group>"; };
-		BDA28BE02D5D5CB300549AFB /* DeterminationStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeterminationStorageTests.swift; sourceTree = "<group>"; };
-		BDA28BE22D5D5CCB00549AFB /* PumpHistoryStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpHistoryStorageTests.swift; sourceTree = "<group>"; };
 		BDA6CC872CAF219800F942F9 /* TempTargetSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargetSetup.swift; sourceTree = "<group>"; };
 		BDA7593D2D37CFC000E649A4 /* CarbEntryEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEntryEditorView.swift; sourceTree = "<group>"; };
 		BDAE3FFF2D372BA8009C12B1 /* WatchState+Requests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatchState+Requests.swift"; sourceTree = "<group>"; };
@@ -2308,14 +2296,10 @@
 		38FCF3EE25E9028E0078B0D1 /* TrioTests */ = {
 			isa = PBXGroup;
 			children = (
-				BDA28BDD2D5D5C8100549AFB /* CoreDataTests */,
-				BDA28BDA2D5D5C6900549AFB /* BolusCalculatorTests */,
 				38FCF3F125E9028E0078B0D1 /* Info.plist */,
 				38FCF3F825E902C20078B0D1 /* FileStorageTests.swift */,
 				CE1F6DD82BADF4620064EB8D /* PluginManagerTests.swift */,
 				CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */,
-				BDA28BD62D5D5C5500549AFB /* TestError.swift */,
-				BDA28BD82D5D5C5600549AFB /* TestError 2.swift */,
 			);
 			path = TrioTests;
 			sourceTree = "<group>";
@@ -2578,24 +2562,6 @@
 			path = Views;
 			sourceTree = "<group>";
 		};
-		BDA28BDA2D5D5C6900549AFB /* BolusCalculatorTests */ = {
-			isa = PBXGroup;
-			children = (
-				BDA28BDB2D5D5C7500549AFB /* BolusCalculatorTests.swift */,
-			);
-			path = BolusCalculatorTests;
-			sourceTree = "<group>";
-		};
-		BDA28BDD2D5D5C8100549AFB /* CoreDataTests */ = {
-			isa = PBXGroup;
-			children = (
-				BDA28BDE2D5D5C9600549AFB /* TestAssembly.swift */,
-				BDA28BE02D5D5CB300549AFB /* DeterminationStorageTests.swift */,
-				BDA28BE22D5D5CCB00549AFB /* PumpHistoryStorageTests.swift */,
-			);
-			path = CoreDataTests;
-			sourceTree = "<group>";
-		};
 		BDDAF9F12D0055CC00B34E7A /* ChartElements */ = {
 			isa = PBXGroup;
 			children = (
@@ -4094,15 +4060,9 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				BDA28BD72D5D5C5500549AFB /* TestError.swift in Sources */,
 				CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */,
-				BDA28BE32D5D5CCB00549AFB /* PumpHistoryStorageTests.swift in Sources */,
-				BDA28BD92D5D5C5600549AFB /* TestError 2.swift in Sources */,
-				BDA28BDC2D5D5C7500549AFB /* BolusCalculatorTests.swift in Sources */,
 				CE1F6DD92BADF4620064EB8D /* PluginManagerTests.swift in Sources */,
-				BDA28BE12D5D5CB300549AFB /* DeterminationStorageTests.swift in Sources */,
 				38FCF3F925E902C20078B0D1 /* FileStorageTests.swift in Sources */,
-				BDA28BDF2D5D5C9600549AFB /* TestAssembly.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 0 - 54
Trio.xcodeproj/xcshareddata/xcschemes/Trio Tests.xcscheme

@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1620"
-   version = "1.7">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES"
-      buildArchitectures = "Automatic">
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      shouldAutocreateTestPlan = "YES">
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "38FCF3EC25E9028E0078B0D1"
-               BuildableName = "TrioTests.xctest"
-               BlueprintName = "TrioTests"
-               ReferencedContainer = "container:Trio.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 2 - 3
Trio/Sources/APS/Storage/DeterminationStorage.swift

@@ -18,10 +18,9 @@ protocol DeterminationStorage {
 
 final class BaseDeterminationStorage: DeterminationStorage, Injectable {
     private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
-    private let context: NSManagedObjectContext
+    private let context = CoreDataStack.shared.newTaskContext()
 
-    init(resolver: Resolver, context: NSManagedObjectContext? = nil) {
-        self.context = context ?? CoreDataStack.shared.newTaskContext()
+    init(resolver: Resolver) {
         injectServices(resolver)
     }
 

+ 2 - 3
Trio/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -25,14 +25,13 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
     @Injected() private var settings: SettingsManager!
 
     private let updateSubject = PassthroughSubject<Void, Never>()
-    private let context: NSManagedObjectContext
+    private let context = CoreDataStack.shared.newTaskContext()
 
     var updatePublisher: AnyPublisher<Void, Never> {
         updateSubject.eraseToAnyPublisher()
     }
 
-    init(resolver: Resolver, context: NSManagedObjectContext? = nil) {
-        self.context = context ?? CoreDataStack.shared.newTaskContext()
+    init(resolver: Resolver) {
         injectServices(resolver)
     }
 

+ 0 - 590
TrioTests/BolusCalculatorTests/BolusCalculatorTests.swift

@@ -1,590 +0,0 @@
-import Foundation
-import Testing
-
-@testable import Trio
-
-@Suite("Bolus Calculator Tests") struct BolusCalculatorTests: Injectable {
-    @Injected() var calculator: BolusCalculationManager!
-    @Injected() var settingsManager: SettingsManager!
-    @Injected() var fileStorage: FileStorage!
-    @Injected() var apsManager: APSManager!
-    let resolver = TrioApp().resolver
-
-    init() {
-        injectServices(resolver)
-    }
-
-    @Test("Calculator is correctly initialized") func testCalculatorInitialization() {
-        #expect(calculator != nil, "BolusCalculationManager should be injected")
-        #expect(calculator is BaseBolusCalculationManager, "Calculator should be of type BaseBolusCalculationManager")
-    }
-
-    @Test("Calculate insulin for standard meal") func testStandardMealCalculation() async throws {
-        // STEP 1: Setup test scenario
-        // We need to provide a CalculationInput struct
-        let carbs: Decimal = 80
-        let currentBG: Decimal = 180 // 80 points above target, should result in 2U correction
-        let deltaBG: Decimal = 5 // Rising trend, should add small correction
-        let target: Decimal = 100
-        let isf: Decimal = 40
-        let carbRatio: Decimal = 10 // Should result in 8U for carbs
-        let iob: Decimal = 1.0 // Should subtract from final result
-        let cob: Int16 = 20
-        let useFattyMealCorrectionFactor: Bool = false
-        let useSuperBolus: Bool = false
-        let fattyMealFactor: Decimal = 0.8
-        let sweetMealFactor: Decimal = 2
-        let basal: Decimal = 1.5
-        let fraction: Decimal = 0.8
-        let maxBolus: Decimal = 10
-
-        // STEP 2: Create calculation input
-        let input = CalculationInput(
-            carbs: carbs,
-            currentBG: currentBG,
-            deltaBG: deltaBG,
-            target: target,
-            isf: isf,
-            carbRatio: carbRatio,
-            iob: iob,
-            cob: cob,
-            useFattyMealCorrectionFactor: useFattyMealCorrectionFactor,
-            fattyMealFactor: fattyMealFactor,
-            useSuperBolus: useSuperBolus,
-            sweetMealFactor: sweetMealFactor,
-            basal: basal,
-            fraction: fraction,
-            maxBolus: maxBolus
-        )
-
-        // STEP 3: Calculate insulin
-        let result = await calculator.calculateInsulin(input: input)
-
-        // STEP 4: Verify results
-        // Expected calculation breakdown:
-        // wholeCob = 80g + 20g COB = 100g
-        // wholeCobInsulin = 100g ÷ 10 g/U = 10U
-        // targetDifference = currentBG - target = 180 - 100 = 80 mg/dL
-        // targetDifferenceInsulin = 80 mg/dL ÷ 40 mg/dL/U = 2U
-        // fifteenMinutesInsulin = 5 mg/dL ÷ 40 mg/dL/U = 0.125U
-        // correctionInsulin = targetDifferenceInsulin = 2U
-        // iobInsulinReduction = 1U
-        // superBolusInsulin = 0U (disabled)
-        // no adjustment for fatty meals (disabled)
-        // wholeCalc = round(wholeCobInsulin + correctionInsulin + fifteenMinutesInsulin - iobInsulinReduction, 3) = 11.125U
-        // insulinCalculated = round(wholeCalc × fraction, 3) = 8.9U
-
-        // Calculate expected values with proper rounding using roundBolus method from the apsManager
-        let wholeCobInsulin = apsManager.roundBolus(amount: Decimal(100) / Decimal(10)) // 10U
-        let targetDifferenceInsulin = apsManager.roundBolus(amount: Decimal(80) / Decimal(40)) // 2U
-        let fifteenMinutesInsulin = apsManager.roundBolus(amount: Decimal(5) / Decimal(40)) // 0.125U
-        let wholeCalc = wholeCobInsulin + targetDifferenceInsulin + fifteenMinutesInsulin - Decimal(1) // 11.125U
-        let expectedInsulinCalculated = apsManager.roundBolus(amount: wholeCalc * fraction) // 8.9U
-
-        #expect(
-            result.insulinCalculated == expectedInsulinCalculated,
-            """
-            Incorrect insulin calculation
-            Expected: \(expectedInsulinCalculated)U
-            Actual: \(result.insulinCalculated)U
-            Components from CalculationResult:
-            - insulinCalculated: \(result.insulinCalculated)U (expected: \(expectedInsulinCalculated)U)
-            - wholeCalc: \(result.wholeCalc)U (expected: \(wholeCalc)U)
-            - correctionInsulin: \(result.correctionInsulin)U (expected: \(targetDifferenceInsulin)U)
-            - iobInsulinReduction: \(result.iobInsulinReduction)U (expected: 1U)
-            - superBolusInsulin: \(result.superBolusInsulin)U (expected: 0U)
-            - targetDifference: \(result.targetDifference) mg/dL (expected: 80 mg/dL)
-            - targetDifferenceInsulin: \(result.targetDifferenceInsulin)U (expected: \(targetDifferenceInsulin)U)
-            - fifteenMinutesInsulin: \(result.fifteenMinutesInsulin)U (expected: \(fifteenMinutesInsulin)U)
-            - wholeCob: \(result.wholeCob)g (expected: 100g)
-            - wholeCobInsulin: \(result.wholeCobInsulin)U (expected: \(wholeCobInsulin)U)
-            """
-        )
-
-        // Verify each component from CalculationResult struct with rounded values
-        #expect(
-            result.insulinCalculated == expectedInsulinCalculated,
-            "Final calculated insulin amount should be \(expectedInsulinCalculated)U"
-        )
-        #expect(result.wholeCalc == wholeCalc, "Total calculation before fraction should be \(wholeCalc)U")
-        #expect(
-            result.correctionInsulin == targetDifferenceInsulin,
-            "Insulin for BG correction should be \(targetDifferenceInsulin)U"
-        )
-        #expect(result.iobInsulinReduction == -1.0, "Absolute IOB reduction amount should be 1U, hence -1U")
-        #expect(result.superBolusInsulin == 0, "Additional insulin for super bolus should be 0U")
-        #expect(result.targetDifference == 80, "Difference from target BG should be 80 mg/dL")
-        #expect(
-            result.targetDifferenceInsulin == targetDifferenceInsulin,
-            "Insulin needed for target difference should be \(targetDifferenceInsulin)U"
-        )
-        #expect(
-            result.fifteenMinutesInsulin == fifteenMinutesInsulin,
-            "Trend-based insulin adjustment should be \(fifteenMinutesInsulin)U"
-        )
-        #expect(result.wholeCob == 100, "Total carbs (COB + new carbs) should be 100g")
-        #expect(result.wholeCobInsulin == wholeCobInsulin, "Insulin for total carbs should be \(wholeCobInsulin)U")
-    }
-
-    @Test("Calculate insulin for fatty meal") func testFattyMealCalculation() async throws {
-        // STEP 1: Setup test scenario
-        // We need to provide a CalculationInput struct
-        let carbs: Decimal = 80
-        let currentBG: Decimal = 180 // 80 points above target, should result in 2U correction
-        let deltaBG: Decimal = 5 // Rising trend, should add small correction
-        let target: Decimal = 100
-        let isf: Decimal = 40
-        let carbRatio: Decimal = 10 // Should result in 8U for carbs
-        let iob: Decimal = 1.0 // Should subtract from final result
-        let cob: Int16 = 20
-        let useFattyMealCorrectionFactor: Bool = true // now set to true
-        let useSuperBolus: Bool = false
-        let fattyMealFactor: Decimal = 0.8
-        let sweetMealFactor: Decimal = 2
-        let basal: Decimal = 1.5
-        let fraction: Decimal = 0.8
-        let maxBolus: Decimal = 10
-
-        // STEP 2: Create calculation input
-        let input = CalculationInput(
-            carbs: carbs,
-            currentBG: currentBG,
-            deltaBG: deltaBG,
-            target: target,
-            isf: isf,
-            carbRatio: carbRatio,
-            iob: iob,
-            cob: cob,
-            useFattyMealCorrectionFactor: useFattyMealCorrectionFactor,
-            fattyMealFactor: fattyMealFactor,
-            useSuperBolus: useSuperBolus,
-            sweetMealFactor: sweetMealFactor,
-            basal: basal,
-            fraction: fraction,
-            maxBolus: maxBolus
-        )
-
-        // STEP 3: Calculate insulin with fatty meal enabled
-        let fattyMealResult = await calculator.calculateInsulin(input: input)
-
-        // STEP 4: Calculate insulin with fatty meal disabled for comparison
-        let standardInput = CalculationInput(
-            carbs: carbs,
-            currentBG: currentBG,
-            deltaBG: deltaBG,
-            target: target,
-            isf: isf,
-            carbRatio: carbRatio,
-            iob: iob,
-            cob: cob,
-            useFattyMealCorrectionFactor: false, // Disabled for comparison
-            fattyMealFactor: fattyMealFactor,
-            useSuperBolus: useSuperBolus,
-            sweetMealFactor: sweetMealFactor,
-            basal: basal,
-            fraction: fraction,
-            maxBolus: maxBolus
-        )
-        let standardResult = await calculator.calculateInsulin(input: standardInput)
-
-        // STEP 5: Verify results
-        // Fatty meal should reduce the insulin amount by the fatty meal factor (0.8)
-        let expectedReduction = fattyMealFactor
-        let actualReduction = Decimal(
-            (Double(fattyMealResult.insulinCalculated) / Double(standardResult.insulinCalculated) * 10.0).rounded() / 10.0
-        )
-
-        #expect(
-            actualReduction == expectedReduction,
-            """
-            Fatty meal calculation incorrect
-            Expected reduction factor: \(expectedReduction)
-            Actual reduction factor: \(actualReduction)
-            Standard calculation: \(standardResult.insulinCalculated)U
-            Fatty meal calculation: \(fattyMealResult.insulinCalculated)U
-            """
-        )
-    }
-
-    @Test("Calculate insulin with super bolus") func testSuperBolusCalculation() async throws {
-        // STEP 1: Setup test scenario
-        // We need to provide a CalculationInput struct
-        let carbs: Decimal = 80
-        let currentBG: Decimal = 180 // 80 points above target, should result in 2U correction
-        let deltaBG: Decimal = 5 // Rising trend, should add small correction
-        let target: Decimal = 100
-        let isf: Decimal = 40
-        let carbRatio: Decimal = 10 // Should result in 8U for carbs
-        let iob: Decimal = 1.0 // Should subtract from final result
-        let cob: Int16 = 20
-        let useFattyMealCorrectionFactor: Bool = false
-        let useSuperBolus: Bool = true // Super bolus enabled
-        let fattyMealFactor: Decimal = 0.8
-        let sweetMealFactor: Decimal = 2
-        let basal: Decimal = 1.5 // Will be added to insulin calculation when super bolus is enabled
-        let fraction: Decimal = 0.8
-        let maxBolus: Decimal = 10
-
-        // STEP 2: Create calculation input with super bolus enabled
-        let input = CalculationInput(
-            carbs: carbs,
-            currentBG: currentBG,
-            deltaBG: deltaBG,
-            target: target,
-            isf: isf,
-            carbRatio: carbRatio,
-            iob: iob,
-            cob: cob,
-            useFattyMealCorrectionFactor: useFattyMealCorrectionFactor,
-            fattyMealFactor: fattyMealFactor,
-            useSuperBolus: useSuperBolus,
-            sweetMealFactor: sweetMealFactor,
-            basal: basal,
-            fraction: fraction,
-            maxBolus: maxBolus
-        )
-
-        // STEP 3: Calculate insulin with super bolus enabled
-        let superBolusResult = await calculator.calculateInsulin(input: input)
-
-        // STEP 4: Calculate insulin with super bolus disabled for comparison
-        let standardInput = CalculationInput(
-            carbs: carbs,
-            currentBG: currentBG,
-            deltaBG: deltaBG,
-            target: target,
-            isf: isf,
-            carbRatio: carbRatio,
-            iob: iob,
-            cob: cob,
-            useFattyMealCorrectionFactor: useFattyMealCorrectionFactor,
-            fattyMealFactor: fattyMealFactor,
-            useSuperBolus: false, // Disabled for comparison
-            sweetMealFactor: sweetMealFactor,
-            basal: basal,
-            fraction: fraction,
-            maxBolus: maxBolus
-        )
-        let standardResult = await calculator.calculateInsulin(input: standardInput)
-
-        // STEP 5: Verify results
-        // Super bolus should add basal rate * sweetMealFactor to the insulin calculation
-        let expectedSuperBolusInsulin = basal * sweetMealFactor
-        #expect(
-            superBolusResult.superBolusInsulin == expectedSuperBolusInsulin,
-            """
-            Super bolus insulin incorrect
-            Expected: \(expectedSuperBolusInsulin)U (basal \(basal)U × sweetMealFactor \(sweetMealFactor))
-            Actual: \(superBolusResult.superBolusInsulin)U
-            """
-        )
-
-        #expect(
-            superBolusResult.insulinCalculated > standardResult.insulinCalculated,
-            """
-            Super bolus calculation incorrect
-            Expected super bolus calculation to be higher than standard
-            Super bolus: \(superBolusResult.insulinCalculated)U
-            Standard: \(standardResult.insulinCalculated)U
-            Difference: \(superBolusResult.insulinCalculated - standardResult.insulinCalculated)U
-            """
-        )
-
-        // The difference should be the difference of super bolus (= standard dose + the basal rate * sweetMealFactor) limited by max bolus, and the standard dose.
-        let actualDifference = (superBolusResult.insulinCalculated - standardResult.insulinCalculated)
-        let expectedDifference = min(superBolusResult.insulinCalculated, maxBolus) - standardResult.insulinCalculated
-        #expect(
-            actualDifference == expectedDifference,
-            """
-            Super bolus difference incorrect
-            Expected difference: min(\(expectedSuperBolusInsulin), \(maxBolus)) U (basal \(basal)U × sweetMealFactor \(sweetMealFactor) + standard dose \(standardResult
-                .insulinCalculated)) - standard dose \(standardResult.insulinCalculated)
-            Actual difference: \(actualDifference)U
-            Standard result: \(standardResult)
-            SuperBolus result: \(superBolusResult)
-            """
-        )
-    }
-
-    @Test("Calculate insulin with zero carbs") func testZeroCarbsCalculation() async throws {
-        // Given
-        let carbs: Decimal = 0
-
-        // When
-        let result = await calculator.handleBolusCalculation(
-            carbs: carbs,
-            useFattyMealCorrection: false,
-            useSuperBolus: false
-        )
-
-        // Then
-        #expect(result.wholeCobInsulin == 0, "Zero carbs should require no insulin for carbs")
-    }
-
-    @Test("Verify settings retrieval") func testGetSettings() async throws {
-        // Given - Save original settings to restore later
-        let originalSettings = settingsManager.settings
-
-        // Setup test settings
-        let expectedUnits = GlucoseUnits.mgdL
-        let expectedFraction: Decimal = 0.7
-        let expectedFattyMealFactor: Decimal = 0.8
-        let expectedSweetMealFactor: Decimal = 2
-        let expectedMaxCarbs: Decimal = 150
-
-        // Update settings through settings manager
-        settingsManager.settings.units = expectedUnits
-        settingsManager.settings.overrideFactor = expectedFraction
-        settingsManager.settings.fattyMealFactor = expectedFattyMealFactor
-        settingsManager.settings.sweetMealFactor = expectedSweetMealFactor
-        settingsManager.settings.maxCarbs = expectedMaxCarbs
-
-        // Save settings to storage
-        fileStorage.save(settingsManager.settings, as: OpenAPS.Settings.settings)
-
-        // When
-        let (units, fraction, fattyMealFactor, sweetMealFactor, maxCarbs) = await getSettings()
-
-        // Then
-        #expect(units == expectedUnits, "Units should match settings")
-        #expect(fraction == expectedFraction, "Override factor should match settings")
-        #expect(fattyMealFactor == expectedFattyMealFactor, "Fatty meal factor should match settings")
-        #expect(sweetMealFactor == expectedSweetMealFactor, "Sweet meal factor should match settings")
-        #expect(maxCarbs == expectedMaxCarbs, "Max carbs should match settings")
-
-        // Cleanup - Restore original settings
-        settingsManager.settings = originalSettings
-        fileStorage.save(originalSettings, as: OpenAPS.Settings.settings)
-    }
-
-    @Test("Verify getCurrentSettingValue returns correct values based on time") func testGetCurrentSettingValue() async throws {
-        // STEP 1: Backup current settings
-        let originalBasalProfile = await fileStorage.retrieveAsync(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self)
-        let originalCarbRatios = await fileStorage.retrieveAsync(OpenAPS.Settings.carbRatios, as: CarbRatios.self)
-        let originalBGTargets = await fileStorage.retrieveAsync(OpenAPS.Settings.bgTargets, as: BGTargets.self)
-        let originalISFValues = await fileStorage.retrieveAsync(
-            OpenAPS.Settings.insulinSensitivities,
-            as: InsulinSensitivities.self
-        )
-
-        // STEP 2: Setup test data with known values
-        // Note: Entries must be sorted by time for the algorithm to work correctly
-        let basalProfile = [
-            BasalProfileEntry(start: "00:00", minutes: 0, rate: 1.0), // 12:00 AM - 6:00 AM: 1.0
-            BasalProfileEntry(start: "06:00", minutes: 360, rate: 1.2), // 6:00 AM - 12:00 PM: 1.2
-            BasalProfileEntry(start: "12:00", minutes: 720, rate: 1.1), // 12:00 PM - 6:00 PM: 1.1
-            BasalProfileEntry(start: "18:00", minutes: 1080, rate: 0.9) // 6:00 PM - 12:00 AM: 0.9
-        ]
-
-        let carbRatios = CarbRatios(
-            units: .grams,
-            schedule: [
-                CarbRatioEntry(start: "00:00", offset: 0, ratio: 10), // 12:00 AM - 12:00 PM: 10
-                CarbRatioEntry(start: "12:00", offset: 720, ratio: 12) // 12:00 PM - 12:00 AM: 12
-            ]
-        )
-
-        let bgTargets = BGTargets(
-            units: .mgdL,
-            userPreferredUnits: .mgdL,
-            targets: [
-                BGTargetEntry(low: 100, high: 120, start: "00:00", offset: 0), // 12:00 AM - 8:00 AM: 100
-                BGTargetEntry(low: 90, high: 110, start: "08:00", offset: 480) // 8:00 AM - 12:00 AM: 90
-            ]
-        )
-
-        let isfValues = InsulinSensitivities(
-            units: .mgdL,
-            userPreferredUnits: .mgdL,
-            sensitivities: [
-                InsulinSensitivityEntry(sensitivity: 40, offset: 0, start: "00:00"), // 12:00 AM - 2:00 PM: 40
-                InsulinSensitivityEntry(sensitivity: 45, offset: 840, start: "14:00") // 2:00 PM - 12:00 AM: 45
-            ]
-        )
-
-        // STEP 3: Store test data
-        fileStorage.save(basalProfile, as: OpenAPS.Settings.basalProfile)
-        fileStorage.save(carbRatios, as: OpenAPS.Settings.carbRatios)
-        fileStorage.save(bgTargets, as: OpenAPS.Settings.bgTargets)
-        fileStorage.save(isfValues, as: OpenAPS.Settings.insulinSensitivities)
-
-        // STEP 4: Define test cases with specific times and expected values
-        // Format: (hour, minute, [setting type: expected value])
-        let testTimes: [(hour: Int, minute: Int, expected: [SettingType: Decimal])] = [
-            // Test midnight values (00:00)
-            (
-                hour: 0, minute: 0,
-                expected: [
-                    .basal: 1.0, // First basal rate
-                    .carbRatio: 10, // First carb ratio
-                    .bgTarget: 100, // First target
-                    .isf: 40 // First ISF
-                ]
-            ),
-            // Test mid-morning values (7:00)
-            (
-                hour: 7, minute: 0,
-                expected: [
-                    .basal: 1.2, // Second basal rate (after 6:00)
-                    .carbRatio: 10, // Still first carb ratio
-                    .bgTarget: 100, // Still first target
-                    .isf: 40 // Still first ISF
-                ]
-            ),
-            // Test afternoon values (15:00)
-            (
-                hour: 15, minute: 0,
-                expected: [
-                    .basal: 1.1, // Third basal rate (after 12:00)
-                    .carbRatio: 12, // Second carb ratio (after 12:00)
-                    .bgTarget: 90, // Second target
-                    .isf: 45 // Second ISF (after 14:00)
-                ]
-            )
-        ]
-
-        // STEP 5: Test each time point
-        for testTime in testTimes {
-            // Create a date object for the test time
-            let calendar = Calendar.current
-            var components = calendar.dateComponents([.year, .month, .day], from: Date())
-            components.hour = testTime.hour
-            components.minute = testTime.minute
-            components.second = 0
-            guard let testDate = calendar.date(from: components) else {
-                throw TestError("Failed to create test date")
-            }
-
-            // Test each setting type at this time
-            for (type, expectedValue) in testTime.expected {
-                // Get the actual value for this setting at the test time
-                let value = await getCurrentSettingValue(for: type, at: testDate)
-
-                // Compare with expected value
-                #expect(
-                    value == expectedValue,
-                    """
-                    Failed at \(testTime.hour):\(String(format: "%02d", testTime.minute))
-                    Setting: \(type)
-                    Expected: \(expectedValue)
-                    Actual: \(value)
-                    """
-                )
-            }
-        }
-
-        // STEP 6: Cleanup - Restore original settings
-        if let originalBasalProfile = originalBasalProfile {
-            fileStorage.save(originalBasalProfile, as: OpenAPS.Settings.basalProfile)
-        }
-        if let originalCarbRatios = originalCarbRatios {
-            fileStorage.save(originalCarbRatios, as: OpenAPS.Settings.carbRatios)
-        }
-        if let originalBGTargets = originalBGTargets {
-            fileStorage.save(originalBGTargets, as: OpenAPS.Settings.bgTargets)
-        }
-        if let originalISFValues = originalISFValues {
-            fileStorage.save(originalISFValues, as: OpenAPS.Settings.insulinSensitivities)
-        }
-    }
-}
-
-// Copied over from BolusCalculationManager as they are not included in the protocol definition (and I don´t want them to be included)
-
-extension BolusCalculatorTests {
-    private enum SettingType {
-        case basal
-        case carbRatio
-        case bgTarget
-        case isf
-    }
-
-    /// Retrieves current settings from the SettingsManager
-    /// - Returns: Tuple containing units, fraction, fattyMealFactor, sweetMealFactor, and maxCarbs settings
-    private func getSettings() async -> (
-        units: GlucoseUnits,
-        fraction: Decimal,
-        fattyMealFactor: Decimal,
-        sweetMealFactor: Decimal,
-        maxCarbs: Decimal
-    ) {
-        return (
-            units: settingsManager.settings.units,
-            fraction: settingsManager.settings.overrideFactor,
-            fattyMealFactor: settingsManager.settings.fattyMealFactor,
-            sweetMealFactor: settingsManager.settings.sweetMealFactor,
-            maxCarbs: settingsManager.settings.maxCarbs
-        )
-    }
-
-    /// Gets the current setting value for a specific setting type based on the time of day
-    /// - Parameter type: The type of setting to retrieve (basal, carbRatio, bgTarget, or isf)
-    /// - Returns: The current decimal value for the specified setting type
-    private func getCurrentSettingValue(for type: SettingType, at date: Date) async -> Decimal {
-        let calendar = Calendar.current
-        let midnight = calendar.startOfDay(for: date)
-        let minutesSinceMidnight = calendar.dateComponents([.minute], from: midnight, to: date).minute ?? 0
-
-        switch type {
-        case .basal:
-            let profile = await getBasalProfile()
-            return profile.last { $0.minutes <= minutesSinceMidnight }?.rate ?? 0
-
-        case .carbRatio:
-            let ratios = await getCarbRatios()
-            return ratios.schedule.last { $0.offset <= minutesSinceMidnight }?.ratio ?? 0
-
-        case .bgTarget:
-            let targets = await getBGTargets()
-            return targets.targets.last { $0.offset <= minutesSinceMidnight }?.low ?? 0
-
-        case .isf:
-            let sensitivities = await getISFValues()
-            return sensitivities.sensitivities.last { $0.offset <= minutesSinceMidnight }?.sensitivity ?? 0
-        }
-    }
-
-    /// Retrieves the pump settings from storage
-    /// - Returns: PumpSettings object containing pump configuration
-    private func getPumpSettings() async -> PumpSettings {
-        await fileStorage.retrieveAsync(OpenAPS.Settings.settings, as: PumpSettings.self)
-            ?? PumpSettings(from: OpenAPS.defaults(for: OpenAPS.Settings.settings))
-            ?? PumpSettings(insulinActionCurve: 10, maxBolus: 10, maxBasal: 2)
-    }
-
-    /// Retrieves the basal profile from storage
-    /// - Returns: Array of BasalProfileEntry objects
-    private func getBasalProfile() async -> [BasalProfileEntry] {
-        await fileStorage.retrieveAsync(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self)
-            ?? [BasalProfileEntry](from: OpenAPS.defaults(for: OpenAPS.Settings.basalProfile))
-            ?? []
-    }
-
-    /// Retrieves carb ratios from storage
-    /// - Returns: CarbRatios object containing carb ratio schedule
-    private func getCarbRatios() async -> CarbRatios {
-        await fileStorage.retrieveAsync(OpenAPS.Settings.carbRatios, as: CarbRatios.self)
-            ?? CarbRatios(from: OpenAPS.defaults(for: OpenAPS.Settings.carbRatios))
-            ?? CarbRatios(units: .grams, schedule: [])
-    }
-
-    /// Retrieves blood glucose targets from storage
-    /// - Returns: BGTargets object containing target schedule
-    private func getBGTargets() async -> BGTargets {
-        await fileStorage.retrieveAsync(OpenAPS.Settings.bgTargets, as: BGTargets.self)
-            ?? BGTargets(from: OpenAPS.defaults(for: OpenAPS.Settings.bgTargets))
-            ?? BGTargets(units: .mgdL, userPreferredUnits: .mgdL, targets: [])
-    }
-
-    /// Retrieves insulin sensitivity factors from storage
-    /// - Returns: InsulinSensitivities object containing sensitivity schedule
-    private func getISFValues() async -> InsulinSensitivities {
-        await fileStorage.retrieveAsync(OpenAPS.Settings.insulinSensitivities, as: InsulinSensitivities.self)
-            ?? InsulinSensitivities(from: OpenAPS.defaults(for: OpenAPS.Settings.insulinSensitivities))
-            ?? InsulinSensitivities(
-                units: .mgdL,
-                userPreferredUnits: .mgdL,
-                sensitivities: []
-            )
-    }
-}

+ 29 - 106
TrioTests/CalibrationsTests.swift

@@ -1,136 +1,59 @@
-import Foundation
 import Swinject
-import Testing
-
 @testable import Trio
+import XCTest
 
-@Suite("Calibration Service Tests") struct CalibrationTests: Injectable {
+class CalibrationsTests: XCTestCase, Injectable {
     let fileStorage = BaseFileStorage()
     @Injected() var calibrationService: CalibrationService!
     let resolver = TrioApp().resolver
 
-    init() {
+    override func setUp() {
         injectServices(resolver)
     }
 
-    @Test("Can create simple calibration") func testCreateSimpleCalibration() {
-        // Given
+    func testCreateSimpleCalibration() {
+        // restore state so each test is independent
         calibrationService.removeAllCalibrations()
-        let calibration = Calibration(x: 100.0, y: 102.0)
 
-        // When
+        let calibration = Calibration(x: 100.0, y: 102.0)
         calibrationService.addCalibration(calibration)
 
-        // Then
-        #expect(calibrationService.calibrations.isNotEmpty)
-        #expect(calibrationService.slope == 1)
-        #expect(calibrationService.intercept == 2)
-        #expect(calibrationService.calibrate(value: 104) == 106)
+        XCTAssertTrue(calibrationService.calibrations.isNotEmpty)
+
+        XCTAssertTrue(calibrationService.slope == 1)
+
+        XCTAssertTrue(calibrationService.intercept == 2)
+
+        XCTAssertTrue(calibrationService.calibrate(value: 104) == 106)
     }
 
-    @Test("Can handle multiple calibrations") func testCreateMultipleCalibration() {
-        // Given
+    func testCreateMultipleCalibration() {
+        // restore state so each test is independent
         calibrationService.removeAllCalibrations()
-        let calibration = Calibration(x: 100.0, y: 120)
-        let calibration2 = Calibration(x: 120.0, y: 130.0)
 
-        // When
+        let calibration = Calibration(x: 100.0, y: 120)
         calibrationService.addCalibration(calibration)
+
+        let calibration2 = Calibration(x: 120.0, y: 130.0)
         calibrationService.addCalibration(calibration2)
 
-        // Then
-        #expect(abs(calibrationService.slope - 0.8) < 0.0001)
-        #expect(abs(calibrationService.intercept - 37) < 0.0001)
-        #expect(abs(calibrationService.calibrate(value: 80) - 101) < 0.0001)
+        XCTAssertEqual(calibrationService.slope, 0.8, accuracy: 0.0001)
+        XCTAssertEqual(calibrationService.intercept, 37, accuracy: 0.0001)
+        XCTAssertEqual(calibrationService.calibrate(value: 80), 101, accuracy: 0.0001)
 
-        // When removing last
         calibrationService.removeLast()
-        #expect(calibrationService.calibrations.count == 1)
 
-        // When removing all
-        calibrationService.removeAllCalibrations()
-        #expect(calibrationService.calibrations.isEmpty)
-    }
+        XCTAssertTrue(calibrationService.calibrations.count == 1)
 
-    @Test("Handles calibration bounds correctly") func testCalibrationBounds() {
-        // Given
         calibrationService.removeAllCalibrations()
+        XCTAssertTrue(calibrationService.calibrations.isEmpty)
+    }
 
-        // When no calibrations exist
-        #expect(calibrationService.slope == 1, "Default slope should be 1")
-        #expect(calibrationService.intercept == 0, "Default intercept should be 0")
-
-        // When adding extreme values
-        let extremeCalibration1 = Calibration(x: 0.0, y: 1000.0) // Should be clamped
-        let extremeCalibration2 = Calibration(x: 1000.0, y: 0.0) // Should be clamped
-
-        calibrationService.addCalibration(extremeCalibration1)
-        calibrationService.addCalibration(extremeCalibration2)
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
 
-        // Then check bounds
-        #expect(calibrationService.slope >= 0.8, "Slope should not be less than minimum")
-        #expect(calibrationService.slope <= 1.25, "Slope should not be more than maximum")
-        #expect(calibrationService.intercept >= -100, "Intercept should not be less than minimum")
-        #expect(calibrationService.intercept <= 100, "Intercept should not be more than maximum")
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
     }
 }
-
-// import Swinject
-// @testable import Trio
-// import XCTest
-//
-// class CalibrationsTests: XCTestCase, Injectable {
-//    let fileStorage = BaseFileStorage()
-//    @Injected() var calibrationService: CalibrationService!
-//    let resolver = TrioApp().resolver
-//
-//    override func setUp() {
-//        injectServices(resolver)
-//    }
-//
-//    func testCreateSimpleCalibration() {
-//        // restore state so each test is independent
-//        calibrationService.removeAllCalibrations()
-//
-//        let calibration = Calibration(x: 100.0, y: 102.0)
-//        calibrationService.addCalibration(calibration)
-//
-//        XCTAssertTrue(calibrationService.calibrations.isNotEmpty)
-//
-//        XCTAssertTrue(calibrationService.slope == 1)
-//
-//        XCTAssertTrue(calibrationService.intercept == 2)
-//
-//        XCTAssertTrue(calibrationService.calibrate(value: 104) == 106)
-//    }
-//
-//    func testCreateMultipleCalibration() {
-//        // restore state so each test is independent
-//        calibrationService.removeAllCalibrations()
-//
-//        let calibration = Calibration(x: 100.0, y: 120)
-//        calibrationService.addCalibration(calibration)
-//
-//        let calibration2 = Calibration(x: 120.0, y: 130.0)
-//        calibrationService.addCalibration(calibration2)
-//
-//        XCTAssertEqual(calibrationService.slope, 0.8, accuracy: 0.0001)
-//        XCTAssertEqual(calibrationService.intercept, 37, accuracy: 0.0001)
-//        XCTAssertEqual(calibrationService.calibrate(value: 80), 101, accuracy: 0.0001)
-//
-//        calibrationService.removeLast()
-//
-//        XCTAssertTrue(calibrationService.calibrations.count == 1)
-//
-//        calibrationService.removeAllCalibrations()
-//        XCTAssertTrue(calibrationService.calibrations.isEmpty)
-//    }
-//
-//    override func setUpWithError() throws {
-//        // Put setup code here. This method is called before the invocation of each test method in the class.
-//    }
-//
-//    override func tearDownWithError() throws {
-//        // Put teardown code here. This method is called after the invocation of each test method in the class.
-//    }
-// }

+ 0 - 304
TrioTests/CoreDataTests/DeterminationStorageTests.swift

@@ -1,304 +0,0 @@
-import CoreData
-import Foundation
-import Swinject
-import Testing
-
-@testable import Trio
-
-@Suite("Determination Storage Tests") struct DeterminationStorageTests: Injectable {
-    @Injected() var storage: DeterminationStorage!
-    let resolver: Resolver
-    let coreDataStack = CoreDataStack.createForTests()
-    let testContext: NSManagedObjectContext
-
-    init() {
-        // 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
-        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, "DeterminationStorage should be injected")
-
-        // Verify it's the correct type
-        #expect(storage is BaseDeterminationStorage, "Storage should be of type BaseDeterminationStorage")
-    }
-
-    @Test("Test fetchLastDeterminationObjectID with different predicates") func testFetchLastDeterminationWithPredicates() async throws {
-        // Given
-        let date = Date()
-        let id = UUID()
-
-        // Create a mock determination
-        await testContext.perform {
-            let determination = OrefDetermination(context: testContext)
-            determination.id = id
-            determination.deliverAt = date
-            determination.timestamp = date
-            determination.enacted = true
-            determination.isUploadedToNS = true
-            try? testContext.save()
-        }
-
-        // Tests with predicates that we use the most for this function
-        // 1. Test within 30 minutes
-        let results = try await storage
-            .fetchLastDeterminationObjectID(predicate: NSPredicate.predicateFor30MinAgoForDetermination)
-        #expect(results.count == 1, "Should find 1 determination within 30 minutes")
-        // Get NSManagedObjectID from exactDateResults
-        try await testContext.perform {
-            do {
-                guard let results = results.first,
-                      let object = try testContext.existingObject(with: results) as? OrefDetermination
-                else {
-                    throw TestError("Failed to fetch determination")
-                }
-                #expect(object.timestamp == date, "Determination within 30 minutes should have the same timestamp as date")
-                #expect(object.deliverAt == date, "Determination within 30 minutes should have the same deliverAt as date")
-                #expect(object.enacted == true, "Determination within 30 minutes should be enacted")
-                #expect(object.isUploadedToNS == true, "Determination within 30 minutes should be uploaded to NS")
-                #expect(object.id == id, "Determination within 30 minutes should have the same id")
-            } catch {
-                throw TestError("Failed to fetch determination")
-            }
-        }
-
-        // 2. Test enacted determinations
-        let enactedPredicate = NSPredicate.enactedDetermination
-        let enactedResults = try await storage.fetchLastDeterminationObjectID(predicate: enactedPredicate)
-        #expect(enactedResults.count == 1, "Should find 1 enacted determination")
-        // Get NSManagedObjectID from enactedResults
-        try await testContext.perform {
-            do {
-                guard let results = enactedResults.first,
-                      let object = try testContext.existingObject(with: results) as? OrefDetermination
-                else {
-                    throw TestError("Failed to fetch determination")
-                }
-                #expect(object.enacted == true, "Enacted determination should be enacted")
-                #expect(object.isUploadedToNS == true, "Enacted determination should be uploaded to NS")
-                #expect(object.id == id, "Enacted determination should have the same id")
-                #expect(object.timestamp == date, "Enacted determination should have the same timestamp")
-                #expect(object.deliverAt == date, "Enacted determination should have the same deliverAt")
-
-                // Delete the determination
-                testContext.delete(object)
-                try testContext.save()
-            } catch {
-                throw TestError("Failed to fetch determination")
-            }
-        }
-    }
-
-    @Test("Test complete forecast hierarchy prefetching") func testForecastHierarchyPrefetching() async throws {
-        // Given
-        let date = Date()
-        let forecastTypes = ["iob", "cob", "zt", "uam"]
-        var determinationId: NSManagedObjectID?
-        let expectedValuesPerForecast = 5
-
-        // STEP 1: Create test data
-        await testContext.perform {
-            let determination = OrefDetermination(context: testContext)
-            determination.id = UUID()
-            determination.deliverAt = date
-            determination.timestamp = date
-            determination.enacted = true
-
-            // Create all forecast types with values
-            for type in forecastTypes {
-                let forecast = Forecast(context: testContext)
-                forecast.id = UUID()
-                forecast.date = date
-                forecast.type = type
-                forecast.orefDetermination = determination
-
-                // Add test values with different patterns per type
-                for i in 0 ..< expectedValuesPerForecast {
-                    let value = ForecastValue(context: testContext)
-                    value.index = Int32(i)
-
-                    // Different value patterns for each type
-                    switch type {
-                    case "iob": value.value = Int32(100 + i * 10) // 100, 110, 120...
-                    case "cob": value.value = Int32(50 + i * 5) // 50, 55, 60...
-                    case "zt": value.value = Int32(80 + i * 8) // 80, 88, 96...
-                    case "uam": value.value = Int32(120 - i * 15) // 120, 105, 90...
-                    default: value.value = 0
-                    }
-
-                    value.forecast = forecast
-                }
-            }
-
-            try? testContext.save()
-            determinationId = determination.objectID
-        }
-
-        guard let determinationId = determinationId else {
-            throw TestError("Failed to create test data")
-        }
-
-        // STEP 2: Test hierarchy fetching
-        let hierarchy = try await storage.fetchForecastHierarchy(
-            for: determinationId,
-            in: testContext
-        )
-
-        // Test hierarchy structure
-        #expect(hierarchy.count == forecastTypes.count, "Should have correct number of forecasts")
-
-        // STEP 3: Test individual forecasts
-        for data in hierarchy {
-            let (id, forecast, values) = await storage.fetchForecastObjects(
-                for: data,
-                in: testContext
-            )
-
-            // Test basic structure
-            #expect(id != UUID(), "Should have valid UUID")
-            #expect(forecast != nil, "Forecast should exist")
-            #expect(values.count == expectedValuesPerForecast, "Should have correct number of values")
-
-            // Test forecast type and values
-            if let forecast = forecast {
-                #expect(forecastTypes.contains(forecast.type ?? ""), "Should have valid forecast type")
-
-                // Test value patterns
-                let sortedValues = values.sorted { $0.index < $1.index }
-                switch forecast.type {
-                case "iob":
-                    #expect(sortedValues.first?.value == 100, "IOB should start at 100")
-                    #expect(sortedValues.last?.value == 140, "IOB should end at 140")
-                case "cob":
-                    #expect(sortedValues.first?.value == 50, "COB should start at 50")
-                    #expect(sortedValues.last?.value == 70, "COB should end at 70")
-                case "zt":
-                    #expect(sortedValues.first?.value == 80, "ZT should start at 80")
-                    #expect(sortedValues.last?.value == 112, "ZT should end at 112")
-                case "uam":
-                    #expect(sortedValues.first?.value == 120, "UAM should start at 120")
-                    #expect(sortedValues.last?.value == 60, "UAM should end at 60")
-                default:
-                    break
-                }
-            }
-        }
-
-        // STEP 4: Test relationship integrity
-        try await testContext.perform {
-            do {
-                let determination = try testContext.existingObject(with: determinationId) as? OrefDetermination
-                let forecasts = Array(determination?.forecasts ?? [])
-
-                #expect(forecasts.count == forecastTypes.count, "Determination should have all forecasts")
-                #expect(
-                    forecasts.allSatisfy { Array($0.forecastValues ?? []).count == expectedValuesPerForecast },
-                    "Each forecast should have correct number of values"
-                )
-            } catch {
-                throw TestError("Failed to verify relationships: \(error)")
-            }
-        }
-    }
-
-    @Test("Measure performance of Core Data fetch operations") func testCoreDataPerformance() async throws {
-        // STEP 1: Setup test data
-        let date = Date()
-        var determinationId: NSManagedObjectID?
-
-        // Create test data
-        await testContext.perform {
-            let determination = OrefDetermination(context: self.testContext)
-            determination.id = UUID()
-            determination.deliverAt = date
-            determination.timestamp = date
-            determination.enacted = true
-
-            // Add forecasts
-            for type in ["iob", "cob", "zt", "uam"] {
-                let forecast = Forecast(context: self.testContext)
-                forecast.id = UUID()
-                forecast.type = type
-                forecast.date = date
-                forecast.orefDetermination = determination
-
-                // Add 48 values (typical forecast length)
-                for i in 0 ..< 48 {
-                    let value = ForecastValue(context: self.testContext)
-                    value.index = Int32(i)
-                    value.value = Int32(100 + i)
-                    forecast.addToForecastValues(value)
-                }
-            }
-
-            try? self.testContext.save()
-            determinationId = determination.objectID
-        }
-
-        guard let determinationId = determinationId else {
-            throw TestError("Failed to create test data")
-        }
-
-        // STEP 2: Test fetchLastDeterminationObjectID
-        let lastDeterminationStartTime = CFAbsoluteTimeGetCurrent()
-
-        let lastDetermination = try await storage.fetchLastDeterminationObjectID(
-            predicate: NSPredicate(format: "deliverAt == %@", date as NSDate)
-        )
-
-        let lastDeterminationTime = CFAbsoluteTimeGetCurrent() - lastDeterminationStartTime
-        debug(.default, "fetchLastDeterminationObjectID time: \(String(format: "%.4f", lastDeterminationTime)) seconds")
-
-        // STEP 3: Test fetchForecastHierarchy
-        let hierarchyStartTime = CFAbsoluteTimeGetCurrent()
-
-        let hierarchy = try await storage.fetchForecastHierarchy(
-            for: determinationId,
-            in: testContext
-        )
-
-        let hierarchyTime = CFAbsoluteTimeGetCurrent() - hierarchyStartTime
-        debug(.default, "fetchForecastHierarchy time: \(String(format: "%.4f", hierarchyTime)) seconds")
-
-        // STEP 4: Test fetchForecastObjects
-        let objectsStartTime = CFAbsoluteTimeGetCurrent()
-        var individualFetchTimes: [Double] = []
-
-        for data in hierarchy {
-            let singleFetchStart = CFAbsoluteTimeGetCurrent()
-            _ = await storage.fetchForecastObjects(
-                for: data,
-                in: testContext
-            )
-            individualFetchTimes.append(CFAbsoluteTimeGetCurrent() - singleFetchStart)
-        }
-
-        let objectsTime = CFAbsoluteTimeGetCurrent() - objectsStartTime
-        let avgObjectTime = individualFetchTimes.reduce(0, +) / Double(individualFetchTimes.count)
-
-        debug(.default, "Total fetchForecastObjects time: \(String(format: "%.4f", objectsTime)) seconds")
-        debug(.default, "Average time per forecast object: \(String(format: "%.4f", avgObjectTime)) seconds")
-
-        // Performance expectations
-        #expect(lastDeterminationTime < 0.1, "fetchLastDeterminationObjectID should take less than 0.1 seconds")
-        #expect(hierarchyTime < 0.1, "fetchForecastHierarchy should take less than 0.1 seconds")
-        #expect(objectsTime < 0.2, "fetchForecastObjects should take less than 0.2 seconds")
-        #expect(avgObjectTime < 0.05, "Individual forecast fetches should take less than 0.05 seconds")
-    }
-}

+ 0 - 367
TrioTests/CoreDataTests/PumpHistoryStorageTests.swift

@@ -1,367 +0,0 @@
-import CoreData
-import Foundation
-import Swinject
-import Testing
-
-@testable import LoopKit
-@testable import Trio
-
-@Suite("PumpHistoryStorage Tests") struct PumpHistoryStorageTests: Injectable {
-    @Injected() var storage: PumpHistoryStorage!
-    let resolver: Resolver
-    let coreDataStack = CoreDataStack.createForTests()
-    let testContext: NSManagedObjectContext
-    typealias PumpEvent = PumpEventStored.EventType
-
-    init() {
-        // Create test context
-        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 PumpHistoryStorage
-        ])
-
-        resolver = assembler.resolver
-        injectServices(resolver)
-    }
-
-    @Test("Storage is correctly initialized") func testStorageInitialization() {
-        // Verify storage exists
-        #expect(storage != nil, "PumpHistoryStorage should be injected")
-
-        // Verify it's the correct type
-        #expect(
-            storage is BasePumpHistoryStorage, "Storage should be of type BasePumpHistoryStorage"
-        )
-
-        // Verify we can access the update publisher
-        #expect(storage.updatePublisher != nil, "Update publisher should be available")
-    }
-
-    @Test("Test read and delete using generic CoreDataStack functions") func testFetchAndDeletePumpEvents() async throws {
-        // Given
-        let date = Date()
-
-        // Insert mock entry
-        let events: [LoopKit.NewPumpEvent] = [
-            LoopKit.NewPumpEvent(
-                date: date,
-                dose: LoopKit.DoseEntry(
-                    type: .bolus,
-                    startDate: date,
-                    value: 0.5,
-                    unit: .units,
-                    deliveredUnits: nil,
-                    description: nil,
-                    syncIdentifier: nil,
-                    scheduledBasalRate: nil,
-                    insulinType: .lyumjev,
-                    automatic: false,
-                    manuallyEntered: false,
-                    isMutable: false
-                ),
-                raw: Data(),
-                title: "Test Bolus for Fetch",
-                type: .bolus
-            )
-        ]
-
-        // Store test event
-        try await storage.storePumpEvents(events)
-
-        // When - Fetch events with our generic fetch function
-        let fetchedEvents = try await coreDataStack.fetchEntitiesAsync(
-            ofType: PumpEventStored.self,
-            onContext: testContext,
-            predicate: NSPredicate(
-                format: "type == %@ AND timestamp == %@",
-                PumpEvent.bolus.rawValue,
-                date as NSDate
-            ),
-            key: "timestamp",
-            ascending: false
-        )
-
-        guard let fetchedEvents = fetchedEvents as? [PumpEventStored] else { return }
-
-        // Then
-        #expect(fetchedEvents.count == 1, "Should have found exactly one event")
-        let fetchedEvent = fetchedEvents.first
-        #expect(fetchedEvent?.type == PumpEvent.bolus.rawValue, "Should be a bolus event")
-        #expect(fetchedEvent?.bolus?.amount as? Decimal == 0.5, "Bolus amount should be 0.5")
-        #expect(
-            abs(fetchedEvent?.timestamp?.timeIntervalSince(date) ?? 1) < 1,
-            "Timestamp should match"
-        )
-
-        // When - Delete event
-        if let fetchedEvent = fetchedEvent {
-            await coreDataStack.deleteObject(identifiedBy: fetchedEvent.objectID)
-        }
-
-        // Then - Verify deletion
-        let eventsAfterDeletion = try await coreDataStack.fetchEntitiesAsync(
-            ofType: PumpEventStored.self,
-            onContext: testContext,
-            predicate: NSPredicate(
-                format: "type == %@ AND timestamp == %@",
-                PumpEvent.bolus.rawValue,
-                date as NSDate
-            ),
-            key: "timestamp",
-            ascending: false
-        )
-
-        guard let eventsAfterDeletion = eventsAfterDeletion as? [PumpEventStored] else { return }
-
-        #expect(eventsAfterDeletion.isEmpty, "Should have no events after deletion")
-    }
-
-    @Test("Test store function in PumpHistoryStorage") func testStorePumpEvents() async throws {
-        // Given
-        let date = Date()
-        let tenMinAgo = date.addingTimeInterval(-10.minutes.timeInterval)
-        let halfHourInFuture = date.addingTimeInterval(30.minutes.timeInterval)
-
-        // Get initial entries to compare to final entries later
-        let initialEntries = try await testContext.perform {
-            try testContext.fetch(PumpEventStored.fetchRequest())
-        }
-
-        // Create 2 test events, 1 bolus + 1 temp basal event
-        let events: [LoopKit.NewPumpEvent] = [
-            // SMB
-            LoopKit.NewPumpEvent(
-                date: tenMinAgo,
-                dose: LoopKit.DoseEntry(
-                    type: .bolus,
-                    startDate: tenMinAgo,
-                    value: 0.4,
-                    unit: .units,
-                    deliveredUnits: nil,
-                    description: nil,
-                    syncIdentifier: nil,
-                    scheduledBasalRate: nil,
-                    insulinType: .lyumjev,
-                    automatic: true,
-                    manuallyEntered: false,
-                    isMutable: false
-                ),
-                raw: Data(),
-                title: "Test Bolus",
-                type: .bolus
-            ),
-            // Temp Basal event
-            LoopKit.NewPumpEvent(
-                date: date,
-                dose: LoopKit.DoseEntry(
-                    type: .tempBasal,
-                    startDate: date,
-                    endDate: halfHourInFuture,
-                    value: 1.2,
-                    unit: .unitsPerHour,
-                    deliveredUnits: nil,
-                    description: nil,
-                    syncIdentifier: nil,
-                    scheduledBasalRate: nil,
-                    insulinType: .lyumjev,
-                    automatic: true,
-                    manuallyEntered: false,
-                    isMutable: true
-                ),
-                raw: Data(),
-                title: "Test Temp Basal",
-                type: .tempBasal
-            )
-        ]
-
-        // When
-        // Store in our in-memory PumphistoryStorage
-        try await storage.storePumpEvents(events)
-
-        // Then
-        // Fetch all events after storing
-        let finalEntries = try await testContext.perform {
-            try testContext.fetch(PumpEventStored.fetchRequest())
-        }
-
-        // Verify there were no initial entries
-        #expect(initialEntries.isEmpty, "There should be no initial entries")
-
-        // Verify count increased by 2
-        #expect(finalEntries.count == initialEntries.count + 2, "Should have added 2 new events")
-
-        // Verify bolus event
-        let bolusEvent = finalEntries.first {
-            $0.type == PumpEvent.bolus.rawValue &&
-                abs($0.timestamp?.timeIntervalSince(tenMinAgo) ?? 1) < 1
-        }
-        #expect(bolusEvent != nil, "Should have found bolus event")
-        #expect(bolusEvent?.bolus?.amount as? Decimal == 0.4, "Bolus amount should be 0.4")
-        #expect(bolusEvent?.isUploadedToNS == false, "Should not be uploaded to NS")
-        #expect(bolusEvent?.isUploadedToHealth == false, "Should not be uploaded to Health")
-        #expect(bolusEvent?.isUploadedToTidepool == false, "Should not be uploaded to Tidepool")
-        #expect(bolusEvent?.bolus?.isSMB == true, "Should be a SMB")
-        #expect(bolusEvent?.bolus?.isExternal == false, "Should not be external insulin")
-
-        // Verify temp basal event
-        let tempBasalEvent = finalEntries.first {
-            $0.type == PumpEvent.tempBasal.rawValue &&
-                abs($0.timestamp?.timeIntervalSince(date) ?? 1) < 1
-        }
-        #expect(tempBasalEvent != nil, "Should have found temp basal event")
-        #expect(tempBasalEvent?.tempBasal?.rate as? Decimal == 1.2, "Temp basal rate should be 1.2")
-        #expect(tempBasalEvent?.tempBasal?.duration == 30, "Temp basal duration should be 30 minutes")
-        #expect(tempBasalEvent?.isUploadedToNS == false, "Should not be uploaded to NS")
-        #expect(tempBasalEvent?.isUploadedToHealth == false, "Should not be uploaded to Health")
-        #expect(bolusEvent?.isUploadedToTidepool == false, "Should not be uploaded to Tidepool")
-    }
-
-    @Test("Test store function for manual boluses") func testStorePumpEventsWithManualBoluses() async throws {
-        // Given
-        let date = Date()
-
-        // Insert mock entry
-        let events: [LoopKit.NewPumpEvent] = [
-            LoopKit.NewPumpEvent(
-                date: date,
-                dose: LoopKit.DoseEntry(
-                    type: .bolus,
-                    startDate: date,
-                    value: 4,
-                    unit: .units,
-                    deliveredUnits: nil,
-                    description: nil,
-                    syncIdentifier: nil,
-                    scheduledBasalRate: nil,
-                    insulinType: .lyumjev,
-                    automatic: false,
-                    manuallyEntered: false,
-                    isMutable: false
-                ),
-                raw: Data(),
-                title: "Test Bolus",
-                type: .bolus
-            )
-        ]
-
-        // Store test event and wait for storage to complete the task
-        try await storage.storePumpEvents(events)
-
-        // When - Fetch events with our generic fetch function
-        let fetchedEvents = try await coreDataStack.fetchEntitiesAsync(
-            ofType: PumpEventStored.self,
-            onContext: testContext,
-            predicate: NSPredicate(
-                format: "type == %@ AND timestamp == %@",
-                PumpEvent.bolus.rawValue,
-                date as NSDate
-            ),
-            key: "timestamp",
-            ascending: false
-        )
-
-        guard let fetchedEvents = fetchedEvents as? [PumpEventStored] else { return }
-
-        // Then
-        #expect(fetchedEvents.count == 1, "Should have found exactly one event")
-        let fetchedEvent = fetchedEvents.first
-        #expect(fetchedEvent?.type == PumpEvent.bolus.rawValue, "Should be a bolus event")
-        #expect(fetchedEvent?.bolus?.amount as? Decimal == 4, "Bolus amount should be 4 U")
-        #expect(
-            abs(fetchedEvent?.timestamp?.timeIntervalSince(date) ?? 1) < 1,
-            "Timestamp should match"
-        )
-        #expect(fetchedEvent?.bolus?.isSMB == false, "Should not be a SMB")
-        // TODO: - check this
-        #expect(fetchedEvent?.bolus?.isExternal == false, "Should not be external Insulin")
-        #expect(fetchedEvent?.isUploadedToNS == false, "Should not be uploaded to NS")
-        #expect(fetchedEvent?.isUploadedToHealth == false, "Should not be uploaded to Health")
-        #expect(fetchedEvent?.isUploadedToTidepool == false, "Should not be uploaded to Tidepool")
-    }
-
-    @Test("Measure performance of PumpHistory storage operations") func testStoragePerformance() async throws {
-        // STEP 1: Setup test data
-        let date = Date()
-        let amount: Decimal = 4.0
-        let events = [
-            NewPumpEvent(
-                date: date,
-                dose: DoseEntry(
-                    type: .bolus,
-                    startDate: date,
-                    endDate: date.addingTimeInterval(1),
-                    value: Double(amount),
-                    unit: .units,
-                    deliveredUnits: Double(amount),
-                    description: nil,
-                    syncIdentifier: "test_bolus_1",
-                    scheduledBasalRate: nil,
-                    insulinType: .lyumjev,
-                    automatic: false,
-                    manuallyEntered: true,
-                    isMutable: false
-                ),
-                raw: Data(),
-                title: "Test Bolus",
-                type: .bolus
-            )
-        ]
-
-        // STEP 2: Test storePumpEvents performance
-        let storeStartTime = CFAbsoluteTimeGetCurrent()
-
-        try await storage.storePumpEvents(events)
-
-        let storeTime = CFAbsoluteTimeGetCurrent() - storeStartTime
-        debug(.default, "storePumpEvents time: \(String(format: "%.4f", storeTime)) seconds")
-
-        // STEP 3: Test Nightscout upload fetch performance
-        let nsStartTime = CFAbsoluteTimeGetCurrent()
-
-        let nsEvents = try await storage.getPumpHistoryNotYetUploadedToNightscout()
-
-        let nsTime = CFAbsoluteTimeGetCurrent() - nsStartTime
-        debug(.default, "Nightscout fetch time: \(String(format: "%.4f", nsTime)) seconds")
-
-        // STEP 4: Test HealthKit upload fetch performance
-        let healthStartTime = CFAbsoluteTimeGetCurrent()
-
-        let healthEvents = try await storage.getPumpHistoryNotYetUploadedToHealth()
-
-        let healthTime = CFAbsoluteTimeGetCurrent() - healthStartTime
-        debug(.default, "HealthKit fetch time: \(String(format: "%.4f", healthTime)) seconds")
-
-        // STEP 5: Test Tidepool upload fetch performance
-        let tidepoolStartTime = CFAbsoluteTimeGetCurrent()
-
-        let tidepoolEvents = try await storage.getPumpHistoryNotYetUploadedToTidepool()
-
-        let tidepoolTime = CFAbsoluteTimeGetCurrent() - tidepoolStartTime
-        debug(.default, "Tidepool fetch time: \(String(format: "%.4f", tidepoolTime)) seconds")
-
-        // Performance expectations
-        #expect(storeTime < 0.1, "Storing events should take less than 0.1 seconds")
-        #expect(nsTime < 0.01, "Fetching Nightscout events should take less than 0.05 seconds")
-        #expect(healthTime < 0.01, "Fetching HealthKit events should take less than 0.05 seconds")
-        #expect(tidepoolTime < 0.01, "Fetching Tidepool events should take less than 0.05 seconds")
-
-        // Log each total time
-        debug(.default, "Total storePumpEvents time: \(String(format: "%.4f", storeTime)) seconds")
-        debug(.default, "Total Nightscout fetch time: \(String(format: "%.4f", nsTime)) seconds")
-        debug(.default, "Total HealthKit fetch time: \(String(format: "%.4f", healthTime)) seconds")
-        debug(.default, "Total Tidepool fetch time: \(String(format: "%.4f", tidepoolTime)) seconds")
-
-        // Verify data integrity
-        #expect(!nsEvents.isEmpty, "Should have found event for Nightscout")
-        #expect(!healthEvents.isEmpty, "Should have found event for HealthKit")
-        #expect(!tidepoolEvents.isEmpty, "Should have found event for Tidepool")
-    }
-}

+ 0 - 24
TrioTests/CoreDataTests/TestAssembly.swift

@@ -1,24 +0,0 @@
-import CoreData
-import Foundation
-import Swinject
-@testable import Trio
-
-class TestAssembly: Assembly {
-    private let testContext: NSManagedObjectContext
-
-    init(testContext: NSManagedObjectContext) {
-        self.testContext = testContext
-    }
-
-    func assemble(container: Container) {
-        // Override PumpHistoryStorage registration for tests
-        container.register(PumpHistoryStorage.self) { r in
-            BasePumpHistoryStorage(resolver: r, context: self.testContext)
-        }.inObjectScope(.container)
-
-        // Override DeterminationStorage registration for tests
-        container.register(DeterminationStorage.self) { r in
-            BaseDeterminationStorage(resolver: r, context: self.testContext)
-        }.inObjectScope(.container)
-    }
-}

+ 12 - 128
TrioTests/FileStorageTests.swift

@@ -1,142 +1,26 @@
-import Foundation
-import Testing
-
 @testable import Trio
+import XCTest
 
-@Suite("File Storage Tests") struct FileStorageTests {
-    let storage = BaseFileStorage()
+class FileStorageTests: XCTestCase {
+    let fileStorage = BaseFileStorage()
 
     struct DummyObject: JSON, Equatable {
         let id: String
         let value: Decimal
     }
 
-    @Test("Can save and retrieve object") func testSaveAndRetrieve() {
-        // Given
-        let dummy = DummyObject(id: "123", value: 78.2)
-
-        // When
-        storage.save(dummy, as: "dummy")
-        let retrieved = storage.retrieve("dummy", as: DummyObject.self)
-
-        // Then
-        #expect(retrieved == dummy)
-    }
-
-    @Test("Can save and retrieve async") func testAsyncSaveAndRetrieve() async {
-        // Given
-        let dummy = DummyObject(id: "123", value: 78.2)
-
-        // When
-        await storage.saveAsync(dummy, as: "dummy_async")
-        let retrieved = await storage.retrieveAsync("dummy_async", as: DummyObject.self)
-
-        // Then
-        #expect(retrieved == dummy)
-    }
-
-    @Test("Can append single value") func testAppendSingleValue() {
-        // Given
-        let dummy1 = DummyObject(id: "1", value: 10.0)
-        let dummy2 = DummyObject(id: "2", value: 20.0)
-
-        // When
-        storage.save([dummy1], as: "dummies")
-        storage.append(dummy2, to: "dummies")
-
-        // Then
-        let retrieved = storage.retrieve("dummies", as: [DummyObject].self)
-        #expect(retrieved?.count == 2)
-        #expect(retrieved?.contains(dummy1) == true)
-        #expect(retrieved?.contains(dummy2) == true)
-    }
-
-    @Test("Can append multiple values") func testAppendMultipleValues() {
-        // Given
-        let dummy1 = DummyObject(id: "1", value: 10.0)
-        let newDummies = [
-            DummyObject(id: "2", value: 20.0),
-            DummyObject(id: "3", value: 30.0)
-        ]
-
-        // When
-        storage.save([dummy1], as: "dummies_multiple")
-        storage.append(newDummies, to: "dummies_multiple")
-
-        // Then
-        let retrieved = storage.retrieve("dummies_multiple", as: [DummyObject].self)
-        #expect(retrieved?.count == 3)
-    }
-
-    @Test("Can append unique values by key path") func testAppendUniqueByKeyPath() {
-        // Given
-        let dummy1 = DummyObject(id: "1", value: 10.0)
-        let dummy2 = DummyObject(id: "1", value: 20.0) // Same id
-
-        // When
-        storage.save([dummy1], as: "unique_dummies")
-        storage.append(dummy2, to: "unique_dummies", uniqBy: \.id)
-
-        // Then
-        let retrieved = storage.retrieve("unique_dummies", as: [DummyObject].self)
-        #expect(retrieved?.count == 1, "Should not append duplicate id")
-    }
-
-    @Test("Can remove file") func testRemoveFile() {
-        // Given
-        let dummy = DummyObject(id: "123", value: 78.2)
-        storage.save(dummy, as: "to_delete")
-
-        // When
-        storage.remove("to_delete")
-
-        // Then
-        let retrieved = storage.retrieve("to_delete", as: DummyObject.self)
-        #expect(retrieved == nil)
-    }
-
-    @Test("Can rename file") func testRenameFile() {
-        // Given
-        let dummy = DummyObject(id: "123", value: 78.2)
-        storage.save(dummy, as: "old_name")
-
-        // When
-        storage.rename("old_name", to: "new_name")
-
-        // Then
-        let oldRetrieved = storage.retrieve("old_name", as: DummyObject.self)
-        let newRetrieved = storage.retrieve("new_name", as: DummyObject.self)
-
-        #expect(oldRetrieved == nil)
-        #expect(newRetrieved == dummy)
+    func testFileStorageTrio() {
+        let dummyObject = DummyObject(id: "21342Z", value: 78.2)
+        fileStorage.save(dummyObject, as: "dummyObject")
+        let dummyObjectRetrieve = fileStorage.retrieve("dummyObject", as: DummyObject.self)
+        XCTAssertTrue(dummyObject == dummyObjectRetrieve)
     }
 
-    @Test("Can execute transaction") func testTransaction() {
-        // Given
-        let dummy = DummyObject(id: "123", value: 78.2)
-
-        // When
-        storage.transaction { storage in
-            storage.save(dummy, as: "transaction_test")
-        }
-
-        // Then
-        let retrieved = storage.retrieve("transaction_test", as: DummyObject.self)
-        #expect(retrieved == dummy)
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
     }
 
-    @Test("Can parse mmol/L settings to mg/dL") func testParseSettingsToMgdL() {
-        // Given
-        var preferences = Preferences()
-        preferences.threshold_setting = 5.5 // mmol/L
-        storage.save(preferences, as: OpenAPS.Settings.preferences)
-
-        // When
-        let wasParsed = storage.parseOnFileSettingsToMgdL()
-
-        // Then
-        #expect(wasParsed == true)
-        let parsed = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
-        #expect(parsed?.threshold_setting == 100) // default mg/dL value
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
     }
 }

+ 36 - 56
TrioTests/PluginManagerTests.swift

@@ -1,91 +1,71 @@
-import Foundation
 import Swinject
-import Testing
 @testable import Trio
+import XCTest
 
-@Suite("Plugin Manager Tests") struct PluginManagerTests: Injectable {
+class PluginManagerTests: XCTestCase, Injectable {
     let fileStorage = BaseFileStorage()
     @Injected() var pluginManager: PluginManager!
     let resolver = TrioApp().resolver
 
-    init() {
+    override func setUp() {
         injectServices(resolver)
     }
 
-    @Test("Can load CGM managers") func testCGMManagerLoad() {
-        // Given
+    func testCGMManagerLoad() {
         let cgmLoopManagers = pluginManager.availableCGMManagers
-
-        // Then
-        #expect(!cgmLoopManagers.isEmpty, "Should have available CGM managers")
-
-        // When loading valid CGM manager
+        XCTAssertNotNil(cgmLoopManagers)
+        XCTAssertTrue(!cgmLoopManagers.isEmpty)
         if let cgmLoop = cgmLoopManagers.first {
             let cgmLoopManager = pluginManager.getCGMManagerTypeByIdentifier(cgmLoop.identifier)
-            #expect(cgmLoopManager != nil, "Should load valid CGM manager")
+            XCTAssertNotNil(cgmLoopManager)
+        } else {
+            XCTFail("Not found CGM loop manager")
         }
-
-        // When trying to load CGM manager with pump identifier
+        /// try to load a Pump manager with a CGM identifier
         if let cgmLoop = cgmLoopManagers.last {
-            let invalidManager = pluginManager.getPumpManagerTypeByIdentifier(cgmLoop.identifier)
-            #expect(invalidManager == nil, "Should not load CGM manager with pump identifier")
+            let cgmLoopManager = pluginManager.getPumpManagerTypeByIdentifier(cgmLoop.identifier)
+            XCTAssertNil(cgmLoopManager)
+        } else {
+            XCTFail("Not found CGM loop manager")
         }
     }
 
-    @Test("Can load pump managers") func testPumpManagerLoad() {
-        // Given
+    func testPumpManagerLoad() {
         let pumpLoopManagers = pluginManager.availablePumpManagers
-
-        // Then
-        #expect(!pumpLoopManagers.isEmpty, "Should have available pump managers")
-
-        // When loading valid pump manager
+        XCTAssertNotNil(pumpLoopManagers)
+        XCTAssertTrue(!pumpLoopManagers.isEmpty)
         if let pumpLoop = pumpLoopManagers.first {
             let pumpLoopManager = pluginManager.getPumpManagerTypeByIdentifier(pumpLoop.identifier)
-            #expect(pumpLoopManager != nil, "Should load valid pump manager")
+            XCTAssertNotNil(pumpLoopManager)
+        } else {
+            XCTFail("Not found pump loop manager")
         }
-
-        // When trying to load pump manager with CGM identifier
+        /// try to load a CGM manager with a pump identifier
         if let pumpLoop = pumpLoopManagers.last {
-            let invalidManager = pluginManager.getCGMManagerTypeByIdentifier(pumpLoop.identifier)
-            #expect(invalidManager == nil, "Should not load pump manager with CGM identifier")
+            let pumpLoopManager = pluginManager.getCGMManagerTypeByIdentifier(pumpLoop.identifier)
+            XCTAssertNil(pumpLoopManager)
+        } else {
+            XCTFail("Not found pump loop manager")
         }
     }
 
-    @Test("Can load service managers") func testServiceManagerLoad() {
-        // Given
+    func testServiceManagerLoad() {
         let serviceManagers = pluginManager.availableServices
-
-        // Then
-        #expect(!serviceManagers.isEmpty, "Should have available services")
-
-        // When
+        XCTAssertNotNil(serviceManagers)
+        XCTAssertTrue(!serviceManagers.isEmpty)
         if let serviceLoop = serviceManagers.first {
             let serviceManager = pluginManager.getServiceTypeByIdentifier(serviceLoop.identifier)
-            #expect(serviceManager != nil, "Should load valid service manager")
+            XCTAssertNotNil(serviceManager)
+        } else {
+            XCTFail("Not found Service loop manager")
         }
     }
 
-    @Test("Available managers have valid descriptors") func testManagerDescriptors() {
-        // Given/When
-        let pumpManagers = pluginManager.availablePumpManagers
-        let cgmManagers = pluginManager.availableCGMManagers
-        let serviceManagers = pluginManager.availableServices
-
-        // Then
-        for manager in pumpManagers {
-            #expect(!manager.identifier.isEmpty, "Pump manager should have non-empty identifier")
-            #expect(!manager.localizedTitle.isEmpty, "Pump manager should have non-empty title")
-        }
-
-        for manager in cgmManagers {
-            #expect(!manager.identifier.isEmpty, "CGM manager should have non-empty identifier")
-            #expect(!manager.localizedTitle.isEmpty, "CGM manager should have non-empty title")
-        }
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
 
-        for manager in serviceManagers {
-            #expect(!manager.identifier.isEmpty, "Service should have non-empty identifier")
-            #expect(!manager.localizedTitle.isEmpty, "Service should have non-empty title")
-        }
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
     }
 }

+ 0 - 10
TrioTests/TestError 2.swift

@@ -1,10 +0,0 @@
-import Foundation
-
-// Custom error type for test failures
-struct TestError: Error {
-    let message: String
-
-    init(_ message: String) {
-        self.message = message
-    }
-}

+ 0 - 1
TrioTests/TestError.swift

@@ -1 +0,0 @@
-import Foundation