@@ -23,15 +23,6 @@ final class LoopAlgorithmTests: XCTestCase {
2323 return ( input: input, recommendation: recommendation)
2424 }
2525
26- func testSuspend( ) throws {
27-
28- let ( input, recommendation) = loadScenario ( " suspend " )
29-
30- let output = LoopAlgorithm . run ( input: input)
31-
32- XCTAssertEqual ( output. recommendation, recommendation)
33- }
34-
3526 func loadPredictedGlucoseFixture( _ name: String ) -> [ PredictedGlucoseValue ] {
3627 let decoder = JSONDecoder ( )
3728 decoder. dateDecodingStrategy = . iso8601
@@ -40,7 +31,16 @@ final class LoopAlgorithmTests: XCTestCase {
4031 return try ! decoder. decode ( [ PredictedGlucoseValue ] . self, from: try ! Data ( contentsOf: url) )
4132 }
4233
43- func testCarbsWithSensitivityChange( ) throws {
34+ func testSuspendScenario( ) throws {
35+
36+ let ( input, recommendation) = loadScenario ( " suspend " )
37+
38+ let output = LoopAlgorithm . run ( input: input)
39+
40+ XCTAssertEqual ( output. recommendation, recommendation)
41+ }
42+
43+ func testCarbsWithSensitivityChangeScenario( ) throws {
4444
4545 // This test computes a dose with a future carb entry
4646 // Between the time of dose and the startTime of the carb
@@ -101,6 +101,7 @@ final class LoopAlgorithmTests: XCTestCase {
101101 XCTAssertEqual ( outputA. effects. retrospectiveCorrection. last? . quantity. doubleValue ( for: . milligramsPerDeciliter) , 165 )
102102 XCTAssertEqual ( outputB. effects. retrospectiveCorrection. last? . quantity. doubleValue ( for: . milligramsPerDeciliter) , 165 )
103103
104+ // These tests fail, because the momentum effect is *not* time independent yet.
104105 // Even though all the input data is the same (just shifted in time), momentum effect varies in relation to how offset
105106 // the glucose samples are from the simulation timeline (at exact 5 minute intervals from the top of the hour)
106107// XCTAssertEqual(outputA.effects.momentum.last?.quantity.doubleValue(for: .milligramsPerDeciliter), 0.0)
@@ -145,7 +146,7 @@ final class LoopAlgorithmTests: XCTestCase {
145146 XCTAssertEqual ( basalAdjustment!. unitsPerHour, 5.83 , accuracy: 0.01 )
146147 }
147148
148- func testLiveCapture ( ) {
149+ func testLiveCaptureScenario ( ) {
149150 let decoder = JSONDecoder ( )
150151 decoder. dateDecodingStrategy = . iso8601
151152
@@ -406,4 +407,53 @@ final class LoopAlgorithmTests: XCTestCase {
406407 }
407408 }
408409
410+ func testEnactingRecommendation( ) throws {
411+ let date = ISO8601DateFormatter ( ) . date ( from: " 2024-01-03T12:00:00+0000 " ) !
412+ var input = AlgorithmInputFixture . mock ( for: date)
413+
414+ let now = input. predictionStart
415+
416+ // Insulin was suspended for a while
417+ input. doses = [
418+ FixtureInsulinDose (
419+ deliveryType: . basal,
420+ startDate: now. addingTimeInterval ( - . hours ( 2 ) ) ,
421+ endDate: now. addingTimeInterval ( - . minutes ( 5 ) ) ,
422+ volume: 0 )
423+ ]
424+
425+ // Rising BG
426+ input. glucoseHistory = [
427+ FixtureGlucoseSample ( startDate: now. addingTimeInterval ( . minutes( - 15 ) ) , quantity: . glucose( 105 ) ) ,
428+ FixtureGlucoseSample ( startDate: now. addingTimeInterval ( . minutes( - 10 ) ) , quantity: . glucose( 110 ) ) ,
429+ FixtureGlucoseSample ( startDate: now. addingTimeInterval ( . minutes( - 5 ) ) , quantity: . glucose( 115 ) ) ,
430+ FixtureGlucoseSample ( startDate: now. addingTimeInterval ( . minutes( - 0 ) ) , quantity: . glucose( 120 ) ) ,
431+ ]
432+
433+ var output = LoopAlgorithm . run ( input: input)
434+
435+ let basalAdjustment = output. recommendation!. automatic!. basalAdjustment
436+ XCTAssertEqual ( basalAdjustment!. unitsPerHour, 4.94 , accuracy: 0.01 )
437+
438+ input. doses. append (
439+ FixtureInsulinDose (
440+ deliveryType: . basal,
441+ startDate: now,
442+ endDate: now. addingTimeInterval ( . minutes( 30 ) ) ,
443+ volume: basalAdjustment!. unitsPerHour / 2 // 4.94 U/hr = 2.47 U delivery over 30m
444+ )
445+ )
446+
447+ // 30 seconds later
448+ input. predictionStart = input. predictionStart. addingTimeInterval ( 30 )
449+
450+ // Add more time to insulin sensitivity timeline to account for running temp basal
451+ input. sensitivity [ 0 ] . endDate = input. sensitivity [ 0 ] . endDate. addingTimeInterval ( . hours( 1 ) )
452+
453+ // Manual recommendation does not cut off doses in progress, like a running temp basal
454+ input. recommendationType = . manualBolus
455+
456+ output = LoopAlgorithm . run ( input: input)
457+ XCTAssertEqual ( output. predictedGlucose. last!. quantity. doubleValue ( for: . milligramsPerDeciliter) , 105 , accuracy: 0.5 )
458+ }
409459}
0 commit comments